// Function wrapper to prevent naming collisions between graph and shared toolkit code.
(function() {

// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.

/**
 * Base object for HTML toolkit derivative objects.
 * @class The base object for HTML toolkit derivative objects.
 * @constructor  
 */
var DvtObj = this.DvtObj;
if (!DvtObj) {
  DvtObj = function() {}; 
  
  // Assign the window to the owner field of DvtObj if a window is present.  This allows the code
  // in createSubclass to expose the class names on the window, which is needed when loading the
  // script inside of a function body.
  if(typeof(window) != "undefined")
    DvtObj[DvtObj._OWNER_KEY] = window;
}

DvtObj._GET_FUNCTION_NAME_REGEXP = /function\s+([\S^]+)\s*\(/;

/**
 * @const
 * @private
 */
DvtObj._OWNER_KEY = "owner";

DvtObj.prototype = {};
DvtObj.prototype.constructor = DvtObj;

/**
 *  Provides inheritance by subclassing a class from a given base class.
 *  @param  {class} extendingClass  The class to be extended from the base class.
 *  @param  {class} baseClass  The base class
 *  @param  {string} typeName The type of the extending class
 */
DvtObj.createSubclass = function (extendingClass, baseClass, typeName) {
  if (extendingClass === undefined || extendingClass === null) {
    return;
  }
  
  if (baseClass === undefined) {
    // assume Object
    baseClass = Object;
  }

  if (extendingClass == baseClass) {
    return;
  }

  // use a temporary constructor to get our superclass as our prototype
  // without out having to initialize the superclass
  var tempConstructor = DvtObj._tempSubclassConstructor;

  tempConstructor.prototype = baseClass.prototype;
  extendingClass.prototype = new tempConstructor();

  extendingClass.prototype.constructor = extendingClass;
  extendingClass.superclass = baseClass.prototype;
  
  if(typeName)
    extendingClass._typeName = typeName;
} ;

/**  @private  */
DvtObj._tempSubclassConstructor = function () {};

DvtObj.getTypeName = function (clazz) {
  var typeName = clazz._typeName;
  if (typeName == null) {
    var constructorText = clazz.toString();
    var matches = DvtObj._GET_FUNCTION_NAME_REGEXP.exec(constructorText);

    typeName = matches[1];

    clazz._typeName = typeName;
  }

  return typeName;
}

DvtObj.prototype.getTypeName = function () {
  return DvtObj.getTypeName(this.constructor);
}


/*-------------------------------------------------------------------*/
/*   clone()                                                         */
/*-------------------------------------------------------------------*/
/**
  *  Returns a copy of this object.  Abstract method, subclasses
  *   must implement.
  *  @type DvtObj
  */
DvtObj.prototype.clone = function()
{
   return null ;
};

/*-------------------------------------------------------------------*/
/*   mergeProps()                                                    */
/*-------------------------------------------------------------------*/
/**
  *   Merge properties in the current object into the supplied object.
  *   Abstract method, subclasses must implement. Used internally by clone().
  *   @param {DvtObj}
  *   @private
  */
DvtObj.prototype.mergeProps = function(obj)
{
} ;

DvtObj._flashToolkit = false ;     // Flash needs a slightly different version
                                   // because the "string version" is not valid for Flash
                                   // this is set only by ActionScript.
/**
 * Creates a callback function
 * 
 * @param {object} thisPtr the object that should be "this" when the function is called
 * @param {function} func the function to create the callback for
 * 
 * @return {function} the callback function
 */
DvtObj.createCallback = function(thisPtr, func) {
  if (DvtObj._flashToolkit) {
    var f = function() {
      return func.apply(thisPtr,arguments) ;
    };
    return f;
  }
  else {
    // create a function that sets up "this" and delegates all of the parameters
    // to the passed in function
    var proxyFunction ;
    try {
          proxyFunction = new Function(
            "var f=arguments.callee; return f._func.apply(f._owner, arguments);");

          // attach ourselves as "this" to the created function
          proxyFunction['_owner'] = thisPtr;

          // attach function to delegate to
          proxyFunction['_func'] = func;
    } catch(e) {} ;

    return proxyFunction;
  }
};

//FIX BUG 17075260: 
/**
 * Define a constant value such that the advanced closure compiler will not simply do an inline replace of the
 * value everywhere it's referenced.  The compiled code should then contain abbreviated references to the constant
 * instead of the literal value, which should help decrease overall code size.  
 * @param {object} constValue value to define for the constant
 * @return {object}
 */
DvtObj.defineConstant = function(constValue) {
  return constValue;
};
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*   DvtArrayUtils         Miscellaneous array utility functions           */
/*-------------------------------------------------------------------------*/
/**
  *  @class  DvtArrayUtils  A static class providing miscellaneous array utility
  *  functions.
  */
var  DvtArrayUtils = new Object();

DvtObj.createSubclass(DvtArrayUtils, DvtObj, "DvtArrayUtils");

//  Factor scaling operation types for toFloat()

/**
  * Factor operation for DvtArrayUtils.toFloat() - no scaling of converted float.
  * @final
  */
DvtArrayUtils.NONE = 0 ;
/**
  * Factor operation for DvtArrayUtils.toFloat() - add factor to converted float.
  * @final
  */
DvtArrayUtils.ADD  = 1 ;
/**
  * Factor operation for DvtArrayUtils.toFloat() - subtract factor from converted float.
  * @final
  */
DvtArrayUtils.SUB  = 2 ;
/**
  * Factor operation for DvtArrayUtils.toFloat() - divide converted float by factor.
  * @final
  */
DvtArrayUtils.DIV  = 3 ;
/**
  * Factor operation for DvtArrayUtils.toFloat() - multiply converted float by factor.
  * @final
  */
DvtArrayUtils.MULT = 4 ;
/**
  *  Factor operation for DvtArrayUtils.toFloat() - set converted float to mod(factor).
  * @final
  */
DvtArrayUtils.MOD  = 5 ;



DvtArrayUtils.argsToArray = function(args)
{
  if (args)
  {
    var array = new Array(args.length);
    if (args.length > 0)
    {
      for (var i = 0; i < args.length; i++)
      {
        array[i] = args[i];
      }
    }
    return array;
  }
  return args;
};


/*--------------------------------------------------------------------*/
/*  isArray()          Cross-browser array detection                  */
/*--------------------------------------------------------------------*/
/**
  *   Test if the specified object is an instance of Array (cross-browser safe).
  * @base DvtArrayUtils
  *   @param {Object}  obj
  *   @type Boolean
  *   @returns true if the object is an Array.
  */
DvtArrayUtils.isArray = function(obj)
{
   // check for undefined explicitly because the second clauses will blow up in Rhino
   return typeof obj != "undefined" && Object.prototype.toString.apply(obj)  === '[object Array]' ;
} ;



/*--------------------------------------------------------------------*/
/*  toFloat()                                                         */
/*--------------------------------------------------------------------*/
/**
  *  Converts an array from string values to floats.  If optional 'factor'
  *  argument is supplied, the array values are scaled by this factor using
  *  the factorType  operator. e.g. <p><code>
  *  &nbsp; &nbsp; &nbsp;  DvtArrayUtils.toFloat(ar, 0.01, DvtArrayUtils.MULT) ;<p></code>
  *  converts an array of percentage value strings to an array of floats converted
  *  to dcimal percentages (between zero and 1).
  *  @param {Array} ar
  *  @param {number} factor
  *  @param {number} factorType
  *  @type {Array}
  *  @returns the Array that was supplied as the first argument.
  */
DvtArrayUtils.toFloat = function(ar, factor, factorType)
{
   if (! DvtArrayUtils.isArray(ar)) {
     return ar ;
   }

   if (! factor) {
     factorType = DvtArrayUtils.NONE ;
   }

   factorType = (factorType === null || isNaN(factorType)) ? DvtArrayUtils.NONE : factorType;

   if (factorType !== DvtArrayUtils.NONE) {
     if ((typeof factor) !== 'number') {
       factor = parseFloat(factor) ;
     } 
     if (isNaN(factor)) {
       factorType = DvtArrayUtils.NONE ;
     }
   }

   var len = ar.length ;
   var i ;

   if (factorType === DvtArrayUtils.NONE) {
     for (i = 0; i < len; i++) {
        ar[i] = parseFloat(ar[i]) ;
     }
   }
   else {
     for (i = 0; i < len; i++)  {
        var  v = parseFloat(ar[i]) ;

         switch (factorType) {
            case  DvtArrayUtils.MULT : v *= factor ;
                                       break ;
            case  DvtArrayUtils.ADD :  v += factor ;
                                       break ;
            case  DvtArrayUtils.SUB :  v -= factor ;
                                       break ;
            case  DvtArrayUtils.DIV :  v /= factor ;
                                       break ;
            case  DvtArrayUtils.MOD :  v %= factor ;
                                       break ;
         }
         ar[i] = v ;
     }

   }

   return ar ;
} ;



/*--------------------------------------------------------------------*/
/*   copy()       Performs a shallow copy of the array. (Much faster  */
/*                than Array.concat().                                */
/*--------------------------------------------------------------------*/
/**
  *  Create a shallow copy of the supplied array.
  *  @param {Array} ar The array to be copied.
  *  @type Array
  *  @returns A shallow copy of the supplied array.
  */
DvtArrayUtils.copy = function(ar)
{
   var arRet ;

   if (ar) {
     arRet = [] ; 
     var len = ar.length ;

     for (var i = 0; i < len; i++)  {
       arRet.push(ar[i]) ;
     }
   }
   return arRet ;
} ;


/**
  *  Returns the index of the first occurrence of an item in an array.  
  *  @param {Array} array  The array to be searched.
  *  @param {Object} item  The item to be found.
  *  @type number
  */
DvtArrayUtils.getIndex = function(array, item)
{
  if (array) {
    for (var i = 0; i < array.length; i++) {
      if (array[i] === item) {
        return i;
      }
    }
  }
  return -1;
};

/**
  *  Returns the index of the last occurrence of an item in an array.  
  *  @param {Array} array  The array to be searched.
  *  @param {Object} item  The item to be found.
  *  @type number
  */
DvtArrayUtils.getLastIndex = function(array, item)
{
  for (var i = array.length - 1; i >= 0; i--)  {
    if (array[i] === item) {
      return i;
    }
  }
  return -1;
};


/*--------------------------------------------------------------------*/
/*   getMinIndex(), getMaxIndex()                                     */
/*--------------------------------------------------------------------*/
/**
  *  Returns the index to the array entry containg the minimum value.
  *  Array entries with undefined or null values are ignored.
  *  @param {Array} ar the array to be searched.
  *  @type number
  *  @returns The index of the array entry containing the minimum value.
  *           If the array length is zero, or all entries are undefined
  *           or null, a value of -1 is returned.
  */
DvtArrayUtils.getMinIndex = function(ar)
{
  var nRet   = -1 ;
  var len    = ar.length; 
  var minVal = Number.MAX_VALUE ;
  var val ;
  
  for (var i = 0; i < len; i++) {
     val = ar[i] ;

     if (val && (typeof val === 'number') && (val < minVal)) {
       minVal = val ;
       nRet   = i ;
     }
  }

  return nRet ;
};


/**
  *  Returns the index to the array entry containg the maximum value.
  *  Array entries with undefined or null values are ignored.
  *  @param {Array} ar the array to be searched.
  *  @type number
  *  @returns The index of the array entry containing the maximum value.
  *           If the array length is zero, or all entries are undefined
  *           or null, a value of -1 is returned.
  */
DvtArrayUtils.getMaxIndex = function(ar)
{
  var nRet   = -1 ;
  var len    = ar.length; 
  var maxVal = Number.MIN_VALUE ;
  var val ;
  
  for (var i = 0; i < len; i++) {
     val = ar[i] ;

     if (val && (typeof val === 'number') && (val > maxVal)) {
       maxVal = val ;
       nRet   = i ;
     }
  }

  return nRet ;
};

/**
 * Returns true if the two arrays have the same contents.
 * @param {array} a
 * @param {array} b
 */
DvtArrayUtils.equals = function(a, b) {
  if(!a && b)
    return false;
  else if(a && !b)
    return false;
  else if(!a && !b)
    return true;
  else // a && b
  {
    if(a.length != b.length)
      return false;
    
    // Compare the individual items
    for(var i=0; i<=a.length; i++) {
      if(a[i] !== b[i])
        return false;
    }
    
    // Everything matched, return true
    return true;
  }
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*   DvtStringUtils         Miscellaneous string utility functions         */
/*-------------------------------------------------------------------------*/
/**
  *  @class  DvtStringUtils  A static class providing miscellaneous string utility
  *  functions.
  */
var  DvtStringUtils = {} ;

DvtObj.createSubclass(DvtStringUtils, DvtObj, "DvtStringUtils");

/*--------------------------------------------------------------------*/
/*  trim(), ltrim(), rtrim()       Whitespace removal                 */
/*--------------------------------------------------------------------*/
/**
  *   Removes leading and trailing whitespace from a string.
  *   @base DvtStringUtils
  *   @param {String} s  The string to be trimmed.
  *   @type String
  *   @returns A string with leading and trailing whitespace removed.
  */
DvtStringUtils.trim = function(s)
{
	return s.replace(/^\s+|\s+$/g,"");
} ;

/**
  *   Removes leading whitespace from a string.
  *   @param {String} s  The string to be trimmed.
  *   @type String
  *   @returns A string with leading whitespace removed.
  */
DvtStringUtils.ltrim = function(s)
{
	return s.replace(/^\s+/,"");
} ;

/**
  *   Removes trailing whitespace from a string.
  *   @param {String} s  The string to be trimmed.
  *   @type String
  *   @returns A string with trailing whitespace removed.
  */
DvtStringUtils.rtrim = function(s)
{
	return s.replace(/\s+$/,"");
} ;


DvtStringUtils.endsWith = function(s, str)
{
  return (s.match(str+"$")==str)
};
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*   DvtContext                                                        */
/*---------------------------------------------------------------------*/
/**
 *   Generic application context.  Used as a single central object to
 *   maintain application specific context, as well as well as providing
 *   access to platform specific data and objects, such as the factory.
 *   @extends DvtObj
 *   @class DvtContext
 *   @constructor
 *   @param {DvtImplFactory} implFactory     The platform implementation factory
 *   @param {DvtStage} stage
 *   @param {DvtTooltipManager} tooltipManager
 *   @export
 */
var DvtContext = function (implFactory, root, id) {
  this.Init(implFactory, root, id);
}

DvtObj.createSubclass(DvtContext, DvtObj, "DvtContext");

/**
  * Initializes this context object with the platform dependent objects.
  * @param  {DvtImplFactory} implFactory
  * @param  {DvtStage} stage
  * @param  {DvtTooltipManager} tooltipManager
  * @protected
  */
DvtContext.prototype.Init = function (implFactory, root, id) {
  this._implFactory    = implFactory;
  this._stage          = new DvtStage(this, root, id + "_stage" );     // TODO use naming utils

  this._tooltipManagers = new Object();
  this._customTooltipManagers = new Object();
  
  this._scheduler      = null;
  
  //BUG FIX 14187937: DvtSvgEventFactory.newEvent(), for example, can use these to store the latest events in
  //order to return the same logical event instance for the same native event instance instead of creating a 
  //new logical event instance every time
  this._nativeEvent = null;
  this._logicalEvent = null;
  
  //BUG FIX 12613719: map of DvtGraphClipPath objects stored by id
  this._clipPathMap = {};
}

/**
 *  Returns a platform dependent implementation factory of the one and only
 *  implementation factory object.
 *  @type DvtImplFactory 
 */
DvtContext.prototype.getImplFactory = function () {
  return this._implFactory;
}


/**
 *  Returns a platform dependent implementation of the one and only
 *  stage.
 *  @type DvtStage 
 *  @export
 */
DvtContext.prototype.getStage = function () {
  return this._stage;
}

/**
 * Specifies the reading direction for the context.  This is used by DvtAgent.isRightToLeft(context) overriding the 
 * reading direction from the DOM when specified.
 * @param {string} dir The reading direction string, such as "rtl" or "ltr".
 * @export
 */
DvtContext.prototype.setReadingDirection = function(dir) {
  this._readingDirection = dir;
}

/**
 * Returns the reading direction for the context, if specified.  Component developers should use DvtAgent.isRightToLeft
 * instead, as that function will determine the reading direction from the DOM when not specified on the context.
 * @return {string} The reading direction string if specified on the context, null otherwise.
 */
DvtContext.prototype.getReadingDirection = function() {
  return this._readingDirection;
}

/**
  * Adds a reference count for the given clip path id
  * @param {string}  id  id of the clipping path
  */
DvtContext.prototype.addClipPath = function (id) {
  if (this._clipPathMap[id])
    this._clipPathMap[id] = this._clipPathMap[id]++;
  else
    this._clipPathMap[id] = 1;
};

/**
  * Removes a reference count for the given clip path id
  * @param {string}  id  id of the clipping path
  */
DvtContext.prototype.removeClipPath = function (id) {
  var refCount = this._clipPathMap[id];
  if (refCount) {
    if (refCount == 1)
     delete this._clipPathMap[id];
    else
     this._clipPathMap[id] = refCount--;
  }
}

/**
  * Determines if the clipping path already exists and has any references.
  * @param {string}  id  id of the clipping path
  * @type boolean
  */
DvtContext.prototype.hasClipPath = function (id) {
  return this._clipPathMap[id] >= 1;
}

/**
 *  Returns a platform dependent implementation of the one and only
 *  tooltip manager.
 *  @type DvtTooltipManager 
 */
DvtContext.prototype.getTooltipManager = function (id) {
    if (!id)
      id = "_dvtTooltip";
    var stageId = this.getStage().getId();
    id = id+stageId;
    var manager = this._tooltipManagers[id];
    if (!manager) {
        this._tooltipManagers[id] = this._implFactory.newTooltipManager(id);
    }
    return this._tooltipManagers[id];
}

/**
  * Get the single scheduler instance for this context.
  * 
  * @type DvtScheduler
  */
DvtContext.prototype.getScheduler = function () {
  if (!this._scheduler)
  {
    this._scheduler = new DvtScheduler(this);
  }
  
  return this._scheduler;
}

/**
  * Obtain document utils singleton.
  *
  * @type Object
  */
DvtContext.prototype.getDocumentUtils = function () {
  return this._implFactory.getDocumentUtils();
}

/**
  * Get coordinates relative to stage.
  *
  * @type DvtPoint
  */
DvtContext.prototype.getRelativePosition = function (pageX, pageY) {
  var docUtils     = this.getDocumentUtils();
  if (docUtils) {
      var stage = this.getStage();
      var position     = docUtils.getStageRelativePosition(stage, pageX, pageY);
      return position;
  }
  return null;
}

/**
  * Get absolute coordinates of stage.
  *
  * @type DvtPoint
  */
DvtContext.prototype.getStageAbsolutePosition = function () {
  var docUtils     = this.getDocumentUtils();
  if (docUtils) {
      var stage = this.getStage();
      var position     = docUtils.getStageAbsolutePosition(stage);
      return position;
  }
  return null;
}

DvtContext.prototype.getCustomTooltipManager = function(id)
{
    if (!id)
      id = "_dvtCustomTooltip";
    var stageId = this.getStage().getId();
    id = id+stageId;
    var manager = this._customTooltipManagers[id];
    if (!manager) {
        this._customTooltipManagers[id] = new DvtCustomTooltipManager(this, id);
    }
    return this._customTooltipManagers[id];
}


/**
  *   Obtains the external Js interface singleton.
  *   @returns {DvtContext}  the external interface singleton.
  */
DvtContext.prototype.getJsExt = function()
{
   if (! this._externalInterface) {
     this._externalInterface = this._implFactory.newJsExt();
   }

   return this._externalInterface ;

}

/**
 * Determine whether this context supports measuring text.
 * @return {boolean}  true if measuring text is supported, false if not
 */
DvtContext.prototype.supportsMeasureText = function() {
  return false;
}

/**
 * Measures text dimensions using a single dedicated DvtText object. The dedicated DvtText is located at (0, 0), so
 * if the horizAlign and vertAlign are provided, the returned x and y will contain the offset of the bounding box
 * top left corner relative to the text x and y.
 * @param {string}  textString  The text string to measure
 * @param {DvtCSSStyle}  cssStyle  CSS object specifying font styles
 * @param {Number} horizAlign  (optional) The horizontal alignment
 * @param {Number} vertAlign  (optional) The vertical alignment 
 * @returns {DvtRectangle}  The text dimensions
 */
DvtContext.prototype.measureText = function(textString, cssStyle, horizAlign, vertAlign) {
  return null;
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*   DvtImplFactory                                                    */
/*---------------------------------------------------------------------*/
/**
 * Abstract class defining implementation factory methods for creating
 * implementation instances for platform specific objects, such as shapes.
 * @extends DvtObj
 * @class DvtImplFactory
 * @constructor
 * @param {DvtContext} context The context object
 */
var DvtImplFactory = function(context) {
  this.Init(context);
};

DvtObj.createSubclass(DvtImplFactory, DvtObj, "DvtImplFactory");



/*---------------------------------------------------------------------*/
/*  Init()                                                             */
/*---------------------------------------------------------------------*/
/**
  *  Object initializer.
  *  @protected
  */
DvtImplFactory.prototype.Init = function(context) {
   this._context = context ;
} ;

/*---------------------------------------------------------------------*/
/*   getContext()                                                      */
/*---------------------------------------------------------------------*/
/**
 * @protected
 */
DvtImplFactory.prototype.GetContext = function() {
  return this._context;
}

/*---------------------------------------------------------------------*/
/*     Base container and shape constructors                           */
/*---------------------------------------------------------------------*/

/**
  *  @returns {DvtObj}
  */
DvtImplFactory.prototype.newContainer = function (id) {
  return null; // subclasses should override;
};

/**
  *  @returns {DvtObj}
  */
DvtImplFactory.prototype.newArc = function (cx, cy, rx, ry, sa, ae, clos,id) {
  return null; // subclasses should override
};

/**
  *  @returns {DvtObj}
  */
DvtImplFactory.prototype.newCircle = function (cx, cy, r, id) {
  return null; // subclasses should override
};

/**
  *  @returns {DvtObj}
  */
DvtImplFactory.prototype.newImage = function (imageSrc, x, y, w, h, id) {
  return null; // subclasses should override
};

/**
  *  @returns {DvtObj}
  */
DvtImplFactory.prototype.newLine = function (x1, y1, x2, y2, id) {
  return null; // subclasses should override
};

/**
  *  @returns {DvtObj}
  */
DvtImplFactory.prototype.newOval = function (cx, cy, rx, ry, id) {
  return null; // subclasses should override
};

/**
  *  @returns {DvtObj}
  */
DvtImplFactory.prototype.newPath = function (cmds, id) {
  return null; // subclasses should override
};

/**
  *  @returns {DvtObj}
  */
DvtImplFactory.prototype.newPolygon = function (arPoints, id) {
  return null; // subclasses should override
};

/**
  *  @returns {DvtObj}
  */
DvtImplFactory.prototype.newRect = function (x, y, w, h, id) {
  return null; // subclasses should override
};

/**
  *  @returns {DvtObj}
  */
DvtImplFactory.prototype.newText = function (textStr, x, y, id) {
  return null; // subclasses should override
};


/*---------------------------------------------------------------------*/
/*     Other Constructors                                              */
/*---------------------------------------------------------------------*/

DvtImplFactory.prototype.newParser = function() {
  return null; // subclasses should override
};

/**
  * Create a new timer.
  */
DvtImplFactory.prototype.newTimer = function () {
  return null; //subclasses must override
};

/**
 * Create a new context menu handler impl.
 */
DvtImplFactory.prototype.newContextMenuHandler = function () {
  return null; //subclasses must override
};

/**
  * Returns a new DvtTextArea object.
  * @param textStr {string} the text string
  * @param x {number} x the x position
  * @param y {number} y the y position
  * @param {String} id  Optional ID for the object (see also {@link DvtDisplayable#setId}) 
  * @override
  * @type DvtTruncatingText
  */
DvtImplFactory.prototype.newTextArea = function (x, y, id) {
  return null; // subclasses should override;
};

/**
  * Returns a new DvtTextFormatted object.
  * @param textStr {string} the text string
  * @param x {number} x the x position
  * @param y {number} y the y position
  * @param {String} id  Optional ID for the object (see also {@link DvtDisplayable#setId}) 
  * @override
  * @type DvtTruncatingText
  */
DvtImplFactory.prototype.newTextFormatted = function (x, y, id) {
  return null; // subclasses should override;
};

/**
 *  Returns a new DvtMarker object which represents a graph or legend marker.
 *  @param {number} type The type of the marker (see {@link DvtMarker}).
 *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
 *  <p>
 *  Example:<br><br><code>
 *  var rect = factory.newMarker(type, 'myRect') ;<br>
 *  </code>
 *  @returns A new {@link DvtMarker} object.
 *  @type DvtMarker
 *  @override
 */
DvtImplFactory.prototype.newMarker = function (type, id) {
  return null;
};

/**
  * Create a new tooltip manager.
  */
DvtImplFactory.prototype.newTooltipManager = function (id) {
  return null;
};

/**
  * Create a new rich tooltip manager.
  */
DvtImplFactory.prototype.newRichTooltipManager = function (id) {
  return null;
};

/**
  * Obtain document utils singleton.
  *
  * @type Object
  * @override
  */
DvtImplFactory.prototype.getDocumentUtils = function () {
  return null;
};
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*   DvtIterator   An iterator for walking the children of a DvtContainer  */
/*-------------------------------------------------------------------------*/
/**
  *  @constructor
  *  Do not create directly - use container.getIterator().
  *  @extends DvtObj
  *  @class  DvtIterator is an iterator for walking the children of DvtContainer. This object is
  *  returned by container.getIterator()  - do not create programatically.
  *  <p>
  *  Typical usage:<br><br><code>
  *  var iter = container.getIterator(true) ;       // deep iterator<br>  
  * <br>
  *  var  obj ;<br>
  *  while ((obj = iter.getNext()) !== null) {<br>
  *     . . .<br>
  *  }<br></code>
  *
  */
var  DvtIterator = function(arList, bDeep)
{
   this._Init(arList, bDeep) ;
};

DvtObj.createSubclass(DvtIterator, DvtObj, "DvtIterator");

/**
  *  Internal flags
  *   @private
  */
DvtIterator._NONE  = 0 ;
/** @private */
DvtIterator._FIRST = 1 ;
/** @private */
DvtIterator._LAST  = 2 ;
/** @private */
DvtIterator._NEXT  = 3 ;






/*--------------------------------------------------------------------*/
/*  getNext()          Return the next object from the collection     */
/*--------------------------------------------------------------------*/
/**
  *  Returns the next object in the collection.
  *  @returns {DvtObj}  The next object in the collection, or null if 
  *  all objects have been returned.
  */
DvtIterator.prototype.getNext = function()
{
   var  obj = null ;          // return value

   if (this._stackPos >= 0) {

      var se    = this._arStack[this._stackPos] ;
      var list  = se._list ;

      if (list) {
         if (se._currentObj) {
           obj = ((++se._pos < list.length)? list[se._pos] : null) ;
         }
         else {
           obj = list[0] ;        // get the first object
           se._pos = 0 ;
         }
         se._currentObj = obj ;
      }

      if (! obj) {
         this._pop() ;                   // pop current stack context
         if (this._stackPos >= 0) {
           this._bInRecurse = true ;
           obj = this.getNext() ;       // and recurse
           this._bInRecurse = false ;
         }
      }

      //  If we are returning a container and this is a deep iterator,
      //  make a note so that the next getNext() will drill down into the
      //  the container.

      if (! this._bInRecurse) {   // only do this out of getNext() recursion
        if ((this._stackPos >= 0) && this._bDeep && obj) {
           if (obj.getNumChildren() !== 0) {     // does object contain its own children?
             this._push(obj._getChildren()) ;        // save current stack context
           }
        }
      }
   }

   this._lastObjReturnedFlags = (obj? DvtIterator._NEXT : DvtIterator._NONE) ;
   this._lastObjReturned = obj ;              // remember for getPrevious()

   return obj ;
} ;


/*--------------------------------------------------------------------*/
/*   _Init()                                                          */
/*--------------------------------------------------------------------*/
/** @private */
DvtIterator.prototype._Init = function(arList, bDeep)
{
   this._bDeep      = bDeep ? bDeep : false ;
   this._bInRecurse = false ;

   this._arStack  = null ;
   this._stackPos = -1 ;

   this._lastObjReturned = null ;
   this._lastObjReturnedFlags = DvtIterator._NONE;

   if (arList) {
     this._push(arList) ;
   }

} ;


/*----------------------------------------------------------------------*/
/*   _pop()     Pop the current StackEntry from the stack context list  */
/*----------------------------------------------------------------------*/
/** @private */
DvtIterator.prototype._pop =  function()
{
   if ((this._arStack !== null) && (this._stackPos >= 0)) {

     var  se =  this._arStack[this._stackPos] ;  // destroy
     se._list       = null ;                     // the stack list
     se._currentObj = null ;                     // entry ;
     this._arStack[this._stackPos] = null ;

     if (--this._stackPos < 0) {
       this._arStack = null ;          // stack is now empty
     }
   }

   //  Note:  although we have logically popped the stack, the stack array
   //         length is unchanged. We will use the "next" position in the
   //         stack on the next push.
} ;



/*----------------------------------------------------------------------*/
/*   _push()          Place a list on the stack context list            */
/*----------------------------------------------------------------------*/
/** @private */
DvtIterator.prototype._push = function(ar)
{
   if (this._arStack === null) {
     this._arStack = [] ;
   }

   var  se  = {} ;                   // create a stacklist entry
   se._list       = ar ;
   se._currentObj = null ;
   se._pos        = -1 ;

   this._stackPos++ ;

   if (this._stackPos < this._arStack.length) {
     this._arStack[this._stackPos] = se ;            // reuse a slot
   }
   else {
     this._arStack.push(se) ;
   }
} ;
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*   DvtJsExt      Abstract implementation for Js external access      */
/*---------------------------------------------------------------------*/

//  Placeholder - revist if/when callbacks are used.


/**
  *  Provides a gateway for access to external Js functionality. 
  *  @class DvtJsExt
  *  @extends DvtObj
  *  @constructor
  */
var  DvtJsExt = function()
{
};

DvtObj.createSubclass(DvtJsExt, DvtObj, "DvtJsExt");


// ADF logger levels.

/** @final @type number  */
DvtJsExt.LOG_OFF      = 1 ;
/** @final @type number  */
DvtJsExt.LOG_SEVERE   = 2 ;
/** @final @type number  */
DvtJsExt.LOG_WARNING  = 3 ;
/** @final @type number  */
DvtJsExt.LOG_INFO     = 4 ;
/** @final @type number  */
DvtJsExt.LOG_CONFIG   = 5 ;
/** @final @type number  */
DvtJsExt.LOG_FINE     = 6 ;
/** @final @type number  */
DvtJsExt.LOG_FINER    = 7 ;
/** @final @type number  */
DvtJsExt.LOG_FINEST   = 8 ;
/** @final @type number  */
DvtJsExt.LOG_ALL      = 9 ;

//  Performance 

/** @final @type number  */
DvtJsExt.PERF_PROFILING   = 0;
/** @final @type number  */
DvtJsExt.PERF_TIMING      = 1;


//  PPR attributes

/** @final @type String  */
DvtJsExt.PPR_ATTRIB_HSS           = "hideShowSeriesInfo";

/** @final @type String  */
DvtJsExt.PPR_ATTRIB_HS_LAYERS     = "_hideShowLayersInfo";

/** @final @type String  */
DvtJsExt.PPR_ATTRIB_SEL           = "selectionInfo";

/** @final @type String  */
DvtJsExt.PPR_ATTRIB_PIE_EXPLODE  = "pieSliceExplodeInfo";


//  External ADF DVT JS routines

/** @final @private @type String  */
DvtJsExt.JS_FPREQUESTREFRESH     = "adf_dvt_fpRefresh" ;
/** @final @private @type String  */
DvtJsExt.JS_FPGETLOGLEVEL        = "adf_dvt_fpGetLogLevel" ;
/** @final @private @type String  */
DvtJsExt.JS_FPINITNOTIFY         = "adf_dvt_fpInitNotify" ;
/** @final @private @type String  */
DvtJsExt.JS_FPLOGGER             = "adf_dvt_fpLogger" ;
/** @final @private @type String  */
DvtJsExt.JS_FPMSG                = "adf_dvt_fpMsg" ;
/** @final @private @type String  */
DvtJsExt.JS_FP_PERF_LOGGER       = "adf_dvt_fpPerfLogger" ;

/** @final @private @type String  */
DvtJsExt.JS_FP_SET_PPR_PROPERTY  = "AdfPage.PAGE.findComponent('X-ID-X').getPeer().setDVTPPRAttribute" ;

/** @final @private @type String  */
DvtJsExt.JS_FP_SEND_PPR          = "AdfPage.PAGE.findComponent('X-ID-X').getPeer().sendToServerPPR({source:'X-ID-X'})" ;
/** @final @private @type String  */
DvtJsExt.JS_FP_SEND_PPR_NON_BLOCKING = "AdfPage.PAGE.findComponent('X-ID-X').getPeer().sendToServerPPR3({source:'X-ID-X'})" ;

/** @final @private @type String  */
DvtJsExt.JS_FPUPDATEPOPUPBOUNDS  = "adf_dvt_fpUpdatePopupHelper";

/** @final @private @type String  */

DvtJsExt.JS_COMPONENT_LOADED     = "AdfDhtmlCommonFlashPeer.setComponentLoaded" ;




/*---------------------------------------------------------------------*/
/*                                 ADF                                 */
/*---------------------------------------------------------------------*/

/**  
  *  Sets a PPR property value.
  *  @param {String}  id  An id to be substituted in the function call.
  *  @param {String)  prop a property such as {@link DvtJsExt#PPR_ATTRIB_HSS}
  *  @param {String}  value  the property value.
  */
DvtJsExt.prototype.sendPPRProperty = function(id, prop, value)
{
};


DvtJsExt.prototype.sendPPR  = function(id)
{
};


DvtJsExt.prototype.sendPPRNonBlocking = function(id)
{
};



/*---------------------------------------------------------------------*/
/*                    External Javascript invocation                   */
/*---------------------------------------------------------------------*/

/**
  *   Performs the specified Javascript.
  *   @returns {unknown} the result, if any, of Javascript.
  */
DvtJsExt.prototype.callJs = function(js)
{
};

/**
  *   Performs the specified Javascript.
  *   @param {String} js the js function to call
  *   @param {unknown} param the param to pass to the function
  *   @returns {unknown} the result, if any, of Javascript.
  */
DvtJsExt.prototype.callJsWithParam = function(js, param)
{
};

/*---------------------------------------------------------------------*/
/*                            Logging                                  */
/*---------------------------------------------------------------------*/

/**
  *   Returns the current ADF log level.
  *   @returns {number} the current ADF log level (such as {DvtJsExt#WARNING}).
  */
DvtJsExt.prototype.getLogLevel = function()
{
    return  adf_dvt_fpGetLogLevel() ;
};


/**
  *   Forwards a message to the ADF logger.
  *   @param  {String} msg the message to be logged.
  *   @param  {number} level the log level of the messsage.  If omitted
  *                          a value of {DvtJsExt#LOG_INFO} is assumed.
  */
DvtJsExt.prototype.log = function(msg, level)
{
};


/**
  *   Forwards a message string to ADF DVT.
  *   @param  {String} msg the message to be sent.
  */
DvtJsExt.prototype.msg = function(msg)
{
};



/*---------------------------------------------------------------------*/
/*                           Performance                               */
/*---------------------------------------------------------------------*/

/**
  *   Forwards a performance message.
  *   @param  {String} msg the performance message to be sent.
  *   @param  {number} type  the performance type (such as {DvtJsExt#PERF_TIMING}).
  */
DvtJsExt.prototype.perf = function(msg, type)
{
};


DvtJsExt.prototype.requestRefresh = function()
{
};


/**
 * A base class for shape fills, strokes, shadows, etc.
 * {@link DvtLockable}.
 * @extends DvtObj
 * @class DvtLockable  A base class for shape fills, strokes,shadows, etc.
 * @constructor  Do not create/use directly.
 */
var DvtLockable = function () {
  this._Init();
}

DvtObj.createSubclass(DvtLockable, DvtObj, "DvtLockable");

/** 
 * @private 
 */
DvtLockable.prototype._Init = function () {
  this._bLocked = false;
}

/**
 *  Returns true if the fill object is immutable, else false.
 *  @type Boolean
 */
DvtLockable.prototype.isLocked = function () {
  return this._bLocked;
}

/**
 *  Makes the property set immutable.  The only way to change the object's
 *  properties once it is immutable is to clone it and apply property changes
 *  to the cloned object.  This method should not be called by application code
 *  and should only be used by toolkit code.
 *  @private
 */
DvtLockable.prototype.__lock = function () {
  this._bLocked = true;
}
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an item that can be scheduled to run with a scheduler.
  * @extends DvtObj
  * @class DvtScheduled
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param {DvtScheduler}  scheduler  scheduler used to run this item
  * @param {number}  duration  length of time to run item, in seconds
  * @param {number}  delay  time to delay start of item, in seconds
  * @param {function}  easing  easing function to use with this item
  */
var DvtScheduled = function(context, scheduler, duration, delay, easing)
{
  this.Init(context, scheduler, duration, delay, easing);
};

DvtObj.createSubclass(DvtScheduled, DvtObj, "DvtScheduled");

/**
  * @private
  * State: initialize.
  */
DvtScheduled._STATE_INITIALIZE = 0;
/**
  * @private
  * State: begin.
  */
DvtScheduled._STATE_BEGIN = 1;
/**
  * @private
  * State: run.
  */
DvtScheduled._STATE_RUN = 2;

/**
  * @protected
  * Calculate the percent progress of the given value between the min and max.
  * 
  * @param {number}  val  current value
  * @param {number}  min  minimum value
  * @param {number}  max  maximum value
  * @type number
  */
DvtScheduled.CalcProgress = function(val, min, max)
{
  var diff = (max - min);
  if (diff == 0)
  {
    return 0.5;
  }
  else
  {
    var ret = ((val - min) / diff);
    if (ret > 1)
    {
      ret = 1;
    }
    return ret;
  }
};

/**
  * @protected
  */
DvtScheduled.prototype.Init = function(context, scheduler, duration, delay, easing)
{
  this._scheduler = scheduler;
  this.setDuration(duration); //in seconds
  this.setDelay(delay); //in seconds
  this.setEasing(easing);
  
  this._bRunning = false;
  this._startTime = null;
  this._progress = 0;
  this._state = DvtScheduled._STATE_INITIALIZE;
  
  this._onInit = null;
  this._onInitObj = null;
  this._onStart = null;
  this._onStartObj = null;
  this._onEnd = null;
  this._onEndObj = null;
  this._onStep = null;
  this._onStepObj = null;
};

/**
  * Get the duration of this item, in seconds.
  * 
  * @type number
  */
DvtScheduled.prototype.getDuration = function()
{
  return this._duration;
};

/**
  * Set the duration of this item.
  * 
  * @param {number}  duration  duration of this item, in seconds
  */
DvtScheduled.prototype.setDuration = function(duration)
{
  this._duration = (duration ? duration : 0.5);
};

/**
  * Get the delay for the start of this item, in seconds.
  * 
  * @type number
  */
DvtScheduled.prototype.getDelay = function()
{
  return this._delay;
};

/**
  * Set the delay for the start of this item.
  * 
  * @param {number}  delay  delay for the start of this item, in seconds
  */
DvtScheduled.prototype.setDelay = function(delay)
{
  this._delay = (delay ? delay : 0);
};

/**
  * Get the easing function for this animation.
  * 
  * @type function
  */
DvtScheduled.prototype.getEasing = function()
{
  return this._easing;
};

/**
  * Set the easing function for this animation.
  * 
  * @param {function}  easing  easing function
  */
DvtScheduled.prototype.setEasing = function(easing)
{
  this._easing = (easing ? easing : DvtEasing.cubicInOut);
};

/**
  * Set the function to call when this item initializes.
  * 
  * @param {function}  onInit  function to call when this item initializes
  * @param {object}  onInitObj  optional reference to object instance on which the 
  *        function is defined
  */
DvtScheduled.prototype.setOnInit = function(onInit, onInitObj)
{
  this._onInit = onInit;
  if (onInitObj)
  {
    this._onInitObj = onInitObj;
  }
  else
  {
    this._onInitObj = null;
  }
};

/**
  * Get the function to call when this item initializes.
  * Returns an array of two elements: 
  * [0] the function
  * [1] optional reference to object instance on which the function is defined
  * 
  * @type array
  */
DvtScheduled.prototype.getOnInit = function()
{
  return [this._onInit, this._onInitObj];
};

/**
  * Set the function to call when this item starts.
  * 
  * @param {function}  onStart  function to call when this item starts
  * @param {object}  onStartObj  optional reference to object instance on which the 
  *        function is defined
  */
DvtScheduled.prototype.setOnStart = function(onStart, onStartObj)
{
  this._onStart = onStart;
  if (onStartObj)
  {
    this._onStartObj = onStartObj;
  }
  else
  {
    this._onStartObj = null;
  }
};

/**
  * Get the function to call when this item starts.
  * Returns an array of two elements: 
  * [0] the function
  * [1] optional reference to object instance on which the function is defined
  * 
  * @type array
  */
DvtScheduled.prototype.getOnStart = function()
{
  return [this._onStart, this._onStartObj];
};

/**
  * Set the function to call when this item ends.
  * 
  * @param {function}  onEnd  function to call when this item ends
  * @param {object}  onEndObj  optional reference to object instance on which the 
  *        function is defined
  */
DvtScheduled.prototype.setOnEnd = function(onEnd, onEndObj)
{
  this._onEnd = onEnd;
  if (onEndObj)
  {
    this._onEndObj = onEndObj;
  }
  else
  {
    this._onEndObj = null;
  }
};

/**
  * Get the function to call when this item ends.
  * Returns an array of two elements: 
  * [0] the function
  * [1] optional reference to object instance on which the function is defined
  * 
  * @type array
  */
DvtScheduled.prototype.getOnEnd = function()
{
  return [this._onEnd, this._onEndObj];
};


/**
  * Set the function to call at each step of this item.
  * 
  * @param {function}  onStep  function to call at each step of this item
  * @param {object}  onStepObj  optional reference to object instance on which the 
  *        function is defined
  */
DvtScheduled.prototype.setOnStep = function(onStep, onStepObj)
{
  this._onStep = onStep;
  if (onStepObj)
  {
    this._onSepObj = onStepObj;
  }
  else
  {
    this._onStepObj = null;
  }
};

/**
  * Get the function to call at each step of this item.
  * Returns an array of two elements: 
  * [0] the function
  * [1] optional reference to object instance on which the function is defined
  * 
  * @type array
  */
DvtScheduled.prototype.getOnStep = function()
{
  return [this._onStep, this._onStepObj];
};

/**
  * Determine if this item is running.
  * 
  * @type boolean
  */
DvtScheduled.prototype.isRunning = function()
{
  return this._bRunning;
};

/**
  * Play this item.
  */
DvtScheduled.prototype.play = function()
{
  if (!this._bRunning)
  {
    this._bRunning = true;
    this.ProcessPlay();
    this._scheduler.addScheduled(this);
  }
};

/**
  * Stop this item.
  * 
  * @param {boolean}  bJumpToEnd  true to jump to 100% progress, 
  *        false to stop at current progress
  */
DvtScheduled.prototype.stop = function(bJumpToEnd)
{
  this._scheduler.removeScheduled(this);
  if (bJumpToEnd)
  {
    this.ProcessStep(1);
  }
  this.ProcessEnd();
};

/**
  * Pause this item.
  */
DvtScheduled.prototype.pause = function()
{
  this._bRunning = false;
  //this._scheduler.removeScheduled(this);
};

/**
  * Reset this item.
  */
DvtScheduled.prototype.reset = function()
{
  this._state = DvtScheduled._STATE_INITIALIZE;
};

/**
  * Process this item for the given timestamp.
  * 
  * @param {number}  time  current timestamp, in milliseconds
  */
DvtScheduled.prototype.processTime = function(time)
{
  if (!this._bRunning)
  {
    return;
  }
  
  var elapsedTime = time - this._startTime;
  if (elapsedTime < 0)
  {
    return false;
  }
  
  var duration = 1000 * this.GetTotalDuration();
  var progress = 1;
  if (duration != 0)
  {
    progress = elapsedTime / duration;
  }
  if (progress > 1)
  {
    progress = 1;
   }
  this.ProcessStep(progress);
  
  var bDone = (progress >= 1);
  if (bDone)
  {
    this.ProcessEnd();
  }
  return bDone;
};

/**
  * @protected
  * Get the total duration of this item, in seconds.
  * 
  * @type number
  */
DvtScheduled.prototype.GetTotalDuration = function()
{
  return this._delay + this._duration;
};

/**
  * @protected
  * Process when this item is played.
  */
DvtScheduled.prototype.ProcessPlay = function()
{
  if (this._state == DvtScheduled._STATE_INITIALIZE)
  {
    this.ProcessInitialize();
  }
  
  if (this._state == DvtScheduled._STATE_BEGIN)
  {
    this._startTime = new Date().getTime();
    this.ProcessStart();
  }
  else
  {
    var elapsedTime = this._progress * 1000 * this.GetTotalDuration();
    this._startTime = new Date().getTime() - elapsedTime;
  }
  
  this._state = DvtScheduled._STATE_RUN;
};

/**
  * @protected
  * Process initialization of this item when it starts to play.
  */
DvtScheduled.prototype.ProcessInitialize = function()
{
  if (this._onInit)
  {
    this._onInit.call(this._onInitObj);
  }
  this.Initialize();
  this._state = DvtScheduled._STATE_BEGIN;
};

/**
  * @protected
  * Process the start of this item after it initializes.
  */
DvtScheduled.prototype.ProcessStart = function()
{
  this._bRunning = true;
  this._progress = 0;
  this.Start();
  if (this._onStart)
  {
    this._onStart.call(this._onStartObj);
  }
};

/**
  * @protected
  * Process a step of this item as it plays.
  * 
  * @param {number}  progress  percent progress of this item
  */
DvtScheduled.prototype.ProcessStep= function(progress)
{
  this._progress = progress;
  var prog = progress;
  if (this._delay > 0)
  {
    prog = DvtScheduled.CalcProgress(progress, this._delay / this.GetTotalDuration(), 1);
  }
  if (prog >= 0)
  {
    if (this._easing)
    {
      prog = this._easing(prog);
    }
    this.Step(prog);
    
    if (this._onStep)
    {
      this._onStep.call(this._onStepObj, prog);
    }
  }
};

/**
  * @protected
  * Process when this item stops.
  */
DvtScheduled.prototype.ProcessEnd = function()
{
  //only process end if still running
  if (this._bRunning)
  {
    this._bRunning = false;
    this._progress = 1;
    this.End();
    this._state = DvtScheduled._STATE_BEGIN;
    if (this._onEnd)
    {
      this._onEnd.call(this._onEndObj);
    }
  }
};

/**
  * @protected
  * Initialize this item.
  */
DvtScheduled.prototype.Initialize = function()
{
  //do nothing; subclasses should implement
};

/**
  * @protected
  * Start playing this item.
  */
DvtScheduled.prototype.Start = function()
{
  //do nothing; subclasses should implement
};

/**
  * @protected
  * Step this item.
  * 
  * @param {number}  progress  percent progress of this item
  */
DvtScheduled.prototype.Step = function(progress)
{
  //do nothing; subclasses should implement
};

/**
  * @protected
  * End this item.
  */
DvtScheduled.prototype.End = function()
{
  //do nothing; subclasses should implement
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing a scheduling service.
  * @extends DvtObj
  * @class DvtScheduler
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  */
var DvtScheduler = function(context)
{
  this.Init(context);
};

DvtObj.createSubclass(DvtScheduler, DvtObj, "DvtScheduler");

/**
  * @protected
  */
DvtScheduler.prototype.Init = function(context)
{
  this._timer = new DvtTimer(context, 1000 / 30, this.HandleTimer, this);
  this._scheduledItems = new Array();
  this._bRunning = false;
};

/**
  * Set the interval between timer ticks, in milliseconds.
  * 
  * @param {number}  interval  interval between timer ticks, in milliseconds
  */
DvtScheduler.prototype.setInterval = function(interval)
{
  var i = interval;
  if (i < 0)
  {
    i = 0;
  }
  this.pause();
  this._timer.setInterval(i);
  this.play();
};

/**
  * Get the interval between timer ticks.
  * 
  * @type number
  */
DvtScheduler.prototype.getInterval = function()
{
  return this._timer.getInterval();
};

/**
  * @protected
  * Handle a timer tick.
  */
DvtScheduler.prototype.HandleTimer = function()
{
  var time = new Date().getTime();
  for (var i = 0; i < this._scheduledItems.length; i++)
  {
    var scheduled = this._scheduledItems[i];
    if (scheduled.processTime(time))
    {
      this.removeScheduled(scheduled);
    }
  }
  
  if (this._scheduledItems.length < 1)
  {
    this.pause();
  }
};

/**
  * Add a scheduled item to be run.  
  * 
  * @param {DvtScheduled}  scheduled  item to add
  */
DvtScheduler.prototype.addScheduled = function(scheduled)
{
  var i = DvtArrayUtils.getIndex(this._scheduledItems, scheduled)
  if (i < 0)
  {
    this._scheduledItems.push(scheduled);
  }
  
  this.play();
};

/**
  * Remove a scheduled item.
  * 
  * @param {DvtScheduled}  scheduled  item to remove
  */
DvtScheduler.prototype.removeScheduled = function(scheduled)
{
  var i = DvtArrayUtils.getIndex(this._scheduledItems, scheduled);
  if (i >= 0)
  {
    this._scheduledItems.splice(i, 1);
  }
};

/**
  * Play the scheduler.
  */
DvtScheduler.prototype.play = function()
{
  if (!this._bRunning)
  {
    for (var i = 0; i < this._scheduledItems.length; i++)
    {
      var scheduled = this._scheduledItems[i];
      if (!scheduled.isRunning())
      {
        scheduled.play();
      }
    }
    
    this._bRunning = true;
    this._timer.start();
  }
};

/**
  * Pause the scheduler.
  */
DvtScheduler.prototype.pause = function()
{
  if (this._bRunning)
  {
    this._bRunning = false;
    this._timer.stop();
    
    for (var i = 0; i < this._scheduledItems.length; i++)
    {
      var scheduled = this._scheduledItems[i];
      if (scheduled.isRunning())
      {
        scheduled.pause();
      }
    }
  }
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
 * Class representing an animator that animates a property change.
 * @extends DvtScheduled
 * @class DvtAnimator
 * @constructor
 * 
 * @param {DvtContext}  context  platform specific context object
 * @param {number}  duration  length of animation, in seconds
 * @param {number}  delay  time to delay start of animation, in seconds
 * @param {function}  easing  easing function to use with this animation
 */
var DvtAnimator = function(context, duration, delay, easing) {
  this.Init(context, duration, delay, easing);
};

DvtObj.createSubclass(DvtAnimator, DvtScheduled, "DvtAnimator");

/**
 * Property type: number.
 */
DvtAnimator.TYPE_NUMBER = "typeNumber";
/**
 * Property type: matrix.
 */
DvtAnimator.TYPE_MATRIX = "typeMatrix";
/**
 * Property type: array of numbers.
 */
DvtAnimator.TYPE_NUMBER_ARRAY = "typeNumberArray";
/**
 * Property type: color.
 */
DvtAnimator.TYPE_COLOR = "typeColor";
/**
 * Property type: array of colors.
 */
DvtAnimator.TYPE_COLOR_ARRAY = "typeColorArray";
/**
 * Property type: grow polyline points.
 */
DvtAnimator.TYPE_GROW_POLYLINE = "typeGrowPolyline";
/**
 * Property type: rectangle.
 */
DvtAnimator.TYPE_RECTANGLE = "typeRectangle";
/**
 * Property type: point.
 */
DvtAnimator.TYPE_POINT = "typePoint";
/**
 * Property type: path.
 */
DvtAnimator.TYPE_PATH = "typePath";
/**
 * Property type: grow path points.
 */
DvtAnimator.TYPE_GROW_PATH = "typeGrowPath";
/**
 * Property type: fill.
 */
DvtAnimator.TYPE_FILL = "typeFill";
/**
 * Property type: stroke.
 */
DvtAnimator.TYPE_STROKE = "typeStroke";
/**
 * Property type: polyline.
 */
DvtAnimator.TYPE_POLYLINE = "typePolyline";

DvtAnimator._KEY_PREFIX = "dvtAnimPropMap_";

/**
 * @protected
 */
DvtAnimator.prototype.Init = function(context, duration, delay, easing) {
  DvtAnimator.superclass.Init.call(this, context, context.getScheduler(), duration, delay, easing);
  
  this._context = context;
  this._props = {};
};

/**
 * Add a property to animate.
 * 
 * @param {string}  type  type of property
 * @param {object}  obj  object being animated
 * @param {function}  getter  getter function for property being animated
 * @param {function}  setter  setter function for property being animated
 * @param  destVal  destination value to animate to
 */
DvtAnimator.prototype.addProp = function(type, obj, getter, setter, destVal) {
  var item = this.GetPropItem(obj, getter);
  if (item) {
    item.destVal = destVal;
  }
  else {
    var item = new DvtAnimatorPropItem(type, obj, getter, setter, destVal);
    //map keys must be strings, so always generate a string key for objects 
    //and functions
    var keyObj = DvtAnimator.GetKey(obj);
    if (!this._props[keyObj]) {
      this._props[keyObj] = {};
    }
    this._props[keyObj][DvtAnimator.GetKey(getter)] = item;
  }
};

/**
 * Removes a property to animate.
 * 
 * @param {object}  obj  object being animated
 * @param {function}  getter  getter function for property being animated
 */
DvtAnimator.prototype.removeProp = function(obj, getter) {
  var item = this.GetPropItem(obj, getter);
  if (item) {
    var destVal = item.destVal;
    delete this._props[DvtAnimator.GetKey(obj)][DvtAnimator.GetKey(getter)];
    return destVal;
  }
  return null;
};


/**
 * Get the destination value for a property being animated.
 * 
 * @param {object}  obj  object being animated
 * @param {function}  getter  getter function for property being animated
 * @param {boolean} callGetter (optional) If no destination value is found, indicates whether the getter should be called directly rather than returning null
 */
DvtAnimator.prototype.getDestVal = function(obj, getter, callGetter) {
  var item = this.GetPropItem(obj, getter);
  if (item) {
    return item.destVal;
  }
  //if not part of the animation, return null
  return callGetter ? getter.call(obj) : null;
};

/**
 * @protected
 * Get the index of the given property in the storage array, 
 * or -1 if the property is not found.
 * 
 * @param {object}  obj  object being animated
 * @param {function}  getter  getter function for property being animated
 */
DvtAnimator.prototype.GetPropItem = function(obj, getter) {
  var keyObj = DvtAnimator.GetKey(obj);
  if (this._props[keyObj]) {
    return this._props[keyObj][DvtAnimator.GetKey(getter)];
  }
  return null;
};

/**
 * @protected
 * Get the key to use for the given obj in the prop map.
 */
DvtAnimator.GetKey = function(obj) {
  if (!obj._dvtAnimPropMapKey) {
    obj._dvtAnimPropMapKey = DvtAnimator._KEY_PREFIX + Math.random();
  }
  return obj._dvtAnimPropMapKey;
};

/**
 * @protected
 * @override
 */
DvtAnimator.prototype.Initialize = function() {
  for (var keyObj in this._props) {
    for (var keyGetter in this._props[keyObj]) {
      var item = this._props[keyObj][keyGetter];
      item.startVal = item.getter.call(item.obj);
    }
  }
};

/**
 * @protected
 * @override
 */
DvtAnimator.prototype.Step = function(progress) {
  for (var keyObj in this._props) {
    for (var keyGetter in this._props[keyObj]) {
      var item = this._props[keyObj][keyGetter];
      var type = item.type;
      var obj = item.obj;
      var setter = item.setter;
      var destVal = item.destVal;
      
      var startVal  = item.startVal;
      var interpVal = DvtInterpolator.interpolate(this._context, type, startVal, destVal, progress);
      setter.call(obj, interpVal);
    }
  }
};

// Nested class begin //////////////////////////////////////

var DvtAnimatorPropItem = function(type, obj, getter, setter, destVal) {
  this.type = type;
  this.obj = obj;
  this.getter = getter;
  this.setter = setter;
  this.destVal = destVal;
  this.startVal = null;
};

DvtObj.createSubclass(DvtAnimatorPropItem, DvtObj, "DvtAnimatorPropItem");

// Nested class end ////////////////////////////////////////
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Abstract base class representing something that can be played, like an animation.
  * @extends DvtObj
  * @class DvtPlayable
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  */
var DvtPlayable = function(context)
{
  this.Init(context);
};

DvtObj.createSubclass(DvtPlayable, DvtObj, "DvtPlayable");

/**
  * Append a function to the end of the given playable's current onEnd function.
  * 
  * @param {DvtPlayable}  playable  playable to append onEnd function to
  * @param {function}  onEnd  new function to append to current onEnd function
  * @param {object}  onEndObj  optional reference to object instance on which the new
  *        onEnd function is defined
  */
DvtPlayable.appendOnEnd = function(playable, onEnd, onEndObj)
{
  if (!playable || !onEnd)
  {
    return;
  }
  
  var arOnEnd = playable.getOnEnd();
  if (!arOnEnd || !arOnEnd[0])
  {
    playable.setOnEnd(onEnd, onEndObj);
  }
  else
  {
    var newOnEnd = function() 
      {
        arOnEnd[0].call(arOnEnd[1]);
        onEnd.call(onEndObj);
      };
    playable.setOnEnd(newOnEnd);
  }
};

/**
  * Prepend a function to the start of the given playable's current onEnd function.
  * 
  * @param {DvtPlayable}  playable  playable to prepend onEnd function to
  * @param {function}  onEnd  new function to prepend to current onEnd function
  * @param {object}  onEndObj  optional reference to object instance on which the new
  *        onEnd function is defined
  */
DvtPlayable.prependOnEnd = function(playable, onEnd, onEndObj)
{
  if (!playable || !onEnd)
  {
    return;
  }
  
  var arOnEnd = playable.getOnEnd();
  if (!arOnEnd || !arOnEnd[0])
  {
    playable.setOnEnd(onEnd, onEndObj);
  }
  else
  {
    var newOnEnd = function() 
      {
        onEnd.call(onEndObj);
        arOnEnd[0].call(arOnEnd[1]);
      };
    playable.setOnEnd(newOnEnd);
  }
};

/**
  * Append a function to the end of the given playable's current onStart function.
  * 
  * @param {DvtPlayable}  playable  playable to append onStart function to
  * @param {function}  onStart  new function to append to current onStart function
  * @param {object}  onStartObj  optional reference to object instance on which the new
  *        onStart function is defined
  */
DvtPlayable.appendOnStart = function(playable, onStart, onStartObj)
{
  if (!playable || !onStart || !playable.getOnStart || !playable.setOnStart)
  {
    return;
  }
  
  var arOnStart = playable.getOnStart();
  if (!arOnStart || !arOnStart[0])
  {
    playable.setOnStart(onStart, onStartObj);
  }
  else
  {
    var newOnStart = function() 
      {
        arOnStart[0].call(arOnStart[1]);
        onStart.call(onStartObj);
      };
    playable.setOnStart(newOnStart);
  }
};

/**
  * Prepend a function to the start of the given playable's current onStart function.
  * 
  * @param {DvtPlayable}  playable  playable to prepend onStart function to
  * @param {function}  onStart  new function to prepend to current onStart function
  * @param {object}  onStartObj  optional reference to object instance on which the new
  *        onStart function is defined
  */
DvtPlayable.prependOnStart = function(playable, onStart, onStartObj)
{
  if (!playable || !onStart || !playable.getOnStart || !playable.setOnStart)
  {
    return;
  }
  
  var arOnStart = playable.getOnStart();
  if (!arOnStart || !arOnStart[0])
  {
    playable.setOnStart(onStart, onStartObj);
  }
  else
  {
    var newOnStart = function() 
      {
        onStart.call(onStartObj);
        arOnStart[0].call(arOnStart[1]);
      };
    playable.setOnStart(newOnStart);
  }
};

/**
  * Append a function to the end of the given playable's current onInit function.
  * 
  * @param {DvtPlayable}  playable  playable to append onInit function to
  * @param {function}  onInit  new function to append to current onInit function
  * @param {object}  onInitObj  optional reference to object instance on which the new
  *        onInit function is defined
  */
DvtPlayable.appendOnInit = function(playable, onInit, onInitObj)
{
  if (!playable || !onInit || !playable.getOnInit || !playable.setOnInit)
  {
    return;
  }
  
  var arOnInit = playable.getOnInit();
  if (!arOnInit || !arOnInit[0])
  {
    playable.setOnInit(onInit, onInitObj);
  }
  else
  {
    var newOnInit = function() 
      {
        arOnInit[0].call(arOnInit[1]);
        onInit.call(onInitObj);
      };
    playable.setOnInit(newOnInit);
  }
};

/**
  * Prepend a function to the start of the given playable's current onInit function.
  * 
  * @param {DvtPlayable}  playable  playable to prepend onInit function to
  * @param {function}  onInit  new function to prepend to current onInit function
  * @param {object}  onInitObj  optional reference to object instance on which the new
  *        onInit function is defined
  */
DvtPlayable.prependOnInit = function(playable, onInit, onInitObj)
{
  if (!playable || !onInit || !playable.getOnInit || !playable.setOnInit)
  {
    return;
  }
  
  var arOnInit = playable.getOnInit();
  if (!arOnInit || !arOnInit[0])
  {
    playable.setOnInit(onInit, onInitObj);
  }
  else
  {
    var newOnInit = function() 
      {
        onInit.call(onInitObj);
        arOnInit[0].call(arOnInit[1]);
      };
    playable.setOnInit(newOnInit);
  }
};

/**
  * @protected
  */
DvtPlayable.prototype.Init = function(context)
{
  this.Context = context;
  this._onEnd = null;
  this._onEndObj = null;
};

/**
  * Set the function to call when this playable ends.
  * 
  * @param {function}  onEnd  function to call when this playable ends
  * @param {object}  onEndObj  optional reference to object instance on which the 
  *        onEnd function is defined
  */
DvtPlayable.prototype.setOnEnd = function(onEnd, onEndObj)
{
  this._onEnd = onEnd;
  if (onEndObj)
  {
    this._onEndObj = onEndObj;
  }
  else
  {
    this._onEndObj = null;
  }
  this.OnEndUpdated();
};

/**
  * Get the function to call when this playable ends.
  * Returns an array of two elements: 
  * [0] the function
  * [1] optional reference to object instance on which the function is defined
  * 
  * @type array
  */
DvtPlayable.prototype.getOnEnd = function()
{
  return [this._onEnd, this._onEndObj];
};

/**
  * @protected
  * Called when the onEnd funciton on this playable is set.
  */
DvtPlayable.prototype.OnEndUpdated = function()
{
  //subclasses must override
};

/**
  * Play this playable.
  */
DvtPlayable.prototype.play = function()
{
  //subclasses must override
};

/**
  * Stop this playable.
  */
DvtPlayable.prototype.stop = function(bJumpToEnd)
{
  //subclasses must override
};

/**
  * Pause this playable.
  */
DvtPlayable.prototype.pause = function()
{
  //subclasses must override
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing a set of DvtPlayables that are played at the same time.
  * The playables should be passed into the constructor as either:
  * 1) individual arguments, for example:
  *    new DvtParallelPlayable(context, playable1, playable2, ...), or
  * 2) a single Array, for example:
  *    new DvtParallelPlayable(context, [playable1, playable2, ...]);
  * @extends DvtPlayable
  * @class DvtParallelPlayable
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  */
var DvtParallelPlayable = function(context)
{
  var arPlayables;
  if (arguments && arguments.length > 1 && arguments[1] instanceof Array)
  {
    arPlayables = arguments[1];
  }
  else
  {
    arPlayables = DvtArrayUtils.argsToArray(arguments);
    //remove the context from the arPlayables array
    arPlayables.splice(0, 1);
  }
  
  this.Init(context, arPlayables);
};

DvtObj.createSubclass(DvtParallelPlayable, DvtPlayable, "DvtParallelPlayable");

/**
  * @protected
  * @override
  */
DvtParallelPlayable.prototype.Init = function(context, arPlayables)
{
  DvtParallelPlayable.superclass.Init.call(this, context);
  
  this._runningCounter = 0;
  this._arPlayables = arPlayables;
  this._bStarted = false;
};

/**
  * Get the list of playables.
  * @type Array
  */
DvtParallelPlayable.prototype.getPlayables = function()
{
  return this._arPlayables;
};

/**
  * @override
  */
DvtParallelPlayable.prototype.play = function()
{
  if (this._arPlayables && this._arPlayables.length > 0)
  {
    this._runningCounter = this._arPlayables.length;
    
    var playable;
    for (var i = 0; i < this._arPlayables.length; i++)
    {
      playable = this._arPlayables[i];
      if (playable instanceof DvtPlayable)
      {
        if (!this._bStarted)
        {
          //call internal onEnd function when each Playable ends
          DvtPlayable.appendOnEnd(playable, this.OnPlayableEnd, this);
        }
        playable.play();
      }
    }
    
    this._bStarted = true;
  }
  else {
    // The onEnd listener should still be called.
    var timer = new DvtTimer(this.Context, 0, this.DoEnd, this, 1);
    timer.start();
  }
};

/**
  * @override
  */
DvtParallelPlayable.prototype.stop = function(bJumpToEnd)
{
  if (this._arPlayables)
  {
    var playable;
    for (var i = 0; i < this._arPlayables.length; i++)
    {
      playable = this._arPlayables[i];
      if (playable instanceof DvtPlayable)
      {
        playable.stop(bJumpToEnd);
      }
    }
  }
};

/**
  * @override
  */
DvtParallelPlayable.prototype.pause = function()
{
  if (this._arPlayables)
  {
    var playable;
    for (var i = 0; i < this._arPlayables.length; i++)
    {
      playable = this._arPlayables[i];
      if (playable instanceof DvtPlayable)
      {
        playable.pause();
      }
    }
  }
};

/**
  * @protected
  * Called after each playable ends.
  */
DvtParallelPlayable.prototype.OnPlayableEnd = function()
{
  //decrement the count of running playables
  this._runningCounter--;
  
  //if no more running, call the onEnd function for this ParallelPlayable
  if (this._runningCounter < 1)
  {
    this.DoEnd();
  }
};

/**
  * @protected
  * Called after all the playables have finished.
  */
DvtParallelPlayable.prototype.DoEnd = function()
{
  if (this._onEnd)
  {
    this._onEnd.call(this._onEndObj);
  }
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing a set of DvtPlayables that are played one after another.
  * The playables should be passed into the constructor as either:
  * 1) individual arguments, for example:
  *    new DvtParallelPlayable(context, playable1, playable2, ...), or
  * 2) a single Array, for example:
  *    new DvtParallelPlayable(context, [playable1, playable2, ...]);
  * @extends DvtPlayable
  * @class DvtParallelPlayable
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  */
var DvtSequentialPlayable = function(context)
{
  var arPlayables;
  if (arguments && arguments.length > 1 && arguments[1] instanceof Array)
  {
    arPlayables = arguments[1];
  }
  else
  {
    arPlayables = DvtArrayUtils.argsToArray(arguments);
    //remove the context from the arPlayables array
    arPlayables.splice(0, 1);
  }
  
  this.Init(context, arPlayables);
};

DvtObj.createSubclass(DvtSequentialPlayable, DvtPlayable, "DvtSequentialPlayable");

/**
  * @protected
  * @override
  */
DvtSequentialPlayable.prototype.Init = function(context, arPlayables)
{
  DvtSequentialPlayable.superclass.Init.call(this, context);
  
  this._arPlayables = arPlayables;
  this._currIndex = -1;
  this._bStarted = false;
};

/**
  * Get the list of playables.
  * @type Array
  */
DvtSequentialPlayable.prototype.getPlayables = function()
{
  return this._arPlayables;
};

/**
  * @override
  */
DvtSequentialPlayable.prototype.play = function()
{
  if (this._arPlayables && this._arPlayables.length > 0)
  {
    if (this._bStarted)
    {
      var currPlayable = this._arPlayables[this._currIndex];
      if (currPlayable instanceof DvtPlayable)
      {
        currPlayable.play();
      }
      return;
    }
    
    var firstPlayable;
    if (this._arPlayables[0] instanceof DvtPlayable)
      firstPlayable = this._arPlayables[0];
    var lastPlayable;
    if (this._arPlayables[this._arPlayables.length - 1] instanceof DvtPlayable)
      lastPlayable = this._arPlayables[this._arPlayables.length - 1];
    
    //var prevPlayable = firstPlayable;
    var playable;
    for (var i = 0; i < this._arPlayables.length - 1; i++)
    {
      playable = this._arPlayables[i];
      if (playable instanceof DvtPlayable)
      {
        //at the end of each playable, play the next one
        //DvtPlayable.appendOnEnd(prevPlayable, playable.play, playable);
        //prevPlayable = playable;
        
        DvtPlayable.appendOnEnd(playable, this.DoSequenceStep, this);
      }
    }
    
    //call the onEnd function for this DvtSequentialPlayable at the very
    //end of the sequence
    //if (playable)
    //  DvtPlayable.appendOnEnd(playable, this.DoEnd, this);
    if (lastPlayable)
      DvtPlayable.appendOnEnd(lastPlayable, this.DoEnd, this);
    
    this._bStarted = true;
    this._currIndex = 0;
    
    if (firstPlayable)
      firstPlayable.play();
  }
  else {
    // The onEnd listener should still be called.
    var timer = new DvtTimer(this.Context, 0, this.DoEnd, this, 1);
    timer.start();
  }
};

/**
  * @protected
  * Called after each playable, except the last one, ends.
  */
DvtSequentialPlayable.prototype.DoSequenceStep = function()
{
  var playable = this._arPlayables[++this._currIndex];
  if (playable instanceof DvtPlayable)
  {
    playable.play();
  }
  else
  {
    this.DoSequenceStep();
  }
};

/**
  * @override
  */
DvtSequentialPlayable.prototype.stop = function(bJumpToEnd)
{
  if (this._arPlayables)
  {
    var playable;
    for (var i = this._currIndex; i < this._arPlayables.length; i++)
    {
      playable = this._arPlayables[i];
      if (playable && playable instanceof DvtPlayable)
      {
        playable.stop(bJumpToEnd);
      }
    }
  }
};

/**
  * @override
  */
DvtSequentialPlayable.prototype.pause = function()
{
  if (this._arPlayables)
  {
    var playable = this._arPlayables[this._currIndex];
    if (playable instanceof DvtPlayable)
    {
      playable.pause();
    }
  }
};

/**
  * @protected
  * Called after the last playable has finished.
  */
DvtSequentialPlayable.prototype.DoEnd = function()
{
  if (this._onEnd)
  {
    this._onEnd.call(this._onEndObj);
  }
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class defining easing functions for animations.
  * @extends DvtObj
  * @class DvtEasing
  * @constructor
  */
var DvtEasing = function()
{
  this.Init();
};

DvtObj.createSubclass(DvtEasing, DvtObj, "DvtEasing");

/**
  * Linear easing function.
  * Returns the percent progress of the animation after applying the easing function.
  * @param {number}  progress  percent progress of the animation
  * @type number
  */
DvtEasing.linear = function(progress)
{
  return progress;
};

/**
  * Quadratic easing function that starts slow and speeds up at the beginning.
  * Returns the percent progress of the animation after applying the easing function.
  * @param {number}  progress  percent progress of the animation
  * @type number
  */
DvtEasing.quadraticIn = function(progress)
{
  return DvtEasing.PolyIn(progress, 2);
};

/**
  * Quadratic easing function that starts fast and slows down at the end.
  * Returns the percent progress of the animation after applying the easing function.
  * @param {number}  progress  percent progress of the animation
  * @type number
  */
DvtEasing.quadraticOut = function(progress)
{
  return DvtEasing.PolyOut(progress, 2);
};

/**
  * Quadratic easing function that starts slow, speeds up, and then slows down at the end.
  * Returns the percent progress of the animation after applying the easing function.
  * @param {number}  progress  percent progress of the animation
  * @type number
  */
DvtEasing.quadraticInOut = function(progress)
{
  return DvtEasing.PolyInOut(progress, 2);
};

/**
  * Cubic easing function that starts slow and speeds up at the beginning.
  * Returns the percent progress of the animation after applying the easing function.
  * @param {number}  progress  percent progress of the animation
  * @type number
  */
DvtEasing.cubicIn = function(progress)
{
  return DvtEasing.PolyIn(progress, 3);
};

/**
  * Cubic easing function that starts fast and slows down at the end.
  * Returns the percent progress of the animation after applying the easing function.
  * @param {number}  progress  percent progress of the animation
  * @type number
  */
DvtEasing.cubicOut = function(progress)
{
  return DvtEasing.PolyOut(progress, 3);
};

/**
  * Cubic easing function that starts slow, speeds up, and then slows down at the end.
  * Returns the percent progress of the animation after applying the easing function.
  * @param {number}  progress  percent progress of the animation
  * @type number
  */
DvtEasing.cubicInOut = function(progress)
{
  return DvtEasing.PolyInOut(progress, 3);
};

/**
  * Cubic easing function that starts fast and slows down at the end, 
  * overshooting the target and then coming back.
  * Returns the percent progress of the animation after applying the easing function.
  * @param {number}  progress  percent progress of the animation
  * @param {number}  overshoot  value to overshoot, with higher values 
  *        overshooting more (value of 0 means no overshoot, 
  *        default value of 1.7 overshoots by about 10%)
  * @type number
  */
DvtEasing.backOut = function(progress, overshoot)
{
  //t = progress, s = overshoot
  //(s+1)*t^3 - s*t^2
  if (!overshoot)
  {
    overshoot = 1.70158;
  }
  progress = 1 - progress;
  return 1 - progress*progress*((overshoot+1)*progress - overshoot);
};

/**
  * Cubic easing function that starts slow and speeds up at the beginning, 
  * overshooting the starting value in the opposite direction and then 
  * proceeding forward.
  * Returns the percent progress of the animation after applying the easing function.
  * @param {number}  progress  percent progress of the animation
  * @param {number}  overshoot  value to overshoot, with higher values 
  *        overshooting more (value of 0 means no overshoot, 
  *        default value of 1.7 overshoots by about 10%)
  * @type number
  */
DvtEasing.backIn = function(progress, overshoot)
{
  //t = progress, s = overshoot
  //(s+1)*t^3 - s*t^2
  if (!overshoot)
  {
    overshoot = 1.70158;
  }
  return progress*progress*((overshoot+1)*progress - overshoot);
};
	
/**
  * Easing function that oscillates at the start with an exponentially 
  * decaying sine wave.
  *
  * @param {number}  progress  percent progress of the animation
  * @param {number}  amplitude  amplitude of the sine wave
  * @param {number}  period  period of the sine wave
  * @type number
  */
DvtEasing.elasticIn = function(progress, amplitude, period)
{
  if (progress <= 0 || progress >= 1)
  {
    return progress;
  }
  if (!period)
  {
    period = 0.45;
  }
  var s;
  if (!amplitude || amplitude < 1)
  {
    amplitude = 1; 
    s = period / 4;
  }
  else
  {
    s = period / (2 * Math.PI) * Math.asin(1 / amplitude);
  }
  return -(amplitude*Math.pow(2,10*(progress-=1)) * Math.sin( (progress-s)*(2*Math.PI)/period ));
};
		
/**
  * Easing function that oscillates at the end with an exponentially 
  * decaying sine wave.
  *
  * @param {number}  progress  percent progress of the animation
  * @param {number}  amplitude  amplitude of the sine wave
  * @param {number}  period  period of the sine wave
  * @type number
  */
DvtEasing.elasticOut = function(progress, amplitude, period) {
  return 1 - DvtEasing.elasticIn(1-progress, amplitude, period);
};

/**
  * @protected
  * Polynomial easing function that starts slow and speeds up at the beginning.
  * Returns the percent progress of the animation after applying the easing function.
  * @param {number}  progress  percent progress of the animation
  * @param {number}  exp  exponent of the polynomial
  * @type number
  */
DvtEasing.PolyIn = function(progress, exp)
{
  if (progress < 0)
  {
    return 0;
  }
  if (progress > 1)
  {
    return 1;
  }
  else
  {
    return Math.pow(progress, exp);
  }
};

/**
  * @protected
  * Polynomial easing function that starts fast and slows down at the end.
  * Returns the percent progress of the animation after applying the easing function.
  * @param {number}  progress  percent progress of the animation
  * @param {number}  exp  exponent of the polynomial
  * @type number
  */
DvtEasing.PolyOut = function(progress, exp)
{
  if (progress < 0)
  {
    return 0;
  }
  if (progress > 1)
  {
    return 1;
  }
  else
 {
    return 1 - Math.pow(1 - progress, exp);
  }
};

/**
  * @protected
  * Polynomial easing function that starts slow, speeds up, and then slows down at the end.
  * Returns the percent progress of the animation after applying the easing function.
  * @param {number}  progress  percent progress of the animation
  * @param {number}  exp  exponent of the polynomial
  * @type number
  */
DvtEasing.PolyInOut = function(progress, exp)
{
  if (progress < 0)
  {
    return 0;
  }
  if (progress > 1)
  {
    return 1;
  }
  if (progress < .5)
  {
    return .5 * Math.pow(2 * progress, exp);
  }
  else
  {
    return .5 * (2 - Math.pow(2 * (1 - progress), exp));
  }
};

/**
  * @protected
  */
DvtEasing.prototype.Init = function()
{
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class defining interpolation functions for animations.
  * @extends DvtObj
  * @class DvtInterpolator
  * @constructor
  */
var DvtInterpolator = function()
{
  this.Init();
};

DvtObj.createSubclass(DvtInterpolator, DvtObj, "DvtInterpolator");

/**
  * Add an interpolator function to use for a given type of value.
  * @param {string}  type  type of value to use interpolator for
  * @param {function}  func  interpolator function
  */
DvtInterpolator.addInterpolator = function(type, func) {
  DvtInterpolator._map[type] = func;
};

/**
  * Remove an interpolator function.
  * @param {string}  type  type of value to remove interpolator for
  */
DvtInterpolator.removeInterpolator = function(type) {
  delete DvtInterpolator._map[type];
};

/**
  * Interpolate a value between the original and destination values for the 
  * given percent progress.
  * @param {DvtContext}  context  platform specific context object
  * @param {string}  type  type of value being interpolated
  * @param  origVal  original property value
  * @param  destVal  destination property value
  * @param {number}  progress  percent progress to interpolate
  */
DvtInterpolator.interpolate = function(context, type, origVal, destVal, progress)
{
  //don't pin progress, so that we can do elastic type easing functions,
  //like backIn and backOut for popIn and popOut anims
  /*if (progress <= 0)
  {
    return origVal;
  }
  
  if (progress >= 1)
  {
    return destVal;
  }*/
  
  var interpolatorFunc = DvtInterpolator._getInterpolator(type);
  if (interpolatorFunc) {
    return interpolatorFunc(context, origVal, destVal, progress);
  }
  
  return destVal;
};

/**
  * @private
  * Get the interpolator to use for the given type of value.
  * @param {string}  type  type of value to interpolate
  * @type function
  */
DvtInterpolator._getInterpolator = function(type) {
  return DvtInterpolator._map[type];
};

/**
  * Interpolate a number between the original and destination values for the 
  * given percent progress.
  * @param {DvtContext}  context  platform specific context object
  * @param {number}  origVal  original property value
  * @param {number}  destVal  destination property value
  * @param {number}  progress  percent progress to interpolate
  * @type number
  * @protected
  */
DvtInterpolator.InterpolateNumber = function(context, origVal, destVal, progress)
{
  //return (origVal + progress * (destVal - origVal));
  return DvtMath.interpolateNumber(origVal, destVal, progress);
};

/**
  * Interpolate an array of numbers between the original and destination 
  * values for the given percent progress.
  * @param {DvtContext}  context  platform specific context object
  * @param {number}  origVal  original property value
  * @param {number}  destVal  destination property value
  * @param {number}  progress  percent progress to interpolate
  * @type array
  * @protected
  */
DvtInterpolator.InterpolateNumberArray = function(context, origVal, destVal, progress)
{
  var origLength = origVal.length;
  var destLength = destVal.length;
  var array = [];
  for (var i = 0; i < Math.max(origLength, destLength); i++)
  {
    //if one array is shorter than the other, use the last actual value
    //from the shorter array to interpolate with all the extra elements
    //in the longer array
    var n1;
    var n2;
    if (i < origLength)
    {
      n1 = origVal[i];
    }
    else
    {
      n1 = origVal[origLength - 1];
    }
    if (i < destLength)
    {
      n2 = destVal[i];
    }
    else
    {
      n2 = destVal[destLength - 1];
    }
    array.push(DvtInterpolator.InterpolateNumber(context, n1, n2, progress));
  }
  return array;
};

/**
  * Interpolate a matrix between the original and destination values for the 
  * given percent progress.
  * @param {DvtContext}  context  platform specific context object
  * @param {DvtMatrix}  origVal  original property value
  * @param {DvtMatrix}  destVal  destination property value
  * @param {number}  progress  percent progress to interpolate
  * @type DvtMatrix
  * @protected
  */
DvtInterpolator.InterpolateMatrix = function(context, origVal, destVal, progress)
{
  var newA = DvtInterpolator.InterpolateNumber(context, origVal.getA(), destVal.getA(), progress);
  var newB = DvtInterpolator.InterpolateNumber(context, origVal.getB(), destVal.getB(), progress);
  var newC = DvtInterpolator.InterpolateNumber(context, origVal.getC(), destVal.getC(), progress);
  var newD = DvtInterpolator.InterpolateNumber(context, origVal.getD(), destVal.getD(), progress);
  var newTx = DvtInterpolator.InterpolateNumber(context, origVal.getTx(), destVal.getTx(), progress);
  var newTy = DvtInterpolator.InterpolateNumber(context, origVal.getTy(), destVal.getTy(), progress);
  
  return new DvtMatrix(newA, newB, newC, newD, newTx, newTy);
};

/**
  * Interpolate a color between the original and destination values for the 
  * given percent progress.
  * @param {DvtContext}  context  platform specific context object
  * @param {number}  origVal  original property value
  * @param {number}  destVal  destination property value
  * @param {number}  progress  percent progress to interpolate
  * @protected
  */
DvtInterpolator.InterpolateColor = function(context, origVal, destVal, progress)
{
  return DvtColorUtils.interpolateColor(origVal, destVal, progress);
};

/**
  * Interpolate an array of colors between the original and destination 
  * values for the given percent progress.
  * @param {DvtContext}  context  platform specific context object
  * @param {number}  origVal  original property value
  * @param {number}  destVal  destination property value
  * @param {number}  progress  percent progress to interpolate
  * @type array
  * @protected
  */
DvtInterpolator.InterpolateColorArray = function(context, origVal, destVal, progress)
{
  var origLength = origVal.length;
  var destLength = destVal.length;
  var array = [];
  for (var i = 0; i < Math.max(origLength, destLength); i++)
  {
    //if one array is shorter than the other, use the last actual value
    //from the shorter array to interpolate with all the extra elements
    //in the longer array
    var n1;
    var n2;
    if (i < origLength)
    {
      n1 = origVal[i];
    }
    else
    {
      n1 = origVal[origLength - 1];
    }
    if (i < destLength)
    {
      n2 = destVal[i];
    }
    else
    {
      n2 = destVal[destLength - 1];
    }
    array.push(DvtInterpolator.InterpolateColor(context, n1, n2, progress));
  }
  return array;
};

/**
  * Interpolate an array of polyline points between the original and 
  * destination values for the given percent progress.
  * @param {DvtContext}  context  platform specific context object
  * @param {number}  origVal  original property value
  * @param {number}  destVal  destination property value
  * @param {number}  progress  percent progress to interpolate
  * @type array
  * @protected
  */
DvtInterpolator.InterpolateGrowPolyline = function(context, origVal, destVal, progress)
{
  if (progress === 0)
  {
    return [destVal[0], destVal[1]];
  }
  else if (progress === 1)
  {
    return destVal;
  }
  
  var destLength = destVal.length;
  var array = [destVal[0], destVal[1]];
  var totalLength = DvtInterpolator.CalcPolylineLength(destVal);
  var partialLength = progress * totalLength;
  var accumLength = 0;
  var currSegLength;
  for (var i = 2; i < destLength - 1; i+=2)
  {
    var x1 = destVal[i-2];
    var y1 = destVal[i-1];
    var x2 = destVal[i];
    var y2 = destVal[i+1];
    
    currSegLength = DvtInterpolator.CalcPolylineLength([x1, y1, x2, y2]);
    
    if (accumLength + currSegLength > partialLength)
    {
      var ratio = (partialLength - accumLength) / currSegLength;
      var diffX = x2 - x1;
      var diffY = y2 - y1;
      array.push(x1 + ratio * diffX);
      array.push(y1 + ratio * diffY);
      break;
    }
    else
    {
      accumLength += currSegLength;
      
      array.push(x2);
      array.push(y2);
    }
  }
  return array;
};

/**
  * Interpolate an array of path commands between the original and 
  * destination values for the given percent progress.
  * @param {DvtContext}  context  platform specific context object
  * @param {number}  origVal  original property value
  * @param {number}  destVal  destination property value
  * @param {number}  progress  percent progress to interpolate
  * @type array
  * @protected
  */
DvtInterpolator.InterpolateGrowPath = function(context, origVal, destVal, progress)
{
  if (progress === 0)
  {
    return [destVal[0], destVal[1]];
  }
  else if (progress === 1)
  {
    return destVal;
  }
  
  var destLength = destVal.length;
  var array = [];
  var totalLength = DvtInterpolator.CalcPathLength(destVal);
  var partialLength = progress * totalLength;
  var accumLength = 0;
  var currSegLength;
  var x1;
  var y1;
  var cpx1;
  var cpy1;
  var cpx2;
  var cpy2;
  var prevX;
  var prevY;
  for (var i = 0; i < destLength; i++)
  {
    var cmd = destVal[i];
    var segArray;
    switch (cmd)
    {
      case "M":
        x1 = destVal[++i];
        y1 = destVal[++i];
        prevX = x1;
        prevY = y1;
        segArray = [prevX, prevY, prevX, prevY];
        break;
      case "L":
        x1 = destVal[++i];
        y1 = destVal[++i];
        segArray = [prevX, prevY, x1, y1];
        break;
      case "Q":
        cpx1 = destVal[++i];
        cpy1 = destVal[++i];
        x1 = destVal[++i];
        y1 = destVal[++i];
        segArray = [prevX, prevY, x1, y1];
        break;
      case "C":
        cpx1 = destVal[++i];
        cpy1 = destVal[++i];
        cpx2 = destVal[++i];
        cpy2 = destVal[++i];
        x1 = destVal[++i];
        y1 = destVal[++i];
        segArray = [prevX, prevY, x1, y1];
        break;
    }
    
    currSegLength = DvtInterpolator.CalcPolylineLength(segArray);
    
    if (accumLength + currSegLength > partialLength)
    {
      var ratio = (partialLength - accumLength) / currSegLength;
      
      array.push(cmd);
      switch (cmd)
      {
        case "Q":
          array.push(prevX + ratio * (cpx1 - prevX), 
                     prevY + ratio * (cpy1 - prevY));
          break;
        case "C":
          array.push(prevX + ratio * (cpx1 - prevX), 
                     prevY + ratio * (cpy1 - prevY), 
                     prevX + ratio * (cpx2 - prevX), 
                     prevY + ratio * (cpy2 - prevY));
          break;
      }
      array.push(prevX + ratio * (x1 - prevX), 
                 prevY + ratio * (y1 - prevY));
      break;
    }
    else
    {
      accumLength += currSegLength;
      
      array.push(cmd);
      switch (cmd)
      {
        case "Q":
          array.push(cpx1, cpy1);
          break;
        case "C":
          array.push(cpx1, cpy1, cpx2, cpy2);
          break;
      }
      array.push(x1);
      array.push(y1);
    }
    prevX = x1;
    prevY = y1;
  }
  return array;
};

DvtInterpolator.CalcPolylineLength = function(arPoints)
{
  var length = 0;
  var oldX;
  var oldY;
  var xx;
  var yy;
  for (var i = 0; i < arPoints.length; i+=2)
  {
    xx = arPoints[i];
    yy = arPoints[i+1];
    
    if (i > 0)
    {
      if (xx === oldX)
      {
        length += (Math.abs(yy - oldY));
      }
      else if (yy === oldY)
      {
        length += (Math.abs(xx - oldX));
      }
      else
      {
        var diffX = Math.abs(xx - oldX);
        var diffY = Math.abs(yy - oldY);
        length += Math.sqrt((diffX * diffX) + (diffY * diffY));
      }
    }
    
    oldX = xx;
    oldY = yy;
  }
  return length;
};

DvtInterpolator.CalcPathLength = function(arPoints)
{
  var length = 0;
  var oldX;
  var oldY;
  var xx;
  var yy;
  var cmd;
  for (var i = 0; i < arPoints.length; )
  {
    cmd = arPoints[i];
    switch (cmd)
    {
      case "M":
        xx = arPoints[i+1];
        yy = arPoints[i+2];
        oldX = xx;
        oldY = yy;
        i += 3;
        continue;
        break;
      case "L":
        xx = arPoints[i+1];
        yy = arPoints[i+2];
        i += 3;
        break;
      case "Q":
        xx = arPoints[i+3];
        yy = arPoints[i+4];
        i += 5;
        break;
      case "Q":
        xx = arPoints[i+5];
        yy = arPoints[i+6];
        i += 7;
        break;
    }
    
    if (i > 0)
    {
      if (xx === oldX)
      {
        length += (Math.abs(yy - oldY));
      }
      else if (yy === oldY)
      {
        length += (Math.abs(xx - oldX));
      }
      else
      {
        var diffX = Math.abs(xx - oldX);
        var diffY = Math.abs(yy - oldY);
        length += Math.sqrt((diffX * diffX) + (diffY * diffY));
      }
    }
    
    oldX = xx;
    oldY = yy;
  }
  return length;
};

/**
  * Interpolate a rectangle between the original and destination values for
  * the given percent progress.
  * @param {DvtContext}  context  platform specific context object
  * @param {DvtRectangle}  origVal  original property value
  * @param {DvtRectangle}  destVal  destination property value
  * @param {number}  progress  percent progress to interpolate
  * @type DvtRectangle
  * @protected
  */
DvtInterpolator.InterpolateRectangle = function(context, origVal, destVal, progress)
{
  var newX = DvtInterpolator.InterpolateNumber(context, origVal.x, destVal.x, progress);
  var newY = DvtInterpolator.InterpolateNumber(context, origVal.y, destVal.y, progress);
  var newW = DvtInterpolator.InterpolateNumber(context, origVal.w, destVal.w, progress);
  var newH = DvtInterpolator.InterpolateNumber(context, origVal.h, destVal.h, progress);
  
  return new DvtRectangle(newX, newY, newW, newH);
};

/**
  * Interpolate a point between the original and destination values for
  * the given percent progress.
  * @param {DvtContext}  context  platform specific context object
  * @param {DvtPoint}  origVal  original property value
  * @param {DvtPoint}  destVal  destination property value
  * @param {number}  progress  percent progress to interpolate
  * @type DvtPoint
  * @protected
  */
DvtInterpolator.InterpolatePoint = function(context, origVal, destVal, progress)
{
  var newX = DvtInterpolator.InterpolateNumber(context, origVal.x, destVal.x, progress);
  var newY = DvtInterpolator.InterpolateNumber(context, origVal.y, destVal.y, progress);
  
  return new DvtPoint(newX, newY);
};

/**
  * Interpolate a path between the original and destination 
  * values for the given percent progress.
  * @param {DvtContext}  context  platform specific context object
  * @param {number}  origVal  original property value
  * @param {number}  destVal  destination property value
  * @param {number}  progress  percent progress to interpolate
  * @type array
  * @protected
  */
DvtInterpolator.InterpolatePath = function(context, origVal, destVal, progress)
{
  var origLength = origVal.length;
  var destLength = destVal.length;
  //TO DO:
  //for now, if the paths are not of equal length, just return the dest val
  if (origLength != destLength)
  {
    return destVal;
  }
  
  var array = [];
  var i = 0;
  var j = 0;
  for (; i < origLength && j < destLength; )
  {
    var n1 = origVal[i];
    var n2 = destVal[j];
    var bNumberN1 = !isNaN(n1);
    var bNumberN2 = !isNaN(n2);
    if (!bNumberN1 && !bNumberN2)
    {
      if (n1 === n2)
      {
        array.push(n1);
      }
      //TO DO: handle case where commands are different
    }
    else if (bNumberN1 && bNumberN2)
    {
      array.push(DvtInterpolator.InterpolateNumber(context, n1, n2, progress));
    }
    i++;
    j++;
  }
  return array;
};

/**
  * Interpolate a fill between the original and destination values for the 
  * given percent progress.
  * @param {DvtContext}  context  platform specific context object
  * @param {number}  origVal  original property value
  * @param {number}  destVal  destination property value
  * @param {number}  progress  percent progress to interpolate
  * @protected
  */
DvtInterpolator.InterpolateFill = function(context, origVal, destVal, progress)
{
  if (origVal instanceof DvtSolidFill && destVal instanceof DvtSolidFill) {
    var color = DvtInterpolator.InterpolateColor(context, origVal.getColor(), destVal.getColor(), progress);
    var alpha = DvtInterpolator.InterpolateNumber(context, origVal.getAlpha(), destVal.getAlpha(), progress);
    var rgb = DvtColorUtils.makeRGB(DvtColorUtils.getRed(color),
                                    DvtColorUtils.getGreen(color),
                                    DvtColorUtils.getBlue(color));
    return new DvtSolidFill(rgb, alpha);
  }
  
  return destVal;
};

/**
  * Interpolate a stroke between the original and destination values for the 
  * given percent progress.
  * @param {DvtContext}  context  platform specific context object
  * @param {number}  origVal  original property value
  * @param {number}  destVal  destination property value
  * @param {number}  progress  percent progress to interpolate
  * @protected
  */
DvtInterpolator.InterpolateStroke = function(context, origVal, destVal, progress)
{
  if (origVal instanceof DvtSolidStroke && destVal instanceof DvtSolidStroke) {
    var color = DvtInterpolator.InterpolateColor(context, origVal.getColor(), destVal.getColor(), progress);
    var alpha = DvtInterpolator.InterpolateNumber(context, origVal.getAlpha(), destVal.getAlpha(), progress);
    var width = DvtInterpolator.InterpolateNumber(context, origVal.getWidth(), destVal.getWidth(), progress);
    var rgb = DvtColorUtils.makeRGB(DvtColorUtils.getRed(color),
                                    DvtColorUtils.getGreen(color),
                                    DvtColorUtils.getBlue(color));
    //clone the destVal because it may have other properties set that
    //we can't interpolate between
    var stroke = destVal.clone();
    stroke.setColor(rgb);
    stroke.setAlpha(alpha);
    stroke.setWidth(width);
    return stroke;
  }
  
  return destVal;
};

/**
  * Interpolate an array of polyline coordinates between the original and 
  * destination values for the given percent progress.
  * @param {DvtContext}  context  platform specific context object
  * @param {number}  origVal  original property value
  * @param {number}  destVal  destination property value
  * @param {number}  progress  percent progress to interpolate
  * @type array
  * @protected
  */
DvtInterpolator.InterpolatePolyline = function(context, origVal, destVal, progress)
{
  var origLength = origVal.length;
  var destLength = destVal.length;
  var array = [];
  for (var i = 0; i < Math.max(origLength, destLength); i++)
  {
    //if one array is shorter than the other, use the last actual coords
    //from the shorter array to interpolate with all the extra coords
    //in the longer array
    //BUG FIX #13376738: use the last coordinate pair, not just the last 
    //element in the array
    var endOffset = 2;
    if (i % 2 == 1) {
      endOffset = 1;
    }
    var n1;
    var n2;
    if (i < origLength)
    {
      n1 = origVal[i];
    }
    else
    {
      n1 = origVal[origLength - endOffset];
    }
    if (i < destLength)
    {
      n2 = destVal[i];
    }
    else
    {
      n2 = destVal[destLength - endOffset];
    }
    array.push(DvtInterpolator.InterpolateNumber(context, n1, n2, progress));
  }
  
  //trim the array to remove redundant coords at the end
  if (destLength < origLength) {
    var arLength = array.length;
    var lastX = array[arLength - 2];
    var lastY = array[arLength - 1];
    for (var i = arLength - 4; i >= 0; i-=2) {
      var currX = array[i];
      var currY = array[i+1];
      if (currX == lastX && currY == lastY) {
        array.splice(i, 2);
      }
      else {
        break;
      }
    }
  }
  
  return array;
};

/**
  * @protected
  */
DvtInterpolator.prototype.Init = function()
{
};

//need to define the map after the functions are defined above
/**
  * @private
  */
DvtInterpolator._map = {};
DvtInterpolator._map[DvtAnimator.TYPE_NUMBER] = DvtInterpolator.InterpolateNumber;
DvtInterpolator._map[DvtAnimator.TYPE_MATRIX] = DvtInterpolator.InterpolateMatrix;
DvtInterpolator._map[DvtAnimator.TYPE_NUMBER_ARRAY] = DvtInterpolator.InterpolateNumberArray;
DvtInterpolator._map[DvtAnimator.TYPE_COLOR] = DvtInterpolator.InterpolateColor;
DvtInterpolator._map[DvtAnimator.TYPE_COLOR_ARRAY] = DvtInterpolator.InterpolateColorArray;
DvtInterpolator._map[DvtAnimator.TYPE_GROW_POLYLINE] = DvtInterpolator.InterpolateGrowPolyline;
DvtInterpolator._map[DvtAnimator.TYPE_RECTANGLE] = DvtInterpolator.InterpolateRectangle;
DvtInterpolator._map[DvtAnimator.TYPE_POINT] = DvtInterpolator.InterpolatePoint;
DvtInterpolator._map[DvtAnimator.TYPE_PATH] = DvtInterpolator.InterpolatePath;
DvtInterpolator._map[DvtAnimator.TYPE_GROW_PATH] = DvtInterpolator.InterpolateGrowPath;
DvtInterpolator._map[DvtAnimator.TYPE_FILL] = DvtInterpolator.InterpolateFill;
DvtInterpolator._map[DvtAnimator.TYPE_STROKE] = DvtInterpolator.InterpolateStroke;
DvtInterpolator._map[DvtAnimator.TYPE_POLYLINE] = DvtInterpolator.InterpolatePolyline
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Abstract class representing a timer.
  * @extends DvtObj
  * @class DvtTimer
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param {number}  interval  interval between timer ticks, in milliseconds
  * @param {function}  callback  function to call for each timer tick
  * @param {object}  callbackObj  optional object instance that callback function is defined on
  * @param {number}  repeatCount  optional number of times for timer to repeat;
  *        timer repeats indefinitely if 0 or undefined
  */
var DvtTimer = function(context, interval, callback, callbackObj, repeatCount)
{
  this.Init(context, interval, callback, callbackObj, repeatCount);
};

DvtObj.createSubclass(DvtTimer, DvtObj, "DvtTimer");

/**
  * @protected
  */
DvtTimer.prototype.Init = function(context, interval, callback, callbackObj, repeatCount)
{
  if (!this._impl)
  {
    this._impl = context.getImplFactory().newTimer();
  }
  
  this._interval = interval; //in ms
  this._callback = callback;
  if (callbackObj)
  {
    this._callbackObj = callbackObj;
  }
  this._repeatCount = repeatCount;
  this._bRunning = false;
  this._numIterations = 0;
};

/**
  * Start this timer.
  */
DvtTimer.prototype.start = function()
{
  if (!this._bRunning)
  {
    this._bRunning = true;
    this.StartTimer();
  }
};

/**
  * Stop this timer.
  */
DvtTimer.prototype.stop = function()
{
  if (this._bRunning)
  {
    this._bRunning = false;
    this.StopTimer();
  }
};

/**
 * Reset this timer.
 */
DvtTimer.prototype.reset = function()
{
  if (this._bRunning) {
    this.stop();
  }
  
  this._numIterations = 0;
}

/**
  * Determine if this timer is running.
  * @type boolean
  */
DvtTimer.prototype.isRunning = function()
{
  return this._bRunning;
};

/**
  * @protected
  * Handle a timer tick.
  */
DvtTimer.prototype.HandleTimer = function()
{
  if (this._callback)
  {
    this._callback.call(this._callbackObj);
    
    if (this._repeatCount)
    {
      this._numIterations++;
      if (this._numIterations >= this._repeatCount)
      {
        this.stop();
      }
    }
  }
};

/**
  * Set the interval between timer ticks, in milliseconds.
  * @param {number}  interval  interval between timer ticks, in milliseconds
  */
DvtTimer.prototype.setInterval = function(interval)
{
  var i = interval;
  if (i < 0)
  {
    i = 0;
  }
  var oldBRunning = this._bRunning;
  if (oldBRunning)
  {
    this.stop();
  }
  this._interval = i;
  if (oldBRunning)
  {
    this.start();
  }
};

/**
  * Get the interval between timer ticks.
  * @type number
  */
DvtTimer.prototype.getInterval = function()
{
  return this._interval;
};

/**
  * @protected
  * Start the internal timer implementation.  
  * Called when the timer is stopped.
  */
DvtTimer.prototype.StartTimer = function()
{
  if (this._impl && this._impl.startTimer)
  {
    this._impl.startTimer(this.getInterval(), this.HandleTimer, this);
  }
};

/**
  * @protected
  * Stop the internal timer implementation.  
  * Called when the timer is started.
  */
DvtTimer.prototype.StopTimer = function()
{
  if (this._impl && this._impl.stopTimer)
  {
    this._impl.stopTimer();
  }
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Abstract base class representing an animation.
  * @extends DvtPlayable
  * @class DvtBaseAnimation
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtBaseAnimation = function(context, objs, duration, delay)
{
  this.Init(context, objs, duration, delay);
};

DvtObj.createSubclass(DvtBaseAnimation, DvtPlayable, "DvtBaseAnimation");

/**
  * Easing: linear.
  */
DvtBaseAnimation.EASING_LINEAR = 1;
/**
  * Easing: cubic in and out.
  */
DvtBaseAnimation.EASING_CUBIC_IN_OUT = 2;
/**
  * Easing: cubic in.
  */
DvtBaseAnimation.EASING_CUBIC_IN = 3;
/**
  * Easing: cubic out.
  */
DvtBaseAnimation.EASING_CUBIC_OUT = 4;
/**
  * Easing: quadratic in and out.
  */
DvtBaseAnimation.EASING_QUADRATIC_IN_OUT = 5;
/**
  * Easing: quadratic in.
  */
DvtBaseAnimation.EASING_QUADRATIC_IN = 6;
/**
  * Easing: quadratic out.
  */
DvtBaseAnimation.EASING_QUADRATIC_OUT = 7;
/**
  * Easing: bounce in.
  */
DvtBaseAnimation.EASING_BOUNCE_IN = 8;
/**
  * Easing: bounce out.
  */
DvtBaseAnimation.EASING_BOUNCE_OUT = 9;
/**
  * Easing: elastic in.
  */
DvtBaseAnimation.EASING_ELASTIC_IN = 10;
/**
  * Easing: elastic out.
  */
DvtBaseAnimation.EASING_ELASTIC_OUT = 11;

/**
  * Direction: center.
  */
DvtBaseAnimation.DIR_C = 1;
/**
  * Direction: north.
  */
DvtBaseAnimation.DIR_N = 2;
/**
  * Direction: northeast.
  */
DvtBaseAnimation.DIR_NE = 3;
/**
  * Direction: east.
  */
DvtBaseAnimation.DIR_E = 4;
/**
  * Direction: southeast.
  */
DvtBaseAnimation.DIR_SE = 5;
/**
  * Direction: south.
  */
DvtBaseAnimation.DIR_S = 6;
/**
  * Direction: southwest.
  */
DvtBaseAnimation.DIR_SW = 7;
/**
  * Direction: west.
  */
DvtBaseAnimation.DIR_W = 8;
/**
  * Direction: northwest.
  */
DvtBaseAnimation.DIR_NW = 9;

//axis constants
/**
 * Axis: x-axis
 */
DvtBaseAnimation.AXIS_X = 1;
/**
 * Axis: y-axis
 */
DvtBaseAnimation.AXIS_Y = 2;

//rotation directions
/**
 * Rotation direction: clockwise
 */
DvtBaseAnimation.ROT_DIR_CLOCKWISE = 1;
/**
 * Rotation direction: counter-clockwise
 */
DvtBaseAnimation.ROT_DIR_COUNTERCLOCKWISE = 2;

/**
  * @protected
  * Get the easing function corresponding to the given constant.
  * @param easing  constant representing the easing function
  * @type function
  */
DvtBaseAnimation.GetEasingFunction = function(easing)
{
  switch (easing)
  {
    case DvtBaseAnimation.EASING_LINEAR:
      return DvtEasing.linear;
    case DvtBaseAnimation.EASING_CUBIC_IN:
      return DvtEasing.cubicIn;
    case DvtBaseAnimation.EASING_CUBIC_OUT:
      return DvtEasing.cubicOut;
    case DvtBaseAnimation.EASING_QUADRATIC_IN_OUT:
      return DvtEasing.quadraticInOut;
    case DvtBaseAnimation.EASING_QUADRATIC_IN:
      return DvtEasing.quadraticIn;
    case DvtBaseAnimation.EASING_QUADRATIC_OUT:
      return DvtEasing.quadraticOut;
    case DvtBaseAnimation.EASING_BOUNCE_IN:
      return DvtEasing.backIn;
    case DvtBaseAnimation.EASING_BOUNCE_OUT:
      return DvtEasing.backOut;
    case DvtBaseAnimation.EASING_ELASTIC_IN:
      return DvtEasing.elasticIn;
    case DvtBaseAnimation.EASING_ELASTIC_OUT:
      return DvtEasing.elasticOut;
    case DvtBaseAnimation.EASING_CUBIC_IN_OUT:
    default:
      return DvtEasing.cubicInOut;
  }
};

/**
  * @protected
  * Get the point corresponding to the given compass direction
  * on the given bounding box.
  * @param {DvtRectangle}  boundsRect  bounding box
  * @param direction  constant representing the compass direction
  * @type DvtPoint
  */
DvtBaseAnimation.GetCompassPoint = function(boundsRect, direction)
{
  switch (direction)
  {
    case DvtBaseAnimation.DIR_N:
      return new DvtPoint(boundsRect.x + 0.5 * boundsRect.w, boundsRect.y);
    case DvtBaseAnimation.DIR_NE:
      return new DvtPoint(boundsRect.x + boundsRect.w,      boundsRect.y);
    case DvtBaseAnimation.DIR_E:
      return new DvtPoint(boundsRect.x + boundsRect.w,      boundsRect.y + 0.5 * boundsRect.h);
    case DvtBaseAnimation.DIR_SE:
      return new DvtPoint(boundsRect.x + boundsRect.w,      boundsRect.y + boundsRect.h);
    case DvtBaseAnimation.DIR_S:
      return new DvtPoint(boundsRect.x + 0.5 * boundsRect.w, boundsRect.y + boundsRect.h);
    case DvtBaseAnimation.DIR_SW:
      return new DvtPoint(boundsRect.x,                         boundsRect.y + boundsRect.h);
    case DvtBaseAnimation.DIR_W:
      return new DvtPoint(boundsRect.x,                         boundsRect.y + 0.5 * boundsRect.h);
    case DvtBaseAnimation.DIR_NW:
      return new DvtPoint(boundsRect.x,                         boundsRect.y);
    case DvtBaseAnimation.DIR_C:
    default:
      return new DvtPoint(boundsRect.x + 0.5 * boundsRect.w, boundsRect.y + 0.5 * boundsRect.h);
  }
};

/**
  * @protected
  * Get the point to position the given object rectangle so that it aligns to the 
  * boundary rectangle in the compass direction.
  * @param {DvtRectangle}  objRect  object bounding box
  * @param {DvtRectangle}  boundsRect  bounding box
  * @param direction  constant representing the compass direction
  * @type DvtPoint
  */
DvtBaseAnimation.GetAlignCompassPoint = function(objRect, boundsRect, direction)
{
  var compassPoint = DvtBaseAnimation.GetCompassPoint(boundsRect, direction);
  var point;
  switch (direction)
  {
    case DvtBaseAnimation.DIR_N:
      return new DvtPoint(compassPoint.x - 0.5 * objRect.w, compassPoint.y);
    case DvtBaseAnimation.DIR_NE:
      return new DvtPoint(compassPoint.x - objRect.w,      compassPoint.y);
    case DvtBaseAnimation.DIR_E:
      return new DvtPoint(compassPoint.x - objRect.w,      compassPoint.y - 0.5 * objRect.h);
    case DvtBaseAnimation.DIR_SE:
      return new DvtPoint(compassPoint.x - objRect.w,      compassPoint.y - objRect.h);
    case DvtBaseAnimation.DIR_S:
      return new DvtPoint(compassPoint.x - 0.5 * objRect.w, compassPoint.y - objRect.h);
    case DvtBaseAnimation.DIR_SW:
      return new DvtPoint(compassPoint.x,                      compassPoint.y - objRect.h);
    case DvtBaseAnimation.DIR_W:
      return new DvtPoint(compassPoint.x,                      compassPoint.y - 0.5 * objRect.h);
    case DvtBaseAnimation.DIR_NW:
      return new DvtPoint(compassPoint.x,                      compassPoint.y);
    case DvtBaseAnimation.DIR_C:
    default:
      return new DvtPoint(compassPoint.x - 0.5 * objRect.w, compassPoint.y - 0.5 * objRect.h);
  }
};

/**
  * @protected
  * @override
  */
DvtBaseAnimation.prototype.Init = function(context, objs, duration, delay)
{
  DvtBaseAnimation.superclass.Init.call(this, context);
  
  this._context = context;
  
  if (objs instanceof Array)
  {
    this._arObjects = objs;
  }
  else
  {
    this._arObjects = [objs];
  }
  
  this._duration = (duration ? duration : .5);
  this._delay = (delay ? delay : 0);
  this._easing = DvtBaseAnimation.EASING_CUBIC_IN_OUT;
  this._bInitialized = false;
  
  this._bSaveAndRestoreOriginalMatrices = false;
  this._origMatrixArray = null;
  this._bHideObjectsOnEnd = false;
  
  this.CreateAnimator(context);
};

/**
  * @protected
  * Get the platform dependent context object.
  * @type DvtContext
  */
DvtBaseAnimation.prototype.GetContext = function()
{
  return this._context;
};

/**
 * Determine if this animation is running.
 * @type boolean
 */
DvtBaseAnimation.prototype.isRunning = function()
{
  if (this._animator) {
    return this._animator.isRunning();
  }
  return false;
};

/**
  * @override
  */
DvtBaseAnimation.prototype.play = function()
{
  if (!this._bInitialized)
  {
    //initialize the desired end results of the animation before the start
    //states because the end results may depend on the object states
    //before the start states are applied
    this.InitializeEndStates(this._arObjects);
    //initialize the display objects appropriately for the start of the animation
    this.InitializeStartStates(this._arObjects);
    
    this.InitializePlay();
    
    this._bInitialized = true;
  }
  
  if (this._animator)
    this._animator.play();
};

/**
  * @override
  */
DvtBaseAnimation.prototype.stop = function(bJumpToEnd)
{
  if (this._animator)
    this._animator.stop(bJumpToEnd);
};

/**
  * @override
  */
DvtBaseAnimation.prototype.pause = function()
{
  if (this._animator)
    this._animator.pause();
};

/**
  * Get the duration of this animation, in seconds.
  * @type number
  */
DvtBaseAnimation.prototype.getDuration = function()
{
  return this._duration;
};

/**
  * Set the duration of this animation.
  * @param {number}  duration  duration of this animation, in seconds
  */
DvtBaseAnimation.prototype.setDuration = function(duration)
{
  this._duration = (duration ? duration : 0);
  
  if (this._animator)
    this._animator.setDuration(this._duration);
};

/**
  * Get the delay for the start of this animation, in seconds.
  * @type number
  */
DvtBaseAnimation.prototype.getDelay = function()
{
  return this._delay;
};

/**
  * Set the delay for the start of this animation.
  * @param {number}  delay  delay for the start of this animation, in seconds
  */
DvtBaseAnimation.prototype.setDelay = function(delay)
{
  this._delay = (delay ? delay : 0);
  
  if (this._animator)
    this._animator.setDelay(this._delay);
};

/**
  * Get the easing function for this animation.
  * Returns one of the EASING_ constants defined on DvtBaseAnimation.
  */
DvtBaseAnimation.prototype.getEasing = function()
{
  return this._easing;
};

/**
  * Set the easing function for this animation.
  * @param  easing  one of the EASING_ constants defined on DvtBaseAnimation
  */
DvtBaseAnimation.prototype.setEasing = function(easing)
{
  this._easing = easing;
  
  if (this._animator)
    this._animator.setEasing(DvtBaseAnimation.GetEasingFunction(this._easing));
};

/**
  * @protected
  * Create the underlying animator used by this animation.
  * @param {DvtContext}  context  platform specific context object
  */
DvtBaseAnimation.prototype.CreateAnimator = function(context)
{
  this._animator = new DvtAnimator(context, this._duration, this._delay, DvtBaseAnimation.GetEasingFunction(this._easing));
  this._animator.setOnEnd(this.OnAnimEnd, this);
};

/**
  * @protected
  * Called when the underlying animator ends.
  */
DvtBaseAnimation.prototype.OnAnimEnd = function()
{
  //hide objects first
  if (this._bHideObjectsOnEnd)
  {
    this.HideObjects();
  }
  
  //restore original transforms after hiding objects
  if (this._bSaveAndRestoreOriginalMatrices)
  {
    this.RestoreOriginalMatrices();
  }
  
  //call external onEnd func after any internal cleanup
  if (this._onEnd)
  {
    this._onEnd.call(this._onEndObj);
  }
};

/**
  * @protected
  * Called when play is called.
  */
DvtBaseAnimation.prototype.InitializePlay = function()
{
  //for subclasses to implement
};

/**
  * @protected
  * Called when play is called to initialize the start states of
  * the objects being animated.
  * @param {array}  arObjects  array of objects being animated
  */
DvtBaseAnimation.prototype.InitializeStartStates = function(arObjects)
{
  if (arObjects)
  {
    var obj;
    for (var i = 0; i < arObjects.length; i++)
    {
      obj = arObjects[i];
      if (obj)
      {
        if (this._bSaveAndRestoreOriginalMatrices)
        {
          if (!this._origMatrixArray)
          {
            this._origMatrixArray = [];
          }
          
          this._origMatrixArray.push(obj.getMatrix());
        }
        
        this.InitStartState(obj);
      }
    }
  }
};

/**
  * @protected
  * Called when play is called to initialize the end states of
  * the objects being animated.
  * @param {array}  arObjects  array of objects being animated
  */
DvtBaseAnimation.prototype.InitializeEndStates = function(arObjects)
{
  if (arObjects)
  {
    var obj;
    for (var i = 0; i < arObjects.length; i++)
    {
      obj = arObjects[i];
      if (obj)
      {
        this.InitEndState(obj);
      }
    }
  }
};

/**
  * @protected
  * Initialize the start state of the given object being animated.
  * @param {object}  obj  object being animated
  */
DvtBaseAnimation.prototype.InitStartState = function(obj)
{
  //subclasses must implement
};

/**
  * @protected
  * Initialize the end state of the given object being animated.
  * @param {object}  obj  object being animated
  */
DvtBaseAnimation.prototype.InitEndState = function(obj)
{
  //subclasses must implement
};

/**
  * @protected
  * Set objects to be visible.  
  */
DvtBaseAnimation.prototype.ShowObjects = function()
{
  this.SetObjectsVisible(true);
};

/**
  * @protected
  * Set objects to be hidden.
  */
DvtBaseAnimation.prototype.HideObjects = function()
{
  this.SetObjectsVisible(false);
};

/**
 * @protected
 * Set visibility of objects.
 * 
 * @param {boolean}  bVisible  true to show, false to hide
 */
DvtBaseAnimation.prototype.SetObjectsVisible = function(bVisible)
{
  if (this._arObjects)
  {
    var obj;
    for (var i = this._arObjects.length - 1; i >= 0; i--)
    {
      obj = this._arObjects[i];
      if (obj)
      {
        obj.setVisible(bVisible);
      }
    }
  }
};

/**
  * @protected
  * Restore objects' original matrices as they were before animation.
  */
DvtBaseAnimation.prototype.RestoreOriginalMatrices = function()
{
  if (this._arObjects)
  {
    var obj;
    for (var i = this._arObjects.length - 1; i >= 0; i--)
    {
      obj = this._arObjects[i];
      if (obj)
      {
        if (this._origMatrixArray)
        {
          obj.setMatrix(this._origMatrixArray.pop());
        }
      }
    }
  }
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
 * Class providing the ability to create a custom animation using a DvtAnimator.
 * @param {DvtContext} context The platform specific context object.
 * @param {obj} obj The object to animate.
 * @param {number} duration The length of animation, in seconds.
 * @param {number} delay The time to delay start of animation, in seconds.
 * @extends DvtBaseAnimation
 * @class DvtSunburstAnimation
 * @constructor
 */
var DvtCustomAnimation = function(context, obj, duration, delay)
{
  this.Init(context, obj, duration, delay);
};

DvtObj.createSubclass(DvtCustomAnimation, DvtBaseAnimation, "DvtCustomAnimation");

/**
 * Returns the animator, which can be used to add animated properties.
 * @return {DvtAnimator} The animator for this animation.
 */
DvtCustomAnimation.prototype.getAnimator = function()
{
  return this._animator;
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to move an object into view as 
  * if on a conveyor belt.  The object will in from the background on 
  * its side and then rotate to face forward.
  * @extends DvtBaseAnimation
  * @class DvtAnimConveyorIn
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param direction compass direction to move display objects in from; 
  *        can be one of: DIR_N, DIR_E, DIR_S, DIR_W
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimConveyorIn = function(context, objs, direction, duration, delay)
{
  this.Init(context, objs, direction, duration, delay);
};

DvtObj.createSubclass(DvtAnimConveyorIn, DvtBaseAnimation, "DvtAnimConveyorIn");

/**
  * @protected
  * @override
  */
DvtAnimConveyorIn.prototype.Init = function(context, objs, direction, duration, delay)
{
  DvtAnimConveyorIn.superclass.Init.call(this, context, objs, duration, delay);
  
  this._direction = direction;
  
  this._SCALE = .2;
  this._SKEW_ANGLE = Math.PI / 12;
  
  this._bSaveAndRestoreOriginalMatrices = true;
};

/**
  * @protected
  * @override
  */
DvtAnimConveyorIn.prototype.CreateAnimator = function(context)
{
  DvtAnimConveyorIn.superclass.CreateAnimator.call(this, context);
  
  this.setEasing(DvtBaseAnimation.EASING_CUBIC_IN);
  this._animator.setDuration(this.getDuration() / 2);
  this._animator.setOnEnd(this.PlayEndAnim, this);
  
  this._endAnim = new DvtAnimator(context, this.getDuration() / 2, 0, DvtBaseAnimation.GetEasingFunction(DvtBaseAnimation.EASING_CUBIC_OUT));
  this._endAnim.setOnEnd(this.OnAnimEnd, this);
};

/**
  * @protected
  * @override
  */
DvtAnimConveyorIn.prototype.InitializePlay = function()
{
  DvtAnimConveyorIn.superclass.InitializePlay.call(this);
  
  this.ShowObjects();
};

/**
  * @protected
  * @override
  */
DvtAnimConveyorIn.prototype.InitStartState = function(obj)
{
  DvtAnimConveyorIn.superclass.InitStartState.call(this, obj);
  
  obj.setVisible(false);
  
  //rotate first
  var scale = this._SCALE;
  var diffScale = 1 - scale;
  
  var bounds = obj.getDimensions();
  var halfWidth = bounds.w * diffScale;
  var halfHeight = bounds.h * diffScale;
  
  var mat = this.GetConcatenatedRotationMatrix(obj, bounds);
  
  //slide into background after
  var mat2 = new DvtMatrix();
  if (this._direction === DvtBaseAnimation.DIR_W ||
      this._direction === DvtBaseAnimation.DIR_E)
  {
    if (this._direction === DvtBaseAnimation.DIR_W)
    {
      mat2.translate(bounds.w, 0);
    }
    else
    {
      mat2.translate(-bounds.w, 0);
    }
  }
  else
  {
    if (this._direction === DvtBaseAnimation.DIR_N)
    {
      mat2.translate(0, bounds.h);
    }
    else
    {
      mat2.translate(0, -bounds.h);
    }
  }
  mat2.concat(mat);
  obj.setMatrix(mat2);
  obj.setAlpha(0);
};

/**
  * @protected
  * @override
  */
DvtAnimConveyorIn.prototype.InitEndState = function(obj)
{
  DvtAnimConveyorIn.superclass.InitEndState.call(this, obj);
  
  var bounds = obj.getDimensions();
  
  //slide into foreground first
  var concatRotMat = this.GetConcatenatedRotationMatrix(obj, bounds);
  this._animator.addProp(DvtAnimator.TYPE_MATRIX, obj, obj.getMatrix, obj.setMatrix, concatRotMat);
  this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getAlpha, obj.setAlpha, 1);
  
  //rotate after
  var currMat = obj.getMatrix();
  this._endAnim.addProp(DvtAnimator.TYPE_MATRIX, obj, obj.getMatrix, obj.setMatrix, currMat);
};

/**
  * @protected
  * Play the end animation, which rotates the object to face forward again.
  */
DvtAnimConveyorIn.prototype.PlayEndAnim = function()
{
  this._endAnim.play();
};

/**
  * @protected
  * Get the concatenation of the rotation matrix with the current matrix.
  * 
  * @param  obj  display object being animated
  * @param {DvtRectangle}  bounds  bounding box for the object
  * @type DvtMatrix
  */
DvtAnimConveyorIn.prototype.GetConcatenatedRotationMatrix = function(obj, bounds)
{
  var scale = this._SCALE;
  var diffScale = 1 - scale;
  
  var halfWidth = bounds.w * diffScale;
  var halfHeight = bounds.h * diffScale;
  
  var skewAngle = this._SKEW_ANGLE;
  if (this._direction === DvtBaseAnimation.DIR_W ||
      this._direction === DvtBaseAnimation.DIR_S)
  {
    skewAngle = -skewAngle;
  }
  var tanAngle = Math.tan(skewAngle);
  
  var currMat = obj.getMatrix();
  var mat = new DvtMatrix();
  if (this._direction === DvtBaseAnimation.DIR_W ||
      this._direction === DvtBaseAnimation.DIR_E)
  {
    mat.skew(0, skewAngle);
    mat.scale(scale, 1);
    if (this._direction === DvtBaseAnimation.DIR_W)
    {
      mat.translate(0, 0);
    }
    else
    {
      mat.translate(halfWidth, -bounds.w * tanAngle);
    }
  }
  else
  {
    mat.skew(skewAngle, 0);
    mat.scale(1, scale);
    if (this._direction === DvtBaseAnimation.DIR_N)
    {
      mat.translate(0, 0);
    }
    else
    {
      mat.translate(-bounds.h * tanAngle, halfHeight);
    }
  }
  mat.concat(currMat);
  return mat;
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to move an object out of view as 
  * if on a conveyor belt.  The object will rotate onto its side and 
  * slide into the background.  
  * @extends DvtBaseAnimation
  * @class DvtAnimConveyorOut
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param direction compass direction to move display objects out; 
  *        can be one of: DIR_N, DIR_E, DIR_S, DIR_W
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimConveyorOut = function(context, objs, direction, duration, delay)
{
  this.Init(context, objs, direction, duration, delay);
};

DvtObj.createSubclass(DvtAnimConveyorOut, DvtBaseAnimation, "DvtAnimConveyorOut");

/**
  * @protected
  * @override
  */
DvtAnimConveyorOut.prototype.Init = function(context, objs, direction, duration, delay)
{
  DvtAnimConveyorOut.superclass.Init.call(this, context, objs, duration, delay);
  
  this._direction = direction;
  
  this._bSaveAndRestoreOriginalMatrices = true;
  this._bHideObjectsOnEnd = true;
};

/**
  * @protected
  * @override
  */
DvtAnimConveyorOut.prototype.CreateAnimator = function(context)
{
  DvtAnimConveyorOut.superclass.CreateAnimator.call(this, context);
  
  this.setEasing(DvtBaseAnimation.EASING_CUBIC_IN);
  this._animator.setDuration(this.getDuration() / 2);
  this._animator.setOnEnd(this.PlayEndAnim, this);
  
  this._endAnim = new DvtAnimator(context, this.getDuration() / 2, 0, DvtBaseAnimation.GetEasingFunction(DvtBaseAnimation.EASING_CUBIC_OUT));
  this._endAnim.setOnEnd(this.OnAnimEnd, this);
};

/**
  * @protected
  * @override
  */
DvtAnimConveyorOut.prototype.InitEndState = function(obj)
{
  DvtAnimConveyorOut.superclass.InitEndState.call(this, obj);
  
  //rotate first
  var scale = .2;
  var diffScale = 1 - scale;
  
  var bounds = obj.getDimensions();
  var halfWidth = bounds.w * diffScale;
  var halfHeight = bounds.h * diffScale;
  
  var skewAngle = Math.PI / 12;
  if (this._direction === DvtBaseAnimation.DIR_W ||
      this._direction === DvtBaseAnimation.DIR_S)
  {
    skewAngle = -skewAngle;
  }
  var tanAngle = Math.tan(skewAngle);
  
  var currMat = obj.getMatrix();
  var mat = new DvtMatrix();
  if (this._direction === DvtBaseAnimation.DIR_W ||
      this._direction === DvtBaseAnimation.DIR_E)
  {
    mat.skew(0, skewAngle);
    mat.scale(scale, 1);
    if (this._direction === DvtBaseAnimation.DIR_W)
    {
      mat.translate(0, 0);
    }
    else
    {
      mat.translate(halfWidth, -bounds.w * tanAngle);
    }
  }
  else
  {
    mat.skew(skewAngle, 0);
    mat.scale(1, scale);
    if (this._direction === DvtBaseAnimation.DIR_N)
    {
      mat.translate(0, 0);
    }
    else
    {
      mat.translate(-bounds.h * tanAngle, halfHeight);
    }
  }
  mat.concat(currMat);
  this._animator.addProp(DvtAnimator.TYPE_MATRIX, obj, obj.getMatrix, obj.setMatrix, mat);
  
  //slide into background after
  var mat2 = new DvtMatrix();
  if (this._direction === DvtBaseAnimation.DIR_W ||
      this._direction === DvtBaseAnimation.DIR_E)
  {
    if (this._direction === DvtBaseAnimation.DIR_W)
    {
      mat2.translate(bounds.w, 0);
    }
    else
    {
      mat2.translate(-bounds.w, 0);
    }
  }
  else
  {
    if (this._direction === DvtBaseAnimation.DIR_N)
    {
      mat2.translate(0, bounds.h);
    }
    else
    {
      mat2.translate(0, -bounds.h);
    }
  }
  mat2.concat(mat);
  this._endAnim.addProp(DvtAnimator.TYPE_MATRIX, obj, obj.getMatrix, obj.setMatrix, mat2);
  this._endAnim.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getAlpha, obj.setAlpha, 0);
};

/**
  * @protected
  * Play the end animation, which slides the object into the background.
  */
DvtAnimConveyorOut.prototype.PlayEndAnim = function()
{
  this._endAnim.play();
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to flip an object into view,
  * as if flipping a playing card.
  * @extends DvtBaseAnimation
  * @class DvtAnimFlipIn
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param axis axis of rotation; can be one of: AXIS_X, AXIS_Y
  * @param rotationDir direction of rotation about the axis; can be one of:
  *        ROT_DIR_CLOCKWISE, ROT_DIR_COUNTERCLOCKWISE;
  *        direction is determined by looking down the axis from the positive side
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimFlipIn = function(context, objs, axis, rotationDir, duration, delay)
{
  this.Init(context, objs, axis, rotationDir, duration, delay);
};

DvtObj.createSubclass(DvtAnimFlipIn, DvtBaseAnimation, "DvtAnimFlipIn");

/**
  * @protected
  * @override
  */
DvtAnimFlipIn.prototype.Init = function(context, objs, axis, rotationDir, duration, delay)
{
  DvtAnimFlipIn.superclass.Init.call(this, context, objs, duration, delay);
  
  this._axis = axis;
  this._rotationDir = rotationDir;
  
  this._bSaveAndRestoreOriginalMatrices = true;
  this._cubeEffect = false;
};

/**
  * @protected
  * @override
  */
DvtAnimFlipIn.prototype.InitStartState = function(obj)
{
  DvtAnimFlipIn.superclass.InitStartState.call(this, obj);
  
  obj.setVisible(false);
  
  var bounds = obj.getDimensions();
  var halfWidth = bounds.w / 2;
  var halfHeight = bounds.h / 2;
  
  var skewAngle = Math.PI / 6;
  if (this._rotationDir === DvtBaseAnimation.ROT_DIR_COUNTERCLOCKWISE)
  {
    skewAngle = -skewAngle;
  }
  var tanAngle = Math.tan(skewAngle);
  
  var currMat = obj.getMatrix();
  var mat = new DvtMatrix();
  if (this._axis === DvtBaseAnimation.AXIS_Y)
  {
    mat.skew(0, skewAngle);
    mat.scale(0, 1);
    if (this._cubeEffect)
    {
      if (this._rotationDir === DvtBaseAnimation.ROT_DIR_COUNTERCLOCKWISE)
      {
        mat.translate(bounds.w, 0);
      }
      else
      {
        mat.translate(0, -bounds.w * tanAngle);
      }
    }
    else
    {
      mat.translate(halfWidth, -halfWidth * tanAngle);
    }
  }
  else
  {
    mat.skew(skewAngle, 0);
    mat.scale(1, 0);
    if (this._cubeEffect)
    {
      if (this._rotationDir === DvtBaseAnimation.ROT_DIR_COUNTERCLOCKWISE)
      {
        mat.translate(-bounds.h *tanAngle, 0);
      }
      else
      {
        mat.translate(0, bounds.h);
      }
    }
    else
    {
      mat.translate(-halfHeight * tanAngle, halfHeight);
    }
  }
  mat.concat(currMat);
  obj.setMatrix(mat);
  
  if (this._cubeEffect)
  {
    obj.setAlpha(0);
  }
};

/**
  * @protected
  * @override
  */
DvtAnimFlipIn.prototype.InitEndState = function(obj)
{
  DvtAnimFlipIn.superclass.InitEndState.call(this, obj);
  
  var currMat = obj.getMatrix();
  this._animator.addProp(DvtAnimator.TYPE_MATRIX, obj, obj.getMatrix, obj.setMatrix, currMat);
  
  if (this._cubeEffect)
  {
    this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getAlpha, obj.setAlpha, 1);
  }
};

/**
  * @protected
  * @override
  */
DvtAnimFlipIn.prototype.InitializePlay = function()
{
  DvtAnimFlipIn.superclass.InitializePlay.call(this);
  
  this.ShowObjects();
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to flip an object out of view,
  * as if flipping a playing card.
  * @extends DvtBaseAnimation
  * @class DvtAnimFlipOut
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param axis axis of rotation; can be one of: AXIS_X, AXIS_Y
  * @param rotationDir direction of rotation about the axis; can be one of:
  *        ROT_DIR_CLOCKWISE, ROT_DIR_COUNTERCLOCKWISE;
  *        direction is determined by looking down the axis from the positive side
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimFlipOut = function(context, objs, axis, rotationDir, duration, delay)
{
  this.Init(context, objs, axis, rotationDir, duration, delay);
};

DvtObj.createSubclass(DvtAnimFlipOut, DvtBaseAnimation, "DvtAnimFlipOut");

/**
  * @protected
  * @override
  */
DvtAnimFlipOut.prototype.Init = function(context, objs, axis, rotationDir, duration, delay)
{
  DvtAnimFlipOut.superclass.Init.call(this, context, objs, duration, delay);
  
  this._axis = axis;
  this._rotationDir = rotationDir;
  
  this._bSaveAndRestoreOriginalMatrices = true;
  this._bHideObjectsOnEnd = true;
  this._cubeEffect = false;
};

/**
  * @protected
  * @override
  */
DvtAnimFlipOut.prototype.InitEndState = function(obj)
{
  DvtAnimFlipOut.superclass.InitEndState.call(this, obj);
  
  var bounds = obj.getDimensions();
  var halfWidth = bounds.w / 2;
  var halfHeight = bounds.h / 2;
  
  var skewAngle = Math.PI / 6;
  if (this._rotationDir === DvtBaseAnimation.ROT_DIR_CLOCKWISE)
  {
    skewAngle = -skewAngle;
  }
  var tanAngle = Math.tan(skewAngle);
  
  var currMat = obj.getMatrix();
  var mat = new DvtMatrix();
  if (this._axis === DvtBaseAnimation.AXIS_Y)
  {
    mat.skew(0, skewAngle);
    mat.scale(0, 1);
    if (this._cubeEffect)
    {
      if (this._rotationDir === DvtBaseAnimation.ROT_DIR_CLOCKWISE)
      {
        mat.translate(bounds.w, 0);
      }
      else
      {
        mat.translate(0, -bounds.w * tanAngle);
      }
    }
    else
    {
      mat.translate(halfWidth, -halfWidth * tanAngle);
    }
  }
  else
  {
    mat.skew(skewAngle, 0);
    mat.scale(1, 0);
    if (this._cubeEffect)
    {
      if (this._rotationDir === DvtBaseAnimation.ROT_DIR_CLOCKWISE)
      {
        mat.translate(-bounds.h *tanAngle, 0);
      }
      else
      {
        mat.translate(0, bounds.h);
      }
    }
    else
    {
      mat.translate(-halfHeight * tanAngle, halfHeight);
    }
  }
  mat.concat(currMat);
  this._animator.addProp(DvtAnimator.TYPE_MATRIX, obj, obj.getMatrix, obj.setMatrix, mat);
  
  if (this._cubeEffect)
  {
    this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getAlpha, obj.setAlpha, 0);
  }
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to rotate an object into view,
  * as if rotating a cube.
  * @extends DvtAnimFlipIn
  * @class DvtAnimCubeIn
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param axis axis of rotation; can be one of: AXIS_X, AXIS_Y
  * @param rotationDir direction of rotation about the axis; can be one of:
  *        ROT_DIR_CLOCKWISE, ROT_DIR_COUNTERCLOCKWISE;
  *        direction is determined by looking down the axis from the positive side
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimCubeIn = function(context, objs, axis, rotationDir, duration, delay)
{
  this.Init(context, objs, axis, rotationDir, duration, delay);
};

DvtObj.createSubclass(DvtAnimCubeIn, DvtAnimFlipIn, "DvtAnimCubeIn");

/**
  * @protected
  * @override
  */
DvtAnimCubeIn.prototype.Init = function(context, objs, axis, rotationDir, duration, delay)
{
  DvtAnimCubeIn.superclass.Init.call(this, context, objs, axis, rotationDir, duration, delay);
  
  this._cubeEffect = true;
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to rotate an object out of view,
  * as if rotating a cube.
  * @extends DvtAnimFlipOut
  * @class DvtAnimCubeOut
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param axis axis of rotation; can be one of: AXIS_X, AXIS_Y
  * @param rotationDir direction of rotation about the axis; can be one of:
  *        ROT_DIR_CLOCKWISE, ROT_DIR_COUNTERCLOCKWISE;
  *        direction is determined by looking down the axis from the positive side
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimCubeOut = function(context, objs, axis, rotationDir, duration, delay)
{
  this.Init(context, objs, axis, rotationDir, duration, delay);
};

DvtObj.createSubclass(DvtAnimCubeOut, DvtAnimFlipOut, "DvtAnimCubeOut");

/**
  * @protected
  * @override
  */
DvtAnimCubeOut.prototype.Init = function(context, objs, axis, rotationDir, duration, delay)
{
  DvtAnimCubeOut.superclass.Init.call(this, context, objs, axis, rotationDir, duration, delay);
  
  this._cubeEffect = true;
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to fade in an object.
  * @extends DvtBaseAnimation
  * @class DvtAnimFadeIn
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimFadeIn = function(context, objs, duration, delay)
{
  this.Init(context, objs, duration, delay);
};

DvtObj.createSubclass(DvtAnimFadeIn, DvtBaseAnimation, "DvtAnimFadeIn");

/**
  * @protected
  * @override
  */
DvtAnimFadeIn.prototype.Init = function(context, objs, duration, delay)
{
  DvtAnimFadeIn.superclass.Init.call(this, context, objs, duration, delay);
};

/**
  * @protected
  * @override
  */
DvtAnimFadeIn.prototype.InitStartState = function(obj)
{
  obj.setAlpha(0);
};

/**
  * @protected
  * @override
  */
DvtAnimFadeIn.prototype.InitEndState = function(obj)
{
  this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getAlpha, obj.setAlpha, 1);
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to fade out an object.
  * @extends DvtBaseAnimation
  * @class DvtAnimFadeOut
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimFadeOut = function(context, objs, duration, delay)
{
  this.Init(context, objs, duration, delay);
};

DvtObj.createSubclass(DvtAnimFadeOut, DvtBaseAnimation, "DvtAnimFadeOut");

/**
  * @protected
  * @override
  */
DvtAnimFadeOut.prototype.Init = function(context, objs, duration, delay)
{
  DvtAnimFadeOut.superclass.Init.call(this, context, objs, duration, delay);
};

/**
  * @protected
  * @override
  */
DvtAnimFadeOut.prototype.InitEndState = function(obj)
{
  this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getAlpha, obj.setAlpha, 0);
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to transform an object by a given matrix.
  * @extends DvtBaseAnimation
  * @class DvtAnimMatrixTransformBy
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param {DvtMatrix}  transformMat  the matrix to use for transforming
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimMatrixTransformBy = function(context, objs, transformMatrix, duration, delay)
{
  this.Init(context, objs, transformMatrix, duration, delay);
};

DvtObj.createSubclass(DvtAnimMatrixTransformBy, DvtBaseAnimation, "DvtAnimMatrixTransformBy");

/**
  * @protected
  * @override
  */
DvtAnimMatrixTransformBy.prototype.Init = function(context, objs, transformMatrix, duration, delay)
{
  this._transformMatrix = transformMatrix;
  
  DvtAnimMatrixTransformBy.superclass.Init.call(this, context, objs, duration, delay);
};

/**
  * @protected
  * @override
  */
DvtAnimMatrixTransformBy.prototype.InitEndState = function(obj)
{
  var mat = obj.getMatrix();
  if (mat)
  {
    mat = mat.clone();
    mat.concat(this._transformMatrix);
    
    this._animator.addProp(DvtAnimator.TYPE_MATRIX, obj, obj.getMatrix, obj.setMatrix, mat);
  }
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to move an object by a given amount.
  * @extends DvtBaseAnimation
  * @class DvtAnimMoveBy
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param offsets  a single DvtPoint or Array of DvtPoints to move by
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimMoveBy = function(context, objs, offsets, duration, delay)
{
  this.Init(context, objs, offsets, duration, delay);
};

DvtObj.createSubclass(DvtAnimMoveBy, DvtBaseAnimation, "DvtAnimMoveBy");

/**
  * @protected
  * @override
  */
DvtAnimMoveBy.prototype.Init = function(context, objs, offsets, duration, delay)
{
  if (offsets instanceof Array)
  {
    this._arOffsets = offsets;
  }
  else if (offsets instanceof DvtPoint)
  {
    this._arOffsets = [offsets];
  }
  else
  {
    this._arOffsets = [new DvtPoint(0, 0)];
  }
  
  this._currIndex = 0;
  
  DvtAnimMoveBy.superclass.Init.call(this, context, objs, duration, delay);
};

/**
  * @protected
  * @override
  */
DvtAnimMoveBy.prototype.InitEndState = function(obj)
{
  var offset;
  if (this._currIndex < this._arOffsets.length)
  {
    offset = this._arOffsets[this._currIndex];
  }
  else
  {
    offset = this._arOffsets[this._arOffsets.length - 1];
  }
  
  this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getTranslateX, obj.setTranslateX, obj.getTranslateX() + offset.x);
  this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getTranslateY, obj.setTranslateY, obj.getTranslateY() + offset.y);
  
  this._currIndex++;
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to move an object to a given point.
  * @extends DvtBaseAnimation
  * @class DvtAnimMoveTo
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param points  a single DvtPoint or Array of DvtPoints to move to
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimMoveTo = function(context, objs, points, duration, delay)
{
  this.Init(context, objs, points, duration, delay);
};

DvtObj.createSubclass(DvtAnimMoveTo, DvtBaseAnimation, "DvtAnimMoveTo");

/**
  * @protected
  * @override
  */
DvtAnimMoveTo.prototype.Init = function(context, objs, points, duration, delay)
{
  if (points instanceof Array)
  {
    this._arPoints = points;
  }
  else if (points instanceof DvtPoint)
  {
    this._arPoints = [points];
  }
  else
  {
    this._arPoints = [new DvtPoint(0, 0)];
  }
  
  this._currIndex = 0;
  
  DvtAnimMoveTo.superclass.Init.call(this, context, objs, duration, delay);
};

/**
  * @protected
  * @override
  */
DvtAnimMoveTo.prototype.InitEndState = function(obj)
{
  var point;
  if (this._currIndex < this._arPoints.length)
  {
    point = this._arPoints[this._currIndex];
  }
  else
  {
    point = this._arPoints[this._arPoints.length - 1];
  }
  
  this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getTranslateX, obj.setTranslateX, point.x);
  this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getTranslateY, obj.setTranslateY, point.y);
  
  this._currIndex++;
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to rotate an object by a given angle.
  * @extends DvtBaseAnimation
  * @class DvtAnimRotateBy
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param angles  a single number or Array of numbers to rotate by, in radians
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimRotateBy = function(context, objs, angles, duration, delay)
{
  this.Init(context, objs, angles, duration, delay);
};

DvtObj.createSubclass(DvtAnimRotateBy, DvtBaseAnimation, "DvtAnimRotateBy");

/**
  * @protected
  * @override
  */
DvtAnimRotateBy.prototype.Init = function(context, objs, angles, duration, delay)
{
  if (angles instanceof Array)
  {
    this._arAngles = angles;
  }
  else if (typeof angles == 'number')
  {
    this._arAngles = [angles];
  }
  else
  {
    this._arAngles = [0];
  }
  
  this._currIndex = 0;
  
  DvtAnimRotateBy.superclass.Init.call(this, context, objs, duration, delay);
};

/**
  * @protected
  * @override
  */
DvtAnimRotateBy.prototype.InitEndState = function(obj)
{
  var angle;
  if (this._currIndex < this._arAngles.length)
  {
    angle = this._arAngles[this._currIndex];
  }
  else
  {
    angle = this._arAngles[this._arAngles.length - 1];
  }
  
  this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getRotation, obj.setRotation, obj.getRotation() + angle);
  
  this._currIndex++;
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to rotate an object to a given angle.
  * @extends DvtBaseAnimation
  * @class DvtAnimRotateTo
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param angles  a single number or Array of numbers to rotate to, in radians
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimRotateTo = function(context, objs, angles, duration, delay)
{
  this.Init(context, objs, angles, duration, delay);
};

DvtObj.createSubclass(DvtAnimRotateTo, DvtBaseAnimation, "DvtAnimRotateTo");

/**
  * @protected
  * @override
  */
DvtAnimRotateTo.prototype.Init = function(context, objs, angles, duration, delay)
{
  if (angles instanceof Array)
  {
    this._arAngles = angles;
  }
  else if (typeof angles == 'number')
  {
    this._arAngles = [angles];
  }
  else
  {
    this._arAngles = [0];
  }
  
  this._currIndex = 0;
  
  DvtAnimRotateTo.superclass.Init.call(this, context, objs, duration, delay);
};

/**
  * @protected
  * @override
  */
DvtAnimRotateTo.prototype.InitEndState = function(obj)
{
  var angle;
  if (this._currIndex < this._arAngles.length)
  {
    angle = this._arAngles[this._currIndex];
  }
  else
  {
    angle = this._arAngles[this._arAngles.length - 1];
  }
  
  this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getRotation, obj.setRotation, angle);
  
  this._currIndex++;
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to scale an object by a given amount.
  * @extends DvtBaseAnimation
  * @class DvtAnimScaleBy
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param scales  a single DvtPoint or Array of DvtPoints to scale by
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimScaleBy = function(context, objs, scales, duration, delay)
{
  this.Init(context, objs, scales, duration, delay);
};

DvtObj.createSubclass(DvtAnimScaleBy, DvtBaseAnimation, "DvtAnimScaleBy");

/**
  * @protected
  * @override
  */
DvtAnimScaleBy.prototype.Init = function(context, objs, scales, duration, delay)
{
  if (scales instanceof Array)
  {
    this._arScales = scales;
  }
  else if (scales instanceof DvtPoint)
  {
    this._arScales = [scales];
  }
  else
  {
    this._arScales = [new DvtPoint(1, 1)];
  }
  
  this._currIndex = 0;
  
  DvtAnimScaleBy.superclass.Init.call(this, context, objs, duration, delay);
};

/**
  * @protected
  * @override
  */
DvtAnimScaleBy.prototype.InitEndState = function(obj)
{
  var scale;
  if (this._currIndex < this._arScales.length)
  {
    scale = this._arScales[this._currIndex];
  }
  else
  {
    scale = this._arScales[this._arScales.length - 1];
  }
  
  this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getScaleX, obj.setScaleX, obj.getScaleX() * scale.x);
  this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getScaleY, obj.setScaleY, obj.getScaleY() * scale.y);
  
  this._currIndex++;
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to move and fade in an object.  The object 
  * is moved in from the edges of a provided rectangle as it fades.  
  * The object is also scaled as it fades, so it becomes larger as it is 
  * more opaque.
  * @extends DvtAnimFadeIn
  * @class DvtAnimScaleFadeIn
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param {DvtRectangle}  boundsRect  a rectangle defining the bounds for moving the object
  * @param direction  compass direction to move display object in from; 
  *        can be one of the values defined in DvtBaseAnimation:
  *        DIR_C, DIR_N, DIR_NE, DIR_E, DIR_SE, DIR_S, DIR_SW, DIR_W, DIR_NW (default is DIR_NW)
  * @param {number}  minScale  minimum scale to make the object as it starts
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimScaleFadeIn = function(context, objs, boundsRect, direction, minScale, duration, delay)
{
  this.Init(context, objs, boundsRect, direction, minScale, duration, delay);
};

DvtObj.createSubclass(DvtAnimScaleFadeIn, DvtAnimFadeIn, "DvtAnimScaleFadeIn");

/**
  * @protected
  * @override
  */
DvtAnimScaleFadeIn.prototype.Init = function(context, objs, boundsRect, direction, minScale, duration, delay)
{
  this._boundsRect = boundsRect;
  this._direction = (direction ? direction : DvtBaseAnimation.DIR_NW);
  this._minScale = (minScale ? minScale : 0.5);
  
  DvtAnimScaleFadeIn.superclass.Init.call(this, context, objs, duration, delay);
  
  //need to do this AFTER calling superclass Init because member will
  //initially be defined there
  this._bSaveAndRestoreOriginalMatrices = true;
};

/**
  * @protected
  * @override
  */
DvtAnimScaleFadeIn.prototype.InitStartState = function(obj)
{
  DvtAnimScaleFadeIn.superclass.InitStartState.call(this, obj);
  
  var newScaleX = this._minScale * obj.getScaleX();
  var newScaleY = this._minScale * obj.getScaleY();
  
  //move the dispObj to the edge of the bounding rect and scale it down
  var objBounds = obj.getDimensions();
  var rect = new DvtRectangle(0, 0, newScaleX * objBounds.w, newScaleY * objBounds.h);
  var point = DvtBaseAnimation.GetAlignCompassPoint(rect, this._boundsRect, this._direction);
  obj.setTranslateX(point.x);
  obj.setTranslateY(point.y);
  obj.setScaleX(newScaleX);
  obj.setScaleY(newScaleY);
};

/**
  * @protected
  * @override
  */
DvtAnimScaleFadeIn.prototype.InitEndState = function(obj)
{
  DvtAnimScaleFadeIn.superclass.InitEndState.call(this, obj);
  
  var currMat = obj.getMatrix();
  this._animator.addProp(DvtAnimator.TYPE_MATRIX, obj, obj.getMatrix, obj.setMatrix, currMat);
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to move and fade out an object.  The object 
  * is moved out toward the edges of a provided rectangle as it fades.  
  * The object is also scaled as it fades, so it becomes smaller as it is 
  * more transparent.
  * @extends DvtAnimFadeOut
  * @class DvtAnimScaleFadeOut
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param {DvtRectangle}  boundsRect  a rectangle defining the bounds for moving the object
  * @param direction  compass direction to move display object out to; 
  *        can be one of the values defined in DvtBaseAnimation:
  *        DIR_C, DIR_N, DIR_NE, DIR_E, DIR_SE, DIR_S, DIR_SW, DIR_W, DIR_NW (default is DIR_NE)
  * @param {number}  minScale  minimum scale to make the object as it starts
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimScaleFadeOut = function(context, objs, boundsRect, direction, minScale, duration, delay)
{
  this.Init(context, objs, boundsRect, direction, minScale, duration, delay);
};

DvtObj.createSubclass(DvtAnimScaleFadeOut, DvtAnimFadeOut, "DvtAnimScaleFadeOut");

/**
  * @protected
  * @override
  */
DvtAnimScaleFadeOut.prototype.Init = function(context, objs, boundsRect, direction, minScale, duration, delay)
{
  this._boundsRect = boundsRect;
  this._direction = (direction ? direction : DvtBaseAnimation.DIR_NE);
  this._minScale = (minScale ? minScale : 0.5);
  
  DvtAnimScaleFadeOut.superclass.Init.call(this, context, objs, duration, delay);
  
  //need to do this AFTER calling superclass Init because member will
  //initially be defined there
  this._bSaveAndRestoreOriginalMatrices = true;
};

/**
  * @protected
  * @override
  */
DvtAnimScaleFadeOut.prototype.InitEndState = function(obj)
{
  DvtAnimScaleFadeOut.superclass.InitEndState.call(this, obj);
  
  var newScaleX = this._minScale * obj.getScaleX();
  var newScaleY = this._minScale * obj.getScaleY();
  
  //move the dispObj to the edge of the bounding rect and scale it down
  var objBounds = obj.getDimensions();
  var rect = new DvtRectangle(0, 0, newScaleX * objBounds.w, newScaleY * objBounds.h);
  var point = DvtBaseAnimation.GetAlignCompassPoint(rect, this._boundsRect, this._direction);
  
  this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getScaleX, obj.setScaleX, newScaleX);
  this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getScaleY, obj.setScaleY, newScaleY);
  
  this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getTranslateX, obj.setTranslateX, point.x);
  this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getTranslateY, obj.setTranslateY, point.y);
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to scale an object to a given value.
  * @extends DvtBaseAnimation
  * @class DvtAnimScaleTo
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param scales  a single DvtPoint or Array of DvtPoints to scale to
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimScaleTo = function(context, objs, scales, duration, delay)
{
  this.Init(context, objs, scales, duration, delay);
};

DvtObj.createSubclass(DvtAnimScaleTo, DvtBaseAnimation, "DvtAnimScaleTo");

/**
  * @protected
  * @override
  */
DvtAnimScaleTo.prototype.Init = function(context, objs, scales, duration, delay)
{
  if (scales instanceof Array)
  {
    this._arScales = scales;
  }
  else if (scales instanceof DvtPoint)
  {
    this._arScales = [scales];
  }
  else
  {
    this._arScales = [new DvtPoint(1, 1)];
  }
  
  this._currIndex = 0;
  
  DvtAnimScaleTo.superclass.Init.call(this, context, objs, duration, delay);
};

/**
  * @protected
  * @override
  */
DvtAnimScaleTo.prototype.InitEndState = function(obj)
{
  var scale;
  if (this._currIndex < this._arScales.length)
  {
    scale = this._arScales[this._currIndex];
  }
  else
  {
    scale = this._arScales[this._arScales.length - 1];
  }
  
  this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getScaleX, obj.setScaleX, scale.x);
  this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getScaleY, obj.setScaleY, scale.y);
  
  this._currIndex++;
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to pop an object in.
  * @extends DvtBaseAnimation
  * @class DvtAnimPopIn
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param {boolean}  bCenter  true to scale the object from its center, 
  *        false to scale from its origin
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimPopIn = function(context, objs, bCenter, duration, delay)
{
  this.Init(context, objs, bCenter, duration, delay);
};

DvtObj.createSubclass(DvtAnimPopIn, DvtBaseAnimation, "DvtAnimPopIn");

/**
  * @protected
  */
DvtAnimPopIn.BackOut = function(progress)
{
  return DvtEasing.backOut(progress, 2.5);
};

/**
  * @protected
  * @override
  */
DvtAnimPopIn.prototype.Init = function(context, objs, bCenter, duration, delay)
{
  this._bCenter = bCenter;
  if (this._bCenter) {
    //temporary containers used to scale an object from its center
    this._tempContainers = [];
    //current index in array of temp containers
    this._currIndex = 0;
  }
  DvtAnimPopIn.superclass.Init.call(this, context, objs, duration, delay);
};

/**
  * @override
  */
DvtAnimPopIn.prototype.setEasing = function(easing)
{
  //do nothing because we want to use our own easing
};

/**
  * @protected
  * @override
  */
DvtAnimPopIn.prototype.CreateAnimator = function(context)
{
  DvtAnimPopIn.superclass.CreateAnimator.call(this, context);
  this._animator.setEasing(DvtAnimPopIn.BackOut);
  if (this._bCenter) {
    //need to remove temporary containers at end of anim
    DvtPlayable.appendOnEnd(this._animator, this.RemoveTempContainers, this);
  }
};

/**
  * @protected
  * @override
  */
DvtAnimPopIn.prototype.InitStartState = function(obj)
{
  if (this._bCenter) {
    //For centering, the idea is to insert a temporary container between the obj and its 
    //parent.  The temp container will be positioned such that its origin is at the center
    //of the obj.  Then, when the temp container is scaled about its origin, it will look 
    //like the obj is scaled about its own center point.  
    var tempContainer = this._tempContainers[this._currIndex];
    
    var dims = obj.getDimensions();
    //store the original translate on the temp container
    tempContainer._dvtAnimPopInOrigTx = obj.getTranslateX();
    tempContainer._dvtAnimPopInOrigTy = obj.getTranslateY();
    //calculate the center point of the obj
    var dx = dims.x + .5 * dims.w;
    var dy = dims.y + .5 * dims.h;
    //position the origin of the temp container at the center point of the obj 
    //so that the obj appears to scale from its center
    tempContainer.setTranslateX(tempContainer._dvtAnimPopInOrigTx + dx);
    tempContainer.setTranslateY(tempContainer._dvtAnimPopInOrigTy + dy);
    //translate the obj so that its center point is at the origin of the temp container
    obj.setTranslateX(-dx);
    obj.setTranslateY(-dy);
    //reparent the obj to the temp container, and insert the temp container into
    //the obj's original parent at the same index
    var childIndex = obj.getParent().getChildIndex(obj);
    tempContainer._dvtAnimPopInChildIndex = childIndex;
    obj.getParent().addChildAt(tempContainer, childIndex);
    tempContainer.addChild(obj);
    
    //scale the temp container instead of the obj
    tempContainer.setScaleX(.01);
    tempContainer.setScaleY(.01);
    
    this._currIndex++;
  }
  else {
    //scale the obj
    obj.setScaleX(.01);
    obj.setScaleY(.01);
  }
  obj.setVisible(true);
};

/**
  * @protected
  * @override
  */
DvtAnimPopIn.prototype.InitEndState = function(obj)
{
  if (this._bCenter) {
    //create a temp container
    var tempContainer = new DvtContainer(this._context);
    this._tempContainers.push(tempContainer);
    //scale the temp container to full size
    this._animator.addProp(DvtAnimator.TYPE_NUMBER, tempContainer, tempContainer.getScaleX, tempContainer.setScaleX, 1);
    this._animator.addProp(DvtAnimator.TYPE_NUMBER, tempContainer, tempContainer.getScaleY, tempContainer.setScaleY, 1);
  }
  else {
    this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getScaleX, obj.setScaleX, 1);
    this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getScaleY, obj.setScaleY, 1);
  }
};

/**
  * @protected
  */
DvtAnimPopIn.prototype.RemoveTempContainers = function() {
  //remove temp containers used for scaling from center
  if (this._tempContainers) {
    for (var i = 0; i < this._tempContainers.length; i++) {
      var tempContainer = this._tempContainers[i];
      this._tempContainers[i] = null;
      if (tempContainer.getNumChildren() > 0) {
        //obj should be first child of temp container
        var obj = tempContainer.getChildAt(0);
        if (obj) {
          //translate the obj back to its original position and reparent it back to 
          //its original container at the same z-index
          obj.setTranslateX(tempContainer._dvtAnimPopInOrigTx);
          obj.setTranslateY(tempContainer._dvtAnimPopInOrigTy);
          tempContainer.getParent().addChildAt(obj, tempContainer._dvtAnimPopInChildIndex);
        }
      }
      //remove the temp container from the display list
      tempContainer.getParent().removeChild(tempContainer);
    }
    this._tempContainers = null;
  }
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to pop an object out.
  * @extends DvtBaseAnimation
  * @class DvtAnimPopOut
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to animate
  * @param {boolean}  bCenter  true to scale the object from its center, 
  *        false to scale from its origin
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAnimPopOut = function(context, objs, bCenter, duration, delay)
{
  this.Init(context, objs, bCenter, duration, delay);
};

DvtObj.createSubclass(DvtAnimPopOut, DvtBaseAnimation, "DvtAnimPopOut");

/**
  * @protected
  */
DvtAnimPopOut.BackIn = function(progress)
{
  return DvtEasing.backIn(progress, 2.5);
};

/**
  * @protected
  * @override
  */
DvtAnimPopOut.prototype.Init = function(context, objs, bCenter, duration, delay)
{
  this._bCenter = bCenter;
  if (this._bCenter) {
    //temporary containers used to scale an object from its center
    this._tempContainers = [];
    //current index in array of temp containers
    this._currIndex = 0;
  }
  DvtAnimPopOut.superclass.Init.call(this, context, objs, duration, delay);
  
  //need to do this AFTER calling superclass Init because member will
  //initially be defined there
  //only need to restore original matrices if not centering (temp containers not used)
  this._bSaveAndRestoreOriginalMatrices = !this._bCenter;
  this._bHideObjectsOnEnd = true;
};

/**
  * @override
  */
DvtAnimPopOut.prototype.setEasing = function(easing)
{
  //do nothing because we want to use our own easing
};

/**
  * @protected
  * @override
  */
DvtAnimPopOut.prototype.CreateAnimator = function(context)
{
  DvtAnimPopOut.superclass.CreateAnimator.call(this, context);
  this._animator.setEasing(DvtAnimPopOut.BackIn);
  if (this._bCenter) {
    //need to remove temporary containers at end of anim
    DvtPlayable.appendOnEnd(this._animator, this.RemoveTempContainers, this);
  }
};

/**
  * @protected
  * @override
  */
DvtAnimPopOut.prototype.InitStartState = function(obj)
{
  if (this._bCenter) {
    //For centering, the idea is to insert a temporary container between the obj and its 
    //parent.  The temp container will be positioned such that its origin is at the center
    //of the obj.  Then, when the temp container is scaled about its origin, it will look 
    //like the obj is scaled about its own center point.  
    var tempContainer = this._tempContainers[this._currIndex];
    
    var dims = obj.getDimensions();
    //store the original translate on the temp container
    tempContainer._dvtAnimPopInOrigTx = obj.getTranslateX();
    tempContainer._dvtAnimPopInOrigTy = obj.getTranslateY();
    //calculate the center point of the obj
    var dx = dims.x + .5 * dims.w;
    var dy = dims.y + .5 * dims.h;
    //position the origin of the temp container at the center point of the obj 
    //so that the obj appears to scale from its center
    tempContainer.setTranslateX(tempContainer._dvtAnimPopInOrigTx + dx);
    tempContainer.setTranslateY(tempContainer._dvtAnimPopInOrigTy + dy);
    //translate the obj so that its center point is at the origin of the temp container
    obj.setTranslateX(-dx);
    obj.setTranslateY(-dy);
    //reparent the obj to the temp container, and insert the temp container into
    //the obj's original parent at the same index
    var childIndex = obj.getParent().getChildIndex(obj);
    tempContainer._dvtAnimPopInChildIndex = childIndex;
    obj.getParent().addChildAt(tempContainer, childIndex);
    tempContainer.addChild(obj);
    
    this._currIndex++;
  }
};

/**
  * @protected
  * @override
  */
DvtAnimPopOut.prototype.InitEndState = function(obj)
{
  if (this._bCenter) {
    //create a temp container
    var tempContainer = new DvtContainer(this._context);
    this._tempContainers.push(tempContainer);
    //scale the temp container to small size
    this._animator.addProp(DvtAnimator.TYPE_NUMBER, tempContainer, tempContainer.getScaleX, tempContainer.setScaleX, .01);
    this._animator.addProp(DvtAnimator.TYPE_NUMBER, tempContainer, tempContainer.getScaleY, tempContainer.setScaleY, .01);
  }
  else {
    this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getScaleX, obj.setScaleX, .01);
    this._animator.addProp(DvtAnimator.TYPE_NUMBER, obj, obj.getScaleY, obj.setScaleY, .01);
  }
};

/**
  * @protected
  */
DvtAnimPopOut.prototype.RemoveTempContainers = function() {
  //remove temp containers used for scaling from center
  if (this._tempContainers) {
    for (var i = 0; i < this._tempContainers.length; i++) {
      var tempContainer = this._tempContainers[i];
      this._tempContainers[i] = null;
      if (tempContainer.getNumChildren() > 0) {
        //obj should be first child of temp container
        var obj = tempContainer.getChildAt(0);
        if (obj) {
          //translate the obj back to its original position and reparent it back to 
          //its original container at the same z-index
          obj.setTranslateX(tempContainer._dvtAnimPopInOrigTx);
          obj.setTranslateY(tempContainer._dvtAnimPopInOrigTy);
          tempContainer.getParent().addChildAt(obj, tempContainer._dvtAnimPopInChildIndex);
        }
      }
      //remove the temp container from the display list
      tempContainer.getParent().removeChild(tempContainer);
    }
    this._tempContainers = null;
  }
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class used to animate replacing one set of display objects with another
  * set by rotating and moving them as if on a conveyor belt.  The old 
  * objects will move back into the display while the new objects will be 
  * moved to the front.
  * @extends DvtParallelPlayable
  * @class DvtCombinedAnimConveyor
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param outObjs  a single DvtContainer or Array of DvtContainers to fade out
  * @param inObjs  a single DvtContainer or Array of DvtContainers to fade in
  * @param outDir compass direction to move old display objects out; can be 
  *        one of: DIR_N, DIR_E, DIR_S, DIR_W
  * @param inDir compass direction to move new display objects in from; can 
  *        be one of: DIR_N, DIR_E, DIR_S, DIR_W
  * @param {number}  duration  length of individual out and in animations, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtCombinedAnimConveyor = function(context, outObjs, inObjs, outDir, inDir, duration, delay)
{
  this.Init(context, outObjs, inObjs, outDir, inDir, duration, delay);
};

DvtObj.createSubclass(DvtCombinedAnimConveyor, DvtParallelPlayable, "DvtCombinedAnimConveyor");

/**
  * @protected
  * @override
  */
DvtCombinedAnimConveyor.prototype.Init = function(context, outObjs, inObjs, outDir, inDir, duration, delay)
{
  if (!duration)
    duration = 0.5;
  if (!delay)
    delay = 0;
  
  var conveyorOut = new DvtAnimConveyorOut(context, outObjs, outDir, duration, delay);
  var conveyorIn = new DvtAnimConveyorIn(context, inObjs, inDir, duration, (duration / 3) + delay);
  
  DvtCombinedAnimConveyor.superclass.Init.call(this, context, [conveyorOut, conveyorIn]);
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class used to animate replacing one set of display objects with 
  * another set by rotating out the old and rotating in the new, 
  * as if on a cube.
  * @extends DvtParallelPlayable
  * @class DvtCombinedAnimCube
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param outObjs  a single DvtContainer or Array of DvtContainers to fade out
  * @param inObjs  a single DvtContainer or Array of DvtContainers to fade in
  * @param axis axis of rotation; can be one of: AXIS_X, AXIS_Y
  * @param rotationDir direction of rotation about the axis; can be one of:
  *        ROT_DIR_CLOCKWISE, ROT_DIR_COUNTERCLOCKWISE;
  *        direction is determined by looking down the axis from the positive side
  * @param {number}  duration  length of individual out and in animations, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtCombinedAnimCube = function(context, outObjs, inObjs, axis, rotationDir, duration, delay)
{
  this.Init(context, outObjs, inObjs, axis, rotationDir, duration, delay);
};

DvtObj.createSubclass(DvtCombinedAnimCube, DvtParallelPlayable, "DvtCombinedAnimCube");

/**
  * @protected
  * @override
  */
DvtCombinedAnimCube.prototype.Init = function(context, outObjs, inObjs, axis, rotationDir, duration, delay)
{
  if (!duration)
    duration = 0.5;
  if (!delay)
    delay = 0;
  
  var cubeOut = new DvtAnimCubeOut(context, outObjs, axis, rotationDir, duration, delay);
  var cubeIn = new DvtAnimCubeIn(context, inObjs, axis, rotationDir, duration, delay);
  
  DvtCombinedAnimCube.superclass.Init.call(this, context, [cubeOut, cubeIn]);
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class used to animate replacing one set of display objects with another set by
  * fading out the old and fading in the new.
  * @extends DvtParallelPlayable
  * @class DvtCombinedAnimFade
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param outObjs  a single DvtContainer or Array of DvtContainers to fade out
  * @param inObjs  a single DvtContainer or Array of DvtContainers to fade in
  * @param {number}  duration  length of individual out and in animations, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtCombinedAnimFade = function(context, outObjs, inObjs, duration, delay)
{
  this.Init(context, outObjs, inObjs, duration, delay);
};

DvtObj.createSubclass(DvtCombinedAnimFade, DvtParallelPlayable, "DvtCombinedAnimFade");

/**
  * @protected
  * @override
  */
DvtCombinedAnimFade.prototype.Init = function(context, outObjs, inObjs, duration, delay)
{
  if (!duration)
    duration = 0.5;
  if (!delay)
    delay = 0;
  
  var fadeOut = new DvtAnimFadeOut(context, outObjs, duration, delay);
  //delay the fade out by a little bit to make both animations easier to see
  var fadeIn = new DvtAnimFadeIn(context, inObjs, duration, 0.5 * fadeOut.getDuration() + delay);
  
  DvtCombinedAnimFade.superclass.Init.call(this, context, [fadeOut, fadeIn]);
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class used to animate replacing one set of display objects with 
  * another set by flipping out the old and flipping in the new, 
  * as if on a playing card.
  * @extends DvtSequentialPlayable
  * @class DvtCombinedAnimFlip
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param outObjs  a single DvtContainer or Array of DvtContainers to fade out
  * @param inObjs  a single DvtContainer or Array of DvtContainers to fade in
  * @param axis axis of rotation; can be one of: AXIS_X, AXIS_Y
  * @param rotationDir direction of rotation about the axis; can be one of:
  *        ROT_DIR_CLOCKWISE, ROT_DIR_COUNTERCLOCKWISE;
  *        direction is determined by looking down the axis from the positive side
  * @param {number}  duration  length of individual out and in animations, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtCombinedAnimFlip = function(context, outObjs, inObjs, axis, rotationDir, duration, delay)
{
  this.Init(context, outObjs, inObjs, axis, rotationDir, duration, delay);
};

DvtObj.createSubclass(DvtCombinedAnimFlip, DvtSequentialPlayable, "DvtCombinedAnimFlip");

/**
  * @protected
  * @override
  */
DvtCombinedAnimFlip.prototype.Init = function(context, outObjs, inObjs, axis, rotationDir, duration, delay)
{
  if (!duration)
    duration = 0.5;
  if (!delay)
    delay = 0;
  
  var flipOut = new DvtAnimFlipOut(context, outObjs, axis, rotationDir, duration, delay);
  //adjust easing so that animation is slow at the beginning, 
  //but fast at the end
  flipOut.setEasing(DvtBaseAnimation.EASING_CUBIC_IN);
  var flipIn = new DvtAnimFlipIn(context, inObjs, axis, rotationDir, duration, delay);
  //adjust easing so that animation is fast at the beginning, 
  //but slow at the end
  flipIn.setEasing(DvtBaseAnimation.EASING_CUBIC_OUT);
  
  //because the flipOut and flipIn animations are played in sequence,
  //and because the initState of the flipIn won't be set until it starts
  //to play, we need to explicitly hide the flipIn objects here so that
  //they're not visible during the first flipOut part of the sequence
  flipIn.HideObjects();
  
  DvtCombinedAnimFlip.superclass.Init.call(this, context, [flipOut, flipIn]);
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class used to animate replacing one set of display objects with another set by
  * moving out the old and moving in the new.
  * @extends DvtParallelPlayable
  * @class DvtCombinedAnimMoveBy
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param outObjs  a single DvtContainer or Array of DvtContainers to move out
  * @param inObjs  a single DvtContainer or Array of DvtContainers to move in
  * @param outOffsets  a single DvtPoint or Array of DvtPoints to move out by
  * @param inOffsets  a single DvtPoint or Array of DvtPoints to move in by
  * @param {number}  duration  length of individual out and in animations, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtCombinedAnimMoveBy = function(context, outObjs, inObjs, outOffsets, inOffsets, duration, delay)
{
  this.Init(context, outObjs, inObjs, outOffsets, inOffsets, duration, delay);
};

DvtObj.createSubclass(DvtCombinedAnimMoveBy, DvtParallelPlayable, "DvtCombinedAnimMoveBy");

/**
  * @protected
  * @override
  */
DvtCombinedAnimMoveBy.prototype.Init = function(context, outObjs, inObjs, outOffsets, inOffsets, duration, delay)
{
  if (!duration)
    duration = 0.5;
  if (!delay)
    delay = 0;
  
  var moveOut = new DvtAnimMoveBy(context, outObjs, outOffsets, duration, delay);
  var moveIn = new DvtAnimMoveBy(context, inObjs, inOffsets, duration, delay);
  
  DvtCombinedAnimMoveBy.superclass.Init.call(this, context, [moveOut, moveIn]);
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class used to animate replacing one set of display objects with another set by
  * moving and fading out the old and moving and fading in the new.  The objects 
  * are moved out toward and in from the edges of a provided rectangle as they fade.  
  * The objects are also scaled as they fade, so they become smaller as they are 
  * more transparent.
  * @extends DvtParallelPlayable
  * @class DvtCombinedAnimScaleFade
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param outObjs  a single DvtContainer or Array of DvtContainers to fade out
  * @param inObjs  a single DvtContainer or Array of DvtContainers to fade in
  * @param {DvtRectangle}  boundsRect  a rectangle defining the bounds for moving the objects
  * @param outDirection  compass direction to move old display objects out to; 
  *        can be one of the values defined in DvtBaseAnimation:
  *        DIR_C, DIR_N, DIR_NE, DIR_E, DIR_SE, DIR_S, DIR_SW, DIR_W, DIR_NW (default is DIR_NE)
  * @param inDirection  compass direction to move new display objects in from; 
  *        can be one of the values defined in DvtBaseAnimation:
  *        DIR_C, DIR_N, DIR_NE, DIR_E, DIR_SE, DIR_S, DIR_SW, DIR_W, DIR_NW (default is DIR_NW)
  * @param {number}  minScale  minimum scale to make the objects as they fade
  * @param {number}  duration  length of individual out and in animations, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtCombinedAnimScaleFade = function(context, outObjs, inObjs, boundsRect, outDirection, inDirection, minScale, duration, delay)
{
  this.Init(context, outObjs, inObjs, boundsRect, outDirection, inDirection, minScale, duration, delay);
};

DvtObj.createSubclass(DvtCombinedAnimScaleFade, DvtParallelPlayable, "DvtCombinedAnimScaleFade");

/**
  * @protected
  * @override
  */
DvtCombinedAnimScaleFade.prototype.Init = function(context, outObjs, inObjs, boundsRect, outDirection, inDirection, minScale, duration, delay)
{
  if (!outDirection)
    outDirection = DvtBaseAnimation.DIR_NE;
  if (!inDirection)
    inDirection = DvtBaseAnimation.DIR_NW;
  if (!minScale)
    minScale = 0.5;
  if (!duration)
    duration = 0.5;
  if (!delay)
    delay = 0;
  
  var fadeOut = new DvtAnimScaleFadeOut(context, outObjs, boundsRect, outDirection, minScale, duration, delay);
  var fadeIn = new DvtAnimScaleFadeIn(context, inObjs, boundsRect, inDirection, minScale, duration, 0.3 * fadeOut.getDuration() + delay);
  
  DvtCombinedAnimScaleFade.superclass.Init.call(this, context, [fadeOut, fadeIn]);
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class used to animate a set of display objects popping into the display
  * at staggered times.  
  * @extends DvtParallelPlayable
  * @class DvtCombinedAnimPopIn
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to pop in
  * @param {boolean}  bCenter  true to scale the object from its center, 
  *        false to scale from its origin (true is not implemented yet)
  * @param {number}  popDuration  length of individual pop animations, 
  *        in seconds
  * @param {number}  totalDuration  total length of time in which all 
  *        individiual animations must run, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtCombinedAnimPopIn = function(context, objs, bCenter, popDuration, totalDuration, delay)
{
  this.Init(context, objs, bCenter, popDuration, totalDuration, delay);
};

DvtObj.createSubclass(DvtCombinedAnimPopIn, DvtParallelPlayable, "DvtCombinedAnimPopIn");

/**
  * @protected
  * @override
  */
DvtCombinedAnimPopIn.prototype.Init = function(context, objs, bCenter, popDuration, totalDuration, delay)
{
  var array = [];
  var dispObjArray;
    
  if (objs instanceof Array)
  {
    dispObjArray = objs;
  }
  else
  {
    dispObjArray = [objs];
  }
  
  delay = (delay === null || isNaN(delay)) ? 0 : delay;
  
  for (var i = 0; i < dispObjArray.length; i++)
  {
    var dispObj = dispObjArray[i];
    //if (dispObj instanceof DvtDisplayable)
    //{
      var individualDelay = delay + (Math.random() * (totalDuration - popDuration));
      var popIn = new DvtAnimPopIn(context, dispObj, bCenter, popDuration, individualDelay);
      array.push(popIn);
    //}
  }
  
  DvtCombinedAnimPopIn.superclass.Init.call(this, context, array);
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class used to animate a set of display objects popping out of the display
  * at staggered times.  
  * @extends DvtParallelPlayable
  * @class DvtCombinedAnimPopOut
  * @constructor
  * 
  * @param {DvtContext}  context  platform specific context object
  * @param objs  a single DvtContainer or Array of DvtContainers to pop out
  * @param {boolean}  bCenter  true to scale the object from its center, 
  *        false to scale from its origin (true is not implemented yet)
  * @param {number}  popDuration  length of individual pop animations, 
  *        in seconds
  * @param {number}  totalDuration  total length of time in which all 
  *        individiual animations must run, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtCombinedAnimPopOut = function(context, objs, bCenter, popDuration, totalDuration, delay)
{
  this.Init(context, objs, bCenter, popDuration, totalDuration, delay);
};

DvtObj.createSubclass(DvtCombinedAnimPopOut, DvtParallelPlayable, "DvtCombinedAnimPopOut");

/**
  * @protected
  * @override
  */
DvtCombinedAnimPopOut.prototype.Init = function(context, objs, bCenter, popDuration, totalDuration, delay)
{
  var array = [];
  var dispObjArray;
  
    
  if (objs instanceof Array)
  {
    dispObjArray = objs;
  }
  else
  {
    dispObjArray = [objs];
  }
  
  delay = (delay === null || isNaN(delay)) ? 0 : delay;
  
  for (var i = 0; i < dispObjArray.length; i++)
  {
    var dispObj = dispObjArray[i];
    //if (dispObj instanceof DvtDisplayable)
    //{
      var individualDelay = delay + (Math.random() * (totalDuration - popDuration));
      var popIn = new DvtAnimPopOut(context, dispObj, bCenter, popDuration, individualDelay);
      array.push(popIn);
    //}
  }
  
  DvtCombinedAnimPopOut.superclass.Init.call(this, context, array);
};
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*   DvtBaseDrawEffect                                                     */
/*-------------------------------------------------------------------------*/
/**
 * A base class for draw effects like sahodws, glows, etc.
 * {@link DvtBaseDrawEffect}.
 * @extends DvtLockable
 * @class DvtBaseDrawEffect  A base class for draw effects like sahodws, glows, etc.
 * @constructor  Do not create/use directly.
 */
var DvtBaseDrawEffect = function () {
  this._Init();
}

DvtObj.createSubclass(DvtBaseDrawEffect, DvtLockable, "DvtBaseDrawEffect");

/** 
 * @private 
 */
DvtBaseDrawEffect.prototype._Init = function () {
   DvtBaseDrawEffect.superclass._Init.call(this) ;
}

/**
 *   Returns the id of this draw effect.
 *   @type String
 */
DvtBaseDrawEffect.prototype.getId = function () {
  return this._id;
}

/**
 *   Sets the id of this fill.
 *   @param {String} id  The id for the fill.
 */
DvtBaseDrawEffect.prototype.setId = function (id) {
  this._id = id;
}

/**
 *   Merge properties in the current object into the supplied object.
 *   Abstract method, subclasses must implement. Used internally by clone().
 *   @param {DvtObj}
 *   @private
 */
DvtBaseDrawEffect.prototype.mergeProps = function (obj) {
  obj.setId(this._id);
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*   DvtColorUtils       A static class for css color manipulation         */
/*-------------------------------------------------------------------------*/
/*   The static utility functions operate on color specifications of the   */
/*   format #rrggbb, or rgb(r,g,b), or rgba(r,g,b,a).                      */
/*-------------------------------------------------------------------------*/
/**  Static utility functions that operate on color specification strings of the
  *  format #rrggbb, or rgb(r,g,b), or rgba(r,g,b,a).
  *  @base DvtColorUtils
  */
var DvtColorUtils = {} ;

DvtObj.createSubclass(DvtColorUtils, DvtObj, "DvtColorUtils");

//  Channel definition constants

DvtColorUtils._RED   = 0 ;  // Channels. Don't change
DvtColorUtils._GREEN = 1 ;  // defs unless the routines
DvtColorUtils._BLUE  = 2 ;  // below are changed.
DvtColorUtils._ALPHA = 3 ;

//  private constants

DvtColorUtils._RGBA  = 'rgba(' ;
DvtColorUtils._RGB   = 'rgb(' ;
DvtColorUtils._POUND = '#'

DvtColorUtils._FACTOR = 0.15 ;  // default darkening percentage.

/** @private */
DvtColorUtils._names ;          // associative array


/*-------------------------------------------------------------------------*/
/*  getColorFromName()                                                     */
/*-------------------------------------------------------------------------*/
/**
  * Returns a color definition string from the named color.
  * @param {String}  name  The color name.
  * @type String
  * @return  An #rrggbb color string.
  */
DvtColorUtils.getColorFromName = function(name)
{
  if (! DvtColorUtils._names) {
    var ar = [] ;
    ar['aqua']    = '#00FFFF';
    ar['black']   = '#000000';
    ar['blue']    = '#0000FF'
    ar['white']   = '#FFFFFF';
    ar['fuchsia'] = '#FF00FF';
    ar['gray']    = '#848284';
    ar['lime']    = '#00FF00';
    ar['maroon']  = '#840000';
    ar['green']   = '#008200';
    ar['navy']    = '#000084';
    ar['olive']   = '#848200';
    ar['red']     = '#FF0000';
    ar['silver']  = '#C6C3C6';
    ar['teal']    = '#008284';
    ar['yellow']  = '#FFFF00';
    ar['purple']  = '#800080';

    // name colors for testing
    ar['cyan']          = '#D2B48C';
    ar['goldenrod']     = '#DAA520';
    ar['lightblue']     = '#ADD8E6';
    ar['lightyellow']   = '#FFFFE0';
    ar['orange']        = '#FFA500';
    ar['paleGoldenRod'] = '#EEE8AA';
    ar['paleturquoise'] = '#AFEEEE';
    ar['peachpuff']     = '#FFDAB9';
    ar['pink']          = '#FFC0CB';
    ar['tan']           = '#D2B48C';
    ar['thistle']       = '#D8BFD8';

    ar['transparent']   = 'rgba(255,255,255,0)';

    DvtColorUtils._names = ar ;
  }
  return DvtColorUtils._names[name] ;

} ;


/*-------------------------------------------------------------------------*/
/*  isColor()                                                              */
/*-------------------------------------------------------------------------*/
/**
 * Determine if the given string specifies a color value.
 * @param {String}  s  The string to be evaluated.
 * @param {Boolean}  bValid  Optional.  If omitted or false, the string prefix
 *                           is checked for validity only. If true, a deeper
 *                           check of the rgb component values is performed.
 * @type Boolean
 * @returns true if the string specifies a color value, else false.
 */
DvtColorUtils.isColor = function(s, bValid)
{
  if (! bValid) {
    return ((s.charAt(0) === DvtColorUtils._POUND) ||
            (s.indexOf(DvtColorUtils._RGBA) === 0) ||
            (s.indexOf(DvtColorUtils._RGB) === 0)) ;
  }

  // Deeper validation

  var bRet = false ;

  s = DvtStringUtils.ltrim(s);
  s = s.toLowerCase() ;

  if  (s.charAt(0) === DvtColorUtils._POUND) {
     if (s.length === 7) {
       var pat = new RegExp(/^#[0-9a-f]{6}$/i);
       bRet = pat.test(s);
     }
  }
  else {
    var bRGBA = (s.indexOf(DvtColorUtils._RGBA) === 0) ;
    var bRGB  ;
    
    if (! bRGBA) {
      bRGB = (s.indexOf(DvtColorUtils._RGB) === 0) ;
    }

    if (bRGBA || bRGB) {
      var x = s.indexOf(')') ;

      if (x >= 0) {
        var ar  = s.substring((bRGBA? 5 : 4), x).split(',') ;
        var len = ar.length ;
        var bVals = true ;

        for (var i = 0; i < len; i++) {
           var n = ar[i] ;
           if (( (1 < 3) && (n < 0 || n > 255)) ||  ((i > 3) && (n < 0 || n > 1))) {
             bVals = false ;
             break ;
           }
        }

        if (bVals) {
          bRet = (bRGBA? (len === 4) : (len === 3)) ;
        }
      }
    }
  }

  return bRet ;
} ;



/*-------------------------------------------------------------------------*/
/*   get/setAlpha()                                                        */
/*-------------------------------------------------------------------------*/
/**
  *  Returns the alpha value (or implied value) of the specified color string.
  *  @param {String} c The color specification.
  *  @type {number}
  *  @returns {number}  The alpha-channel value (between 0 and 1).
  */
DvtColorUtils.getAlpha = function(c)
{
   return DvtColorUtils._getChannel(c, DvtColorUtils._ALPHA) ;
} ;


/**
  *  Changes the alpha channel in the supplied color string.
  *  @param {String} c The color specification.
  *  @param {number} a The alpha value (range 0 to 1).
  *  @type String
  *  @returns  A new color specification.
  */
DvtColorUtils.setAlpha = function(c, a)
{
   return DvtColorUtils._setChannel(c, DvtColorUtils._ALPHA, a) ;
} ;


/*-------------------------------------------------------------------------*/
/*   get/setRed()                                                          */
/*-------------------------------------------------------------------------*/
/**
  *  Returns the red-channel value of the specified color string.
  *  @param {String} c The color specification.
  *  @type number
  *  @returns   The red-channel value as a decimal number between 0 and 255.
  */
DvtColorUtils.getRed = function(c)
{
   return DvtColorUtils._getChannel(c, DvtColorUtils._RED) ;
} ;

/**
  *  Changes the red-channel value in the supplied color string.
  *  @param {String} c The color specification.
  *  @param {number} r The red channel value as a decimal number between 0 and 255.
  *  @type String
  *  @returns  A new color string specification.
  */
DvtColorUtils.setRed = function(c, r)
{
   return DvtColorUtils._setChannel(c, DvtColorUtils._RED, r) ;
} ;



/*-------------------------------------------------------------------------*/
/*   get/setBlue()                                                         */
/*-------------------------------------------------------------------------*/
/**
  *  Returns the blue-channel value of the specified color string.
  *  @param {String} c The color specification.
  *  @type number
  *  @returns  The blue-channel value as a decimal number between 0 and 255.
  */
DvtColorUtils.getBlue = function(c)
{
   return DvtColorUtils._getChannel(c, DvtColorUtils._BLUE) ;
} ;


/**
  *  Changes the blue-channel value in the supplied color string.
  *  @param {String} c The color specification.
  *  @param {number} b The blue channel value as a decimal number.
  *  @type String
  *  @returns  A new color string specification.
  */
DvtColorUtils.setBlue = function(c, b)
{
   return DvtColorUtils._setChannel(c, DvtColorUtils._BLUE, b) ;
} ;



/*-------------------------------------------------------------------------*/
/*   get/setGreen()                                                        */
/*-------------------------------------------------------------------------*/
/**
  *  Returns the green-channel value of the specified color string.
  *  @param {String} c The color specification.
  *  @type number
  *  @returns   The green value as a decimal number between 0 and 255.
  */
DvtColorUtils.getGreen = function(c)
{
   return DvtColorUtils._getChannel(c, DvtColorUtils._GREEN) ;
} ;

/**
  *  Changes the green-channel value in the supplied color string.
  *  @param {String} c The color specification.
  *  @param {number} g The green channel value as a decimal number between 0 and 255.
  *  @type String
  *  @returns  A new color string specification.
  */
DvtColorUtils.setGreen = function(c, g)
{
   return DvtColorUtils._setChannel(c, DvtColorUtils._GREEN, g) ;
} ;



/*-------------------------------------------------------------------------*/
/*   getChannel()     Returns a channel from a #, rgb or rgba string.      */
/*                    (Note: if an alpha channel is requested and a # or   */
/*                    rgb string is supplied, the implied value of 1 is    */
/*                    returned.                                            */
/*-------------------------------------------------------------------------*/
/**
  *  Returns a specifed channel value from a css color specification (#, rgb(), rgba()).
  *  If an alpha channel is requested and a # or rgb string is supplied,
  *  an implied value of 1 is returned.
  *  @private
  *  @param  {String} c  The color string. 
  *  @param  {number} chan  The channel (see {@DvtColorUtils#_RED} for example).
  *  @type number
  *  @returns The channel value as a decimal number (between 0 and 255).
  */
DvtColorUtils._getChannel = function(c, chan)
{
    var clr = c;
    var chval = null;// the return value
    
    // If clr is a named color, then convert into usable format
    var namedColor = DvtColorUtils.getColorFromName(clr);
    if(namedColor)
      clr = namedColor;
    
    // Alpha support
    if (chan === DvtColorUtils._ALPHA) {
      if (clr.charAt(0) === '#') 
        return (clr.length > 7) ? parseInt(clr.substring(1, 3), 16) / 255 : 1;//check for MT extended format of #aarrggbb
      else if (clr === 'none') 
        return 0;
    }

    var x1 = clr.indexOf('(');
    if (x1 < 0) {
        ar = [];
        //BUG FIX 13704008: parse channels for different # formats
        if (clr.length > 7) {
            //  #aarrggbb format
            ar[0] = parseInt(clr.substr(3, 2), 16);
            ar[1] = parseInt(clr.substr(5, 2), 16);
            ar[2] = parseInt(clr.substr(7, 2), 16);
            ar[3] = parseInt(clr.substr(1, 2), 16) / 255;
        }
        else {
            //  #rrggbb format
            ar[0] = parseInt(clr.substr(1, 2), 16);
            ar[1] = parseInt(clr.substr(3, 2), 16);
            ar[2] = parseInt(clr.substr(5, 2), 16);
            ar[3] = 1;
        }
        chval = ar[chan];
    }
    else {

        //  rgb() or rgba() format
        var x2 = clr.indexOf(')');
        var ar = clr.substring(x1 + 1, x2).split(',');

        if (ar.length === 3 && chan === DvtColorUtils._ALPHA) {
            chval = 1;
        }
        else {
            chval = ar[chan];
            chval = parseFloat(chval);
        }
    }

    return chval;
} ;



/*-------------------------------------------------------------------------*/
/*   getDarker()                                                           */
/*-------------------------------------------------------------------------*/
/**
  * Returns the specified color made darker by the specified percentage.
  * <p>
  * Example:<br><br><code>
  * // create a color darkened by 25%). 
  *  var darker = DvtColorUtils.getDarker("rgba(220,128,49)", 0.25) ;<br>
  *  </code>
  * @param {String} color   A color specification.
  * @param {number} factor  An optional percentage by which each color component is to be
  *                         darkened (0 returns unchanged) specified as a decimal
  *                         (e.g. 25% = 0.25).  If omitted, a default percentage of
  *                         15% (i.e 0.15) is applied.
  * @type String
  * @returns A darkened color specification in RGBA format.
  */
DvtColorUtils.getDarker = function(color, factor)
{
   //TDO

   var  r = DvtColorUtils._getChannel(color, DvtColorUtils._RED);
   var  g = DvtColorUtils._getChannel(color, DvtColorUtils._GREEN);
   var  b = DvtColorUtils._getChannel(color, DvtColorUtils._BLUE);
   var  a = DvtColorUtils._getChannel(color, DvtColorUtils._ALPHA);

   if (! factor) {
     factor = DvtColorUtils._FACTOR ;    // use default factor
   }
   
   r = Math.max(parseInt(r * factor), 0);
   g = Math.max(parseInt(g * factor), 0);
   b = Math.max(parseInt(b * factor), 0);

   return DvtColorUtils.makeRGBA(r, g, b, a);
}; 



/*-------------------------------------------------------------------------*/
/*   getGrey()                                                             */
/*-------------------------------------------------------------------------*/
/**
  * @this {DvtColorUtils}
  *   Returns a greyscale version of the supplied color (in the same format).
  *   @param {String} color  The color to be converted.
  *   @returns a greyscale version of the supplied color
  *   @type String
  *   @returns a greyscale version of the supplied color (in the same format).
  */
DvtColorUtils.getGrey = function(color)
{
   var  ret ;

   var  r = DvtColorUtils._getChannel(color, DvtColorUtils._RED);
   var  g = DvtColorUtils._getChannel(color, DvtColorUtils._BLUE);
   var  b = DvtColorUtils._getChannel(color, DvtColorUtils._GREEN);

   var gray = Math.round((r * 0.3) + (g * 0.59) + (b * 0.11));

   return this.setRGB(color, gray, gray, gray) ;
} ;



/*-------------------------------------------------------------------------*/
/*   invert()                                                              */
/*-------------------------------------------------------------------------*/
/**
  *   Inverts the specified color. 
  *   @param {String} c   The color to be inverted.
  *   @type  String
  *   @returns The inverted color in the same color format as supplied.
  */
DvtColorUtils.invert = function(c) {

   var  ret ;

   var  r = 255 - DvtColorUtils._getChannel(c, DvtColorUtils._RED);
   var  g = 255 - DvtColorUtils._getChannel(c, DvtColorUtils._BLUE);
   var  b = 255 - DvtColorUtils._getChannel(c, DvtColorUtils._GREEN);

   if (c.charAt(0) === '#') {
      ret = DvtColorUtils.makePound(r,g,b) ;
   }
   else if (c.substr(0,4) === DvtColorUtils._RGB) {
      ret = DvtColorUtils.makeRGB(r,g,b) ;
   }
   else {
      ret = DvtColorUtils.makeRGBA(r,g,b,DvtColorUtils._getChannel(c, DvtColorUtils._ALPHA)) ;
   }

   return ret ;
} ;



/*-------------------------------------------------------------------------*/
/*   isRGBA()                                                              */
/*-------------------------------------------------------------------------*/
/**
  *   Returns true if the color string is of rgba(...) format.
  *   @param {String} c   the color to be tested.
  *   @returns {boolean}  true if the color is of rgba(. . .) type, else false.
  */
DvtColorUtils.isRGBA = function(c)
{
   return (c.substr(0,5) === DvtColorUtils._RGBA) ;
};



/*-------------------------------------------------------------------------*/
/*   makeRGB()                                                             */
/*-------------------------------------------------------------------------*/
/**
  *  Creates an rgb(...) format from the supplied red, green, blue channel values.
  *  @param {number} r  The red value as a decimal number in the range 0 - 255. 
  *  @param {number} g  The green value as a decimal number in the range 0 - 255. 
  *  @param {number} b  The blue value as a decimal number in the range 0 - 255. 
  *  @type String
  *  @returns A new rgb(. . .) format string.
  */
DvtColorUtils.makeRGB = function(r,g,b)
{
   b = ((b === null || isNaN(b)) ? 0 : b);
   g = ((g === null || isNaN(g)) ? 0 : g);
   r = ((r === null || isNaN(r)) ? 0 : r);

   return  DvtColorUtils._RGB  + r + ',' + g + ',' + b + ')' ;
} ;


/*-------------------------------------------------------------------------*/
/*   makeRGBA()                                                            */
/*-------------------------------------------------------------------------*/
/**
  *  Creates an rgba(...) format string using the supplied red, green, blue,
  *   and alpha channel values.
  *  @param {number} r  The red value as a decimal number in the range 0 - 255. 
  *  @param {number} g  The green value as a decimal number in the range 0 - 255. 
  *  @param {number} b  The blue value as a decimal number in the range 0 - 255. 
  *  @param {number} a  The alpha value as a decimal number in the range 0 - 1.
  *                  If omitted, 1 is assumed.
  *  @type String
  *  @returns A new rgba(. . .) format string.
  */
DvtColorUtils.makeRGBA = function(r,g,b,a)
{
   b = ((b === null || isNaN(b)) ? 0 : b);
   g = ((g === null || isNaN(g)) ? 0 : g);
   r = ((r === null || isNaN(r)) ? 0 : r);
   a = ((a === null || isNaN(a)) ? 1 : a);

   return  DvtColorUtils._RGBA  + r + ',' + g + ',' + b + ',' + a + ')' ;
} ;


/*-------------------------------------------------------------------------*/
/*   makePound()                                                           */
/*-------------------------------------------------------------------------*/
/**
  *  Creates a #rrggbb format string using the supplied red, green and blue
  *  channel values.
  *  @param {number} r  The red value as a decimal number in the range 0 - 255. 
  *  @param {number} g  The green value as a decimal number in the range 0 - 255. 
  *  @param {number} b  The blue value as a decimal number in the range 0 - 255. 
  *  @type String
  *  @returns A new #rrggbb format string.
  */
DvtColorUtils.makePound = function(r,g,b)
{
  var rr = Math.round(r);
  var gg = Math.round(g);
  var bb = Math.round(b);
   var red   = rr.toString(16) ;
   var green = gg.toString(16) ;
   var blue  = bb.toString(16) ;

   return '#' + (red.length === 1  ? '0' : '') + red + (green.length === 1 ? '0' : '') + green +
                (blue.length === 1 ? '0' : '') + blue ;
} ;



/*-------------------------------------------------------------------------*/
/*   getPound()  Returns a "#rrggbb" string from a color string            */
/*              specification such as #rr[gg[bb[aa]]], rgba(...), rgb(...) */
/*-------------------------------------------------------------------------*/
/**
  *   Returns a "#rrggbb"  color string from the supplied color string. Formats
  *   accepted are the extended middle-tier "#aarrggbb" string, "rgba(r,g,b,a)",
  *   or an "rgb(r,g,b)". If a "#rrggbb" is supplied, the same object will be
  *   returned.  (See also {@link DvtColorUtils#makePound}.)
  *   @param {String} s  A color string specification.
  *   @returns {String}  a string of the format "#rrggbb".
  */
DvtColorUtils.getPound = function(s)
{
   var ret ;

   if (s.charAt(0) === '#') {

     var len = s.length ;

     if (len <= 7) {
       return s ;
     }
     else  {             // alpha specified (#aarrggbb) - middle-tier extended format
       ret = DvtColorUtils._POUND ;
       ret += s.substring(3, 5) + s.substring(5,7) + s.substring(7,9)  ;
     }
   }
   else {
     var r =  DvtColorUtils.getRed(s);
     var g =  DvtColorUtils.getGreen(s);
     var b =  DvtColorUtils.getBlue(s);
     ret   = DvtColorUtils.makePound(r,g,b) ;
   }

   return ret;

} ;



/*-------------------------------------------------------------------------*/
/*   getRGB()   Returns an rgb(rr,gg,bb) string from a color string        */
/*              specification such as #rr[gg[bb[aa]]], rgba(...), rgb(...) */
/*-------------------------------------------------------------------------*/
/**
  *   Returns an "rgb(r,g,b)"  color string from a supplied "#rrggbb" or
  *   extended middle-tier "#aarrggbb" string, or an rgb(r,g,b,a).
  *   If an "rgb(r,g,b)" is supplied, the same object will be returned.  (See also {@link DvtColorUtils#makePound}.)
  *   @param {String} s  A color string specification.
  *   @type String.
  */
DvtColorUtils.getRGB = function(s)
{
   var ret ;
   
   // If clr is a named color, then convert into usable format
   var namedColor = DvtColorUtils.getColorFromName(s);
   if(namedColor)
     s = namedColor;

   if (s.charAt(0) === '#') {
     ret = DvtColorUtils._RGB ;

     var len = s.length ;

     if (len > 7) {             // alpha specified (#aarrggbb) - middle-tier extended format
       ret += parseInt(s.substring(3, 5),16) + ","  +  parseInt(s.substring(5,7),16) + "," +
              parseInt(s.substring(7,9),16)  ;
     }
     else if (len === 7) {      // alpha not specified (#rrggbb)
       ret += parseInt(s.substring(1, 3),16) + "," + parseInt(s.substring(3,5),16) + "," + 
              parseInt(s.substr(5),16) ;
     }
     else if (len === 4) {      // #rgb
         var r = s.substring(1, 2);
             r += r ;
         var g = s.substring(2, 3);
             g += g ;
         var b = s.substring(3);
             b += b ;
             
         ret += parseInt(r,16) + "," + parseInt(g,16) + "," + parseInt(b, 16) ;
     }
     else if (len === 5) {      // #rrgg
         ret += parseInt(s.substring(1, 3),16) + "," + parseInt(s.substring(3,5),16) + ",0,1" ;
     }
     else if (len === 3) {      // #rr
         ret += parseInt(s.substring(1, 3),16) + ",0,0,1" ;
     }
     
     ret += ')' ;
   }
   else if (s.substr(0,5) === DvtColorUtils._RGBA) {
     var x = s.lastIndexOf(',') ;
     ret = DvtColorUtils._RGB ;
     ret += s.substring(5, x);
     ret += ')' ;
   }
   else {
     ret = s ;
   }
   return ret;

} ;




/*-------------------------------------------------------------------------*/
/*   getRGBA()  Returns an rgba(rr,gg,bb,aa) string from a color string    */
/*              specification such as #rr[gg[bb[aa]]], rgb(...), rgba(...) */
/*-------------------------------------------------------------------------*/
/**
  *   Returns an "rgba(r,g,b,a)"  color string from a supplied "#rrggbb" or
  *   extended middle-tier "#aarrggbb" string.  Will also accept an rgb(r,g,b)
  *   string (in which case the implied alpha is 1).  If an "rgba(r,g,b,a)" is
  *   supplied, the same object will be returned.  (See also {@link DvtColorUtils#makePound}.)
  *   @param {String} s  A color string specification.
  *   @type String.
  */
DvtColorUtils.getRGBA = function(s)
{
   var ret ;
   
   // If clr is a named color, then convert into usable format
   var namedColor = DvtColorUtils.getColorFromName(s);
   if(namedColor)
     s = namedColor;

   if (s.charAt(0) === '#') {
     ret = DvtColorUtils._RGBA ;

     var len = s.length ;

     if (len > 7) {             // alpha specified (#aarrggbb)
       ret += parseInt(s.substring(3, 5),16) + ","  +  parseInt(s.substring(5,7),16) + "," +
              parseInt(s.substring(7,9),16)  + ","  + (parseInt(s.substring(1, 3),16)/255)  ;
     }
     else if (len === 7) {      // alpha not specified (#rrggbb)  - alpha of 1 is assumed
       ret += parseInt(s.substring(1, 3),16) + "," + parseInt(s.substring(3,5),16) + "," + 
              parseInt(s.substr(5),16)  + ",1" ;
     }
     else if (len === 5) {      // #rrgg
         ret += parseInt(s.substring(1, 3),16) + "," + parseInt(s.substring(3,5),16) + ",0,1" ;
     }
     else if (len === 4) {      // #rgb
         var r = s.substring(1, 2);
             r += r ;
         var g = s.substring(2, 3);
             g += g ;
         var b = s.substring(3);
             b += b ;
             
         ret += parseInt(r,16) + "," + parseInt(g,16) + "," + parseInt(b, 16) + ",1" ;
     }
     else if (len === 3) {      // #rr
         ret += parseInt(s.substring(1, 3),16) + ",0,0,1" ;
     }
     
     ret += ')' ;
   }
   else if (s.substr(0,4) === DvtColorUtils._RGB) {
     ret = DvtColorUtils._RGBA ;
     ret += s.substring(4, s.length -1) + ',1)';
   }
   else {
     ret = s ;
   }
   return ret;

} ;


/*-------------------------------------------------------------------------*/
/*  _setChannel()     Replaces a channel in an rgb or rgba string.  (Note: */
/*                    if an alpha channel is added and an rgb string was   */
/*                    supplied, the string is changed to an rgba string.)  */
/*-------------------------------------------------------------------------*/
/**
  *  Returns a new color String with the specifed channel value set.
  *  If an alpha channel is requested and a # or rgb string is supplied,
  *  an rgba(...) string is returned.
  *  @private
  *  @param {String} s     A color specification to be changed.
  *  @param {number} chan  The channel to be changed.
  *  @param {number} chval The new channel value (as a decimal number).
  *  @type {String}
  */
DvtColorUtils._setChannel = function(s, chan, chval)
{
   if ((chan === undefined) || (chval === undefined) || (chan < DvtColorUtils._RED) || (chan > DvtColorUtils._ALPHA)) {
     return s ;
   }
   
   // If named color, then convert into usable format
   var namedColor = DvtColorUtils.getColorFromName(s);
   if(namedColor)
     s = namedColor;

   var  ar ;
   var  bPound = (s.charAt(0) === '#') ;
   var  bRGBA ;
   var ret ;
   if (bPound) {
     ar = [] ;
     ar.push(parseInt(s.substr(1,2), 16)) ;
     ar.push(parseInt(s.substr(3,2), 16)) ;
     ar.push(parseInt(s.substr(5,2), 16)) ;
     if (chan === DvtColorUtils._ALPHA) {
       ar.push(chval) ;
       bPound = false ;
       bRGBA  = true ;
     }
   } 
   else {
     var  bRGBA = (s.substr(0,5) === DvtColorUtils._RGBA);
     var x1     = s.indexOf('(') ;
     var x2     = s.indexOf(')') ;
     var ar     = s.substring(x1+1, x2).split(',') ;

     if ((! bRGBA) && chan === DvtColorUtils._ALPHA) {
       ar.push(chval) ;
       bRGBA = true ;
     }
   }

   ar[chan] = chval ;

   if (bPound) {
     ret = DvtColorUtils.makePound(ar[0],ar[1],ar[2]) ;
   }
   else {
      ret = (bRGBA?  DvtColorUtils.makeRGBA(ar[0],ar[1],ar[2], ar[3]) :
                     DvtColorUtils.makeRGB(ar[0],ar[1],ar[2])) ;
   }
   return ret ;
   
} ;


/*-------------------------------------------------------------------------*/
/*   setRGB()                                                              */
/*-------------------------------------------------------------------------*/
/**
  *  Returns a string of the same format as the input string, with the red, green,
  *  and blue channels replaced.
  *  @param {String}  s  The input string.
  *  @param {number}  r  The red value as a decimal number. 
  *  @param {number}  g  The green value as a decimal number. 
  *  @param {number}  b  The blue value as a decimal number. 
  *  @type String
  *  @returns A new color string of the same format as the input string, with
  *  the new color values.
  */
DvtColorUtils.setRGB = function(s, r, g, b)
{
   b = ((b === null || isNaN(b)) ? 0 : b);
   g = ((g === null || isNaN(g)) ? 0 : g);
   r = ((r === null || isNaN(r)) ? 0 : r);

   if (s.charAt(0) === '#')
   {
      return DvtColorUtils.makePound(r, g, b);
   }

   var  bRGBA = (s.substr(0,5) === DvtColorUtils._RGBA);

   var ret = (bRGBA? DvtColorUtils._RGBA : DvtColorUtils._RGB) + r + ',' + g + ',' + b ;

   if (bRGBA) {
     var x1 = s.lastIndexOf(5,',') ;
     var x2 = s.indexOf(')') ;

     ret += ',' + s.substring(x1+1, x2) ;
   }
   ret += ')' ;
   return ret ;
} ;




/**
  * Returns a brighter color of the supplied color based on a percentage factor.
  * @param {String} color   A color specification.
  * @param {number} factor  An optional percentage by which the color is to be brightened
  *                         lightened (0 returns unchanged) specified as a decimal
  *                         (e.g. 25% = 0.25).  If omitted, a default percentage of
  *                         15% (i.e 0.15) is applied.
  * @type String
  * @returns  A new color string brightened by the factor <code>uint</code> containing the new color value.
  */
DvtColorUtils.getBrighter = function(color, factor)
{
   var  r = DvtColorUtils._getChannel(color, DvtColorUtils._RED);
   var  g = DvtColorUtils._getChannel(color, DvtColorUtils._GREEN);
   var  b = DvtColorUtils._getChannel(color, DvtColorUtils._BLUE);
   var  a = DvtColorUtils._getChannel(color, DvtColorUtils._ALPHA);

   if (! factor) {
     factor = DvtColorUtils._FACTOR ;    // use default factor
   }

   // From 2D group:
   // 1. black.brighter() should return grey
   // 2. applying brighter to blue will always return blue, brighter
   // 3. non pure color (non zero rgb) will eventually return white
   
   var i = parseInt(1.0 / (1.0 - factor));
   if (r === 0 && g === 0 && b ===0) {
     //if factor=.5, then this only creates a color of rgb(2,2,2),
     //which is still black, so instead, just use the factor times white
     //return DvtColorUtils.makeRGBA(i, i, i, a);
     var newI = parseInt(255 * factor);
     return DvtColorUtils.makeRGBA(newI, newI, newI, a);
   }
   
   if (r > 0 && r < i) {
     r = i;
   }
   if (g > 0 && g < i) {
     g = i;
   }
   if (b > 0 && b < i) {
     b = i;
   }
   
   r = Math.min(parseInt(r / factor), 255);
   g = Math.min(parseInt(g / factor), 255);
   b = Math.min(parseInt(b / factor), 255);

   return DvtColorUtils.makeRGBA(r, g, b, a);
};
 
/**
 * Returns the hsl values for a color with the given rgb.
 * @param {number} r The r value, on a scale from 0 to 255
 * @param {number} g The g value, on a scale from 0 to 255
 * @param {number} b The b value, on a scale from 0 to 255
 * @return {object} The object containing the h, s, and l fields.  The h field is on a scale from 0 to 360, and the s 
 *                  and l fields are on a scale from 0 to 1.
 */
DvtColorUtils.rgb2hsl = function(r, g, b) {
  // Scale rgb to be from 0 to 1
  r /= 255;
  g /= 255;
  b /= 255;
  
  var max = Math.max(r, g, b)
  var min = Math.min(r, g, b);
  var sum = max + min;
  
  var h, s;
  var l = sum/2;

  if (max == min) {
    h = 0;
    s = 0;
  }
  else {
    var diff = max - min;
    s = l > 0.5 ? diff/(2 - sum) : diff/sum;
    
    if(max == r)
      h = (g - b)/diff + (g < b ? 6 : 0);
    else if(max == g)
      h = (b - r)/diff+2;
    else if(max == b)
      h = (r - g)/diff+4;
    
    h /= 6;
  }

  return {h:h*360, s:s, l:l};
}

/**
 * Returns the rgb values for a color with the given hsl.
 * @param {number} h The h value, on a scale from 0 to 360
 * @param {number} s The s value, on a scale from 0 to 1
 * @param {number} l The l value, on a scale from 0 to 1
 * @return {object} The object containing the r, g, and b fields on a scale from 0 to 255.
 */
DvtColorUtils.hsl2rgb = function(h, s, l) {
  // Scale h to be from 0 to 1
  h /= 360;

  var r, g, b;
  if (s == 0)
    r = g = b = l;
  else {
    var q = l < 0.5 ? l * (1+s) : l + s - (l*s);
    var p = 2 * l - q;
    r = DvtColorUtils._hue2rgb(p, q, h + 1/3);
    g = DvtColorUtils._hue2rgb(p, q, h);
    b = DvtColorUtils._hue2rgb(p, q, h - 1/3);
  }

  return {r: r * 255, g: g * 255, b: b * 255};
}

/**
 * @private
 */
DvtColorUtils._hue2rgb = function(p, q, t) {
  if (t < 0)
    t += 1;
  else if (t > 1)
    t -= 1;
    
  if (t < 1/6)
    return p + (q-p) * 6 * t;
  else if (t < 1 / 2)
    return q;
  else if (t < 2 / 3)
    return p + (q-p) * (2/3 - t) * 6;
  else 
    return p;
}

/**
  * Converts an HSV color to RGB.
  * 
  * @param (number) hue hue of the HSV color
  * @param (number) sat saturation of the HSV color
  * @param (number) val value of the HSV color
  * 
  * @type object
  * @returns an object defining r,g,b for the color
  */
DvtColorUtils.hsv2rgb = function(hue, sat, val)
{
  var red;
  var grn;
  var blu; 
  var i; 
  var f; 
  var p; 
  var q; 
  var t;
  
  hue %= 360;
  if (val == 0)
  {
    return (
    {
      r : 0, g : 0, v : 0
    });
  }
  sat /= 100;
  val /= 100;
  hue /= 60;
  i = Math.floor(hue);
  f = hue - i;
  p = val * (1 - sat);
  q = val * (1 - (sat * f));
  t = val * (1 - (sat * (1 - f)));
  if (i == 0)
  {
    red = val;
    grn = t;
    blu = p;
  }
  else if (i == 1)
  {
    red = q;
    grn = val;
    blu = p;
  }
  else if (i == 2)
  {
    red = p;
    grn = val;
    blu = t;
  }
  else if (i == 3)
  {
    red = p;
    grn = q;
    blu = val;
  }
  else if (i == 4)
  {
    red = t;
    grn = p;
    blu = val;
  }
  else if (i == 5)
  {
    red = val;
    grn = p;
    blu = q;
  }
  red = Math.floor(red * 255);
  grn = Math.floor(grn * 255);
  blu = Math.floor(blu * 255);
  return ({r : red, g : grn, b : blu});
}



/**
  * Converts an RGB color to HSV.
  * 
  * @param (number) red red of the RGB color
  * @param (number) grn green of the RGB color
  * @param (number) blu blue of the RGB color
  * 
  * @type object
  * @returns an object defining h,s,v for the color
  */
DvtColorUtils.rgb2hsv = function(red, grn, blu)
{
  var x;
  var val;
  var f;
  var i;
  var hue;
  var sat;
  
  red /= 255;
  grn /= 255;
  blu /= 255;
  x = Math.min(red, grn, blu);
  val = Math.max(red, grn, blu);
  if (x == val)
  {
    return ({h : undefined, s : 0, v : val * 100});
  }
  f = (red == x) ? grn - blu : ((grn == x) ? blu - red : red - grn);
  i = (red == x) ? 3 : ((grn == x) ? 5 : 1);
  hue = Math.floor((i - f / (val - x)) * 60) % 360;
  sat = Math.floor(((val - x) / val) * 100);
  val = Math.floor(val * 100);
  return ({h : hue, s : sat, v : val});
};

 

/**
  * Returns a pastel color using the supplied color based on a ratio.
  *
  *
  * @param (String) color  A color specification.
  * @param (number) factor An optional percentage by which to apply the pastel effect (0 returns unchanged) 
  *                        specified as a decimal (e.g., 25% = 0.25).  If omitted, the default percentage of 
  *                        15% (i.e., 0.15) is applied.
  *
  * @return A <code>uint</code> containing the new color value.
  */
DvtColorUtils.getPastel = function(color, factor)
{
  // TDO
  var a = DvtColorUtils._getChannel(color, DvtColorUtils._ALPHA) ;
  var r = DvtColorUtils._getChannel(color, DvtColorUtils._RED) ;
  var g = DvtColorUtils._getChannel(color, DvtColorUtils._GREEN) ;
  var b = DvtColorUtils._getChannel(color, DvtColorUtils._BLUE) ; ;

  var gR = Math.min(r + parseInt((255 - r) * factor), 255) ;
  var gG = Math.min(g + parseInt((255 - g) * factor), 255) ;
  var gB = Math.min(b + parseInt((255 - b) * factor), 255) ;
   
  return DvtColorUtils.makeRGBA( gR, gG, gB, a ) ;    
     
};

/**
 * Returns a color whose lightness has been adjusted by the specified amount.
 * @param {string} color The original color.
 * @param {number} dh The change in hue.
 * @param {number} ds The change in saturation.
 * @param {number} dl The change in lightness.
 * @return {string} The adjusted color.
 */
DvtColorUtils.adjustHSL = function(color, dh, ds, dl) 
{
  // Break down in rgb
  var r = DvtColorUtils._getChannel(color, DvtColorUtils._RED) ;
  var g = DvtColorUtils._getChannel(color, DvtColorUtils._GREEN) ;
  var b = DvtColorUtils._getChannel(color, DvtColorUtils._BLUE) ;
    
  // Get the color as HSL
  var hslColor = DvtColorUtils.rgb2hsl(r, g, b);
  
  // Add the changes, bounded to the min and max for each value
  var h = Math.min(Math.max(0, hslColor.h + dh), 360);
  var s = Math.min(Math.max(0, hslColor.s + ds), 1);
  var l = Math.min(Math.max(0, hslColor.l + dl), 1);
  
  // Convert back to rgba and return
  var rgbColor = DvtColorUtils.hsl2rgb(h, s, l);
  return DvtColorUtils.makePound(rgbColor.r, rgbColor.g, rgbColor.b);
}

/**
  * Interpolate a color between the original and destination values for the 
  * given percent.
  * @param  origVal  original color value, a string
  * @param  destVal  destination color value, a string
  * @param {number}  percent  percent value to interpolate
  */
DvtColorUtils.interpolateColor = function(origVal, destVal, percent)
{
    var oldR = DvtColorUtils.getRed(origVal);
    var oldG = DvtColorUtils.getGreen(origVal);
    var oldB = DvtColorUtils.getBlue(origVal);
    var oldA = DvtColorUtils.getAlpha(origVal);

    var destR = DvtColorUtils.getRed(destVal);
    var destG = DvtColorUtils.getGreen(destVal);
    var destB = DvtColorUtils.getBlue(destVal);
    var destA = DvtColorUtils.getAlpha(destVal);

    var newR = Math.round(DvtMath.interpolateNumber(oldR, destR, percent));
    var newG = Math.round(DvtMath.interpolateNumber(oldG, destG, percent));
    var newB = Math.round(DvtMath.interpolateNumber(oldB, destB, percent));
    var newA = Math.round(DvtMath.interpolateNumber(oldA, destA, percent));

    return DvtColorUtils.makeRGBA(newR, newG, newB, newA);
};

DvtColorUtils.inferColor = function(baseColor1, baseColor2, newColor1) {
  if (baseColor1 == baseColor2) {
    return newColor1;
  }

  if (newColor1 == baseColor1) {
    return baseColor2;
  }

  var red;
  var divisor = DvtColorUtils.getRed(baseColor1);
  var dividend = DvtColorUtils.getRed(baseColor2);
  if (divisor == dividend) {
    red = DvtColorUtils.getRed(newColor1);
  }
  else {
    if (divisor == 0) {
      divisor = 1;
    }
    red = dividend / divisor * DvtColorUtils.getRed(newColor1);
  }
  var green;
  divisor = DvtColorUtils.getGreen(baseColor1);
  dividend = DvtColorUtils.getGreen(baseColor2);
  if (divisor == dividend) {     
    green = DvtColorUtils.getGreen(newColor1);
  }
  else {
    if (divisor == 0) {
      divisor = 1;
    }
    green = dividend / divisor * DvtColorUtils.getGreen(newColor1);
  }
  var blue;
  divisor = DvtColorUtils.getBlue(baseColor1);
  dividend = DvtColorUtils.getBlue(baseColor2);
  if (divisor == dividend) {
    blue = DvtColorUtils.getBlue(newColor1);
  }
  else {
    if (divisor == 0) {
      divisor = 1;
    }
    blue = dividend / divisor * DvtColorUtils.getBlue(newColor1);
  }
  //return (0xffffff & (red << 16)) + (0xffff & (green << 8)) + (0xff & blue);
  return DvtColorUtils.makePound(red, green, blue);
}

/**
 * Returns a contrasting text color for the specified background color.
 * @param {string} backgroundColor The background color.
 * @return {string} A constrasting color for use on text.
 */
DvtColorUtils.getContrastingTextColor = function(backgroundColor) {
  var r = DvtColorUtils.getRed(backgroundColor);
  var g = DvtColorUtils.getGreen(backgroundColor);
  var b = DvtColorUtils.getBlue(backgroundColor);
  var yiq = (r*299 + g*587 + b*114)/1000;
  return (yiq >= 128) ? "#000000" : "#FFFFFF";
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.

/**
  *  @class DvtGraphClipPath
  *  Defines a clipping region composed of the union of one or more outlines.
  *  @param {String} prefix  An optional prefix for the clip path id.
  *  @constructor
  */
var DvtGraphClipPath = function(prefix)
{
   /** Id of the clip path. @type String  */
   this._id = (prefix ? prefix+'$' : '') + 'cp' + DvtGraphClipPath._uniqueSeed++;
   /** the clipping outline  @type Object  */
   this._regions  = [] ;
} ;

DvtObj.createSubclass(DvtGraphClipPath, DvtObj, "DvtGraphClipPath");

DvtGraphClipPath._uniqueSeed = 0;

//  Clipping path outlines

/**  @final  @type number  */
DvtGraphClipPath.NONE    = 0 ;
/**  @final  @type number  */
DvtGraphClipPath.RECT    = 1 ;
/**  @final  @type number  */
DvtGraphClipPath.PATH    = 2 ;
/**  @final  @type number  */
DvtGraphClipPath.POLYGON = 3 ;
/**  @final  @type number  */
DvtGraphClipPath.ELLIPSE = 4 ;
/**  @final  @type number  */
DvtGraphClipPath.CIRCLE = 5 ;

/**
  *   @private
  */
DvtGraphClipPath.prototype._addRegion = function(obj)
{
   if (obj)
    this._regions.push(obj);
};


/**
  *   Returns the ID of the clip path
  *   @returns {String}  The ID of the clip path
  */ 

DvtGraphClipPath.prototype.getId = function()
{
   return this._id ;
};

/**
 *   Returns a  clipping region outline object (by index).
 *   @type Object
 */

DvtGraphClipPath.prototype.getRegions = function(idx)
{
  return this._regions ;
};

/**
  *  Adds a rectangular clipping region to the clip path.
  *  @param {number}  x   The top left x position of the rectangular region.
  *  @param {number}  y   The top left y position of the rectangular region.
  *  @param {number}  w   The width of the rectangular region.
  *  @param {number}  h   The height of the rectangular region.
  *  @param {number}  rx  Optional x-axis radius of the ellipse used to round off the cornders of the rectangle.
  *  @param {number}  ry  Optional y-axis radius of the ellipse used to round off the cornders of the rectangle.
  */ 
DvtGraphClipPath.prototype.addRect = function(x, y, w, h, rx, ry)
{
   var obj  = {} ;
   obj.type = DvtGraphClipPath.RECT ;
   obj.x    = x ;
   obj.y    = y ;
   obj.w    = w ;
   obj.h    = h ;
   obj.rx   = rx ;
   obj.ry   = ry ;
   this._addRegion(obj);
};

/**
  *  Adds a circular clipping region to the clip path.
  *  @param {number}  cx  The x-axis coordinate of the center of the circle.
  *  @param {number}  cy  The y-axis coordinate of the center of the circle.
  *  @param {number}  r   The radius of the circle.
  */ 
DvtGraphClipPath.prototype.addCircle = function(cx, cy, r)
{
   var obj  = {} ;
   obj.type = DvtGraphClipPath.CIRCLE ;
   obj.cx   = cx ;
   obj.cy   = cy ;
   obj.r    = r ;
   this._addRegion(obj);
};

/**
  *  Adds an ellipse clipping region to the clip path.
  *  @param {number}  cx  The x-axis coordinate of the center of the circle.
  *  @param {number}  cy  The y-axis coordinate of the center of the circle.
  *  @param {number}  rx  The x-axis radius of the ellipse.
  *  @param {number}  ry  The y-axis radius of the ellipse.
  */ 
DvtGraphClipPath.prototype.addEllipse = function(cx, cy, rx, ry)
{
  var obj  = {} ;
   obj.type = DvtGraphClipPath.ELLIPSE ;
   obj.cx   = cx ;
   obj.cy   = cy ;
   obj.rx   = rx ;
   obj.ry   = ry ;
   this._addRegion(obj);
};

/**
  *  Adds a polygon clipping region to the clip path.
  *  @param {string}  points  The points that make up the polygon.
  */ 
DvtGraphClipPath.prototype.addPolygon = function(points)
{
  var obj  = {} ;
  obj.type = DvtGraphClipPath.POLYGON ;
  obj.points   = points ;
  this._addRegion(obj);
};

/**
  *  Adds a path clipping region to the clip path.
  *  @param {string}  d  Path data.
  */ 
DvtGraphClipPath.prototype.addPath = function(d)
{
  var obj  = {} ;
  obj.type = DvtGraphClipPath.PATH ;
  obj.d   = d ;
  this._addRegion(obj);
};
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*   DvtFill                                                               */
/*-------------------------------------------------------------------------*/
/**
  * A base class for shape fills. DvtFill is intended to be subclassed (e.g.
  * {@link DvtSolidFill}).
  * @extends DvtBaseDrawEffect
  * @class DvtFill  is a base class for shape fill specifications.  DvtFill is intended to be subclassed (e.g.
  * {@link DvtSolidFill}).
  * @constructor  Do not create/use directly.  
  * @param {String} id  Optional ID for the object (see also {@link DvtBaseDrawEffect#setId}). 
  */
var  DvtFill = function()
{
   this._Init() ;
};

DvtObj.createSubclass(DvtFill, DvtLockable, "DvtFill");


//  Fill type definitions

/**   @final @type number  */
DvtFill.NONE     = 0 ;            // No fill specified.
/**   @final @type number  */
DvtFill.COLOR    = 1 ;            // Solid color fill. 
/**   @final @type number  */
DvtFill.GRADIENT = 2 ;            // Gradient fill.
/**   @final @type number  */
DvtFill.PATTERN  = 3 ;            // Pattern fill.
/**   @final @type number  */
DvtFill.IMAGE    = 4 ;            // Image fill.

/** 
 * @private 
 */
DvtFill.prototype._Init = function() {
  DvtFill.superclass._Init.call(this) ;
}

/**
 *   Returns the id of this draw effect.
 *   @type String
 */
DvtFill.prototype.getId = function () {
  return this._id;
}

/**
 *   Sets the id of this fill.
 *   @param {String} id  The id for the fill.
 */
DvtFill.prototype.setId = function (id) {
  this._id = id;
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*   DvtFont                                                           */
/*---------------------------------------------------------------------*/
/**
  * Encapsulates font properties.
  * @extends DvtObj
  * @class DvtFont
  * @constructor
 */
var  DvtFont = function(size, color, alpha, name, bold, italic, underline)
{
   this._Init(size, color, alpha, name, bold, italic, underline) ;
};

DvtObj.createSubclass(DvtFont, DvtObj, "DvtFont") ;



/*---------------------------------------------------------------------*/
/*   _Init()                                                           */
/*---------------------------------------------------------------------*/
/** @private */
DvtFont.prototype._Init = function(size, color, alpha, name, bold, italic, underline)
{
   this.size      = ((size === null || isNaN(size)) ? 12 : size) ;
   this.color     = (color ? color : '#000000') ;
   this.alpha     = ((alpha === null || isNaN(alpha)) ? 1 : alpha) ;
   this.name      = name;
   this.bold      = (bold || false) ;
   this.italic    = (italic || false) ;
   this.underline = (underline || false) ;
};




/*---------------------------------------------------------------------*/
/*   property getters                                                  */
/*---------------------------------------------------------------------*/
/**
  * Returns the font size
  * @type number
  */
DvtFont.prototype.getSize = function()
{
   return this.size ;
} ;

/**
  * Returns the font alpha
  * @type number
  */
DvtFont.prototype.getAlpha = function()
{
   return this.alpha ;
};

/**
  * Returns the font color
  * @type String
  */
DvtFont.prototype.getColor = function()
{
   return this.color ;
};
/**
  * Returns the font name
  * @type String
  */
DvtFont.prototype.getName = function()
{
   return this.name ;
} ;
/**
  * Returns true if bold text is required.
  * @type boolean
  */
DvtFont.prototype.isBold = function()
{
   return this.bold ;
} ;
/**
  * Returns true if italic text is required.
  * @type boolean
  */
DvtFont.prototype.isItalic = function()
{
   return this.italic ;
} ;
/**
  * Returns true if underlined text is required.
  * @type boolean
  */
DvtFont.prototype.isUnderline = function()
{
   return this.underline ;
};

/**
 * @return {DvtCSSStyle} The CSS style attributes associated with this DvtFont
 */
DvtFont.prototype.getCSSStyle = function()
{
  var style = new DvtCSSStyle();

  style.setStyle(DvtCSSStyle.FONT_FAMILY, this.getName());
  style.setStyle(DvtCSSStyle.COLOR, this.getColor());
  style.setStyle(DvtCSSStyle.FONT_SIZE, this.getSize());

  if (this.isBold())
    style.setStyle(DvtCSSStyle.FONT_WEIGHT, "bold");

  if (this.isItalic())
    style.setStyle(DvtCSSStyle.FONT_STYLE, "italic");

  if (this.isUnderline())
    style.setStyle(DvtCSSStyle.TEXT_DECORATION, "underline");
  
  return style;
};

// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*   DvtSolidFill                                                          */
/*-------------------------------------------------------------------------*/
/**
  * A shape fill class rendering a solid fill color.
  * @extends DvtFill
  * @class DvtSolidFill
  * @constructor
  * @param {String} fc  A css color specification for the fill color.  If omitted,
  * a transparent fill is created.
  * @param {String} fa  An optional alpha. Can be used to supplement the alpha
  * specified (or implied) by the fill color.  If omitted, the alpha specifed
  * by the fill color (or 1 if it does not specify an alpha) is used.
  * @param {String} id  Optional ID for the object (see also {@link DvtBaseDrawEffect#setId}) 
 */
var  DvtSolidFill = function(fc, fa)
{
   this._Init(fc, fa) ;
};

DvtObj.createSubclass(DvtSolidFill, DvtFill, "DvtSolidFill") ;

DvtSolidFill._INVISIBLE_FILL = null;

/**
  *  Returns an (unlocked) copy of this object.
  *  @type DvtSolidFill
  */
DvtSolidFill.prototype.clone = function()
{
   var o = new DvtSolidFill() ;

   this.mergeProps(o) ;    // merge properties into cloned obj.

   return o ;
};




/*-------------------------------------------------------------------------*/
/*   _Init()                                                               */
/*-------------------------------------------------------------------------*/

/** @private */
DvtSolidFill.prototype._Init = function(fc, fa)
{
   DvtSolidFill.superclass._Init.call(this) ;

   if (! fc) {
     fc = 'none' ;
   }

   if (fa === undefined || fa === null) {
     fa = DvtColorUtils.getAlpha(fc) ;
     fc = DvtColorUtils.getRGB(fc);
   }

   this._fc  = fc ;
   this._fa  = fa ;
};


/*-------------------------------------------------------------------------*/
/*   get/setColor()           Gets/Sets the fill color                     */
/*-------------------------------------------------------------------------*/
/**
  *  Returns the fill's solid color (and alpha).
  *  @type String
  */
DvtSolidFill.prototype.getColor = function()
{
   return  this._fc ;
} ;

/**
  *  Sets the fill color.
  *  @param  {String} fc  A color specification. 
  *  e.g.  #123456, &nbsp; rgb(128,42,200), &nbsp; rgba(28, 128, 56, 0.7)
  *  &nbsp;or &nbsp;'green'&nbsp; or&nbsp; 'none'
  */
DvtSolidFill.prototype.setColor = function(fc)
{
   if (! this.isLocked()) {
     this._fc = fc ;
   }
} ;


/*-------------------------------------------------------------------------*/
/*   get/setAlpha()                                                        */
/*-------------------------------------------------------------------------*/
/**
  *  Returns the fill's solid color alpha channel value in the range 0 (invisible) to 1 (opaque).
  *  @type number
  */
DvtSolidFill.prototype.getAlpha = function()
{
   return this._fa ;
};

/**
  *  Sets the fill alpha.
  *  @param {number} alpha  A value between 0 (invisible) and 1 (opaque).
  */
DvtSolidFill.prototype.setAlpha = function(alpha)
{
   if (! this.isLocked()) {
     this._fa = alpha ;
   }
};


/*-------------------------------------------------------------------------*/
/*   mergeProps()                                                          */
/*-------------------------------------------------------------------------*/
/**
  *   Merges the fill properties in the current object into the supplied fill
  *   object.
  *   Used internally by clone().
  *   @private
  */
DvtSolidFill.prototype.mergeProps = function(obj)
{
   DvtSolidFill.superclass.mergeProps.call(this, obj) ;   // merge in subclass props

   obj._fc  = this._fc ;   // color
   obj._fa  = this._fa ;   // alpha
};

/**
 * Returns an instance of DvtSolidFill that is invisible and can be used for event detection.
 * @return {DvtSolidFill}
 */
DvtSolidFill.invisibleFill = function() {
  if (!DvtSolidFill._INVISIBLE_FILL) 
    DvtSolidFill._INVISIBLE_FILL = new DvtSolidFill("#FFFFFF", 0);
  return DvtSolidFill._INVISIBLE_FILL;
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*   DvtStroke                 Base stroke properties                      */
/*-------------------------------------------------------------------------*/
/**
  * An abstract base class representing stroke properties.  
  * @class DvtStroke
  * @extends DvtObj
  * @constructor
  * @param {number} sw  The width of the stroke line.  If omitted, default is 1.
  * @param {String} id  Optional ID for the object (see also {@link DvtBaseDrawEffect#setId}). 
  */
var  DvtStroke = function(sw, id)
{
   this._Init(sw, id) ;
};

DvtObj.createSubclass(DvtStroke, DvtLockable, "DvtStroke");

/**
  * Stroke Types
  */
DvtStroke.SOLID         = 0 ;
DvtStroke.DASHED        = 1 ;
DvtStroke.DOTTED        = 2 ;
DvtStroke.DASHED_DOTTED = 3 ;

/**
  * Stroke joins
  */
DvtStroke.SQUARE = 'square' ;
DvtStroke.ROUND  = 'round' ;
DvtStroke.BEVEL  = 'bevel' ;
DvtStroke.MITER  = 'miter';

/**
  * Stroke endings
  */
//DvtSTROKE.SQUARE  ;     // same as for join
//DvtSTROKE.ROUND   ;     //  ..   .  ..  ..
DvtStroke.BUTT    = 'butt' ;

/**
 * Converts the specified stroke type string to its constant value.
 * @param {string} shape The stroke type.
 * @return {number} The corresponding constant value.
 */
DvtStroke.convertTypeString = function(type) {
  if(type == "solid")
    return DvtStroke.SOLID;
  else if(type == "dashed")
    return DvtStroke.DASHED;
  else if(type == "dotted")
    return DvtStroke.DOTTED;
  else
    return DvtStroke.SOLID;
}




/*-------------------------------------------------------------------------*/
/*   _Init()                                                               */
/*-------------------------------------------------------------------------*/
/** @private */
DvtStroke.prototype._Init = function(sw)
{
   this._sw   = ((sw === null || isNaN(sw)) ? 1 : sw);    // stroke_width

   DvtStroke.superclass._Init.call(this) ;
   this._bFixedWidth = false;
};

/**
 *   Returns the id of this draw effect.
 *   @type String
 */
DvtStroke.prototype.getId = function () {
  return this._id;
}

/**
 *   Sets the id of this fill.
 *   @param {String} id  The id for the fill.
 */
DvtStroke.prototype.setId = function (id) {
  this._id = id;
}


/*-------------------------------------------------------------------------*/
/*   mergeProps()                                                          */
/*-------------------------------------------------------------------------*/

DvtStroke.prototype.mergeProps = function(to)
{
   to._sw   = this._sw ;      // stroke width
   to._st   = this._st ;      // stroke type (solid, dashed, etc)
   to._sd   = this._sd ;      // dash details 
   to._sdo  = this._sdo;      // dash offset
   to._sj   = this._sj ;      // line join style
   to._se   = this._se ;      // line ending style
   to._sm   = this._sm ;      // miter limit
   to._bFixedWidth = this._bFixedWidth ;

   DvtStroke.superclass.mergeProps.call(this, to) ;
} ;


/*-------------------------------------------------------------------------*/
/*   setStyle()                                                            */
/*-------------------------------------------------------------------------*/
/**
  *  Sets the stroke style (e.g solid line, or intermittent line, and optionally
  *  the stroke join type and the line ending type). Null may be used to ignore
  *  any argument.
  *  <p>
  *  Dashed line examples :<br><br><code>   
  *  // 3 pixel dash followed by a 3 pixel gap<br><br>
  *  obj.setStyle(DvtStroke.DASHED, "3");<br><br>
  *  // 5 pixel dash, 3 pixel gap, 9 pixel dash, 2 pixel gap.  The pattern then repeats
  *  to fill the line<br><br>
  *  obj.setStyle(DvtStroke.DASHED, "5,3,9,2");
  *
  *  @param {number} type  DvtStroke.SOLID or DvtStroke.DASHED.
  *  @param {String} dashsize  For dashed lines, specifies the dash and space size.
  *                            The string contains a list of numbers separated by commas
  *                            or whitespace, specifying dash length and gaps. The list
  *                            should have an even number of entries, but if an odd number
  *                            is used the list will be repeated so that the entry count is even.
  *  @param {String} join  Specifies the type of line join.  May be DvtStroke.SQUARE,
  *                        DvtStroke.ROUND, DvtStroke.BEVEL, or DvtStroke.MITER.
  *  @param {String} end   Specifies the type of line end.  May be DvtSTROKE.SQUARE,
  *                        DvtSTROKE.ROUND, or DvtStroke.BUTT.
  *  @param {number} miter miter limit when join is DvtStroke.MITER
  */
DvtStroke.prototype.setStyle = function(type, dashsize, join, end, miter)
{
   if (this.isLocked()) {
     return ;
   }

   //  Line-type attributes

   if (type && !dashsize)
     dashsize = DvtStroke.getDefaultDash(type, this._sw);

   this._st = type ;
   this._sd = dashsize ;
   

   //  Line-joining attributes

   if (join) {
     this._sj = join ;
   } 

   //  Line-ending attributes

   if (end) {
     this._se = end ;
   } 

   //  miter limit attributes

   if (miter) {
     this._sm = miter ;
   } 

} ;


DvtStroke.prototype.getDash = function()
{
   return this._sd ;
};

DvtStroke.prototype.getDashOffset = function()
{
   return this._sdo ;
};


DvtStroke.prototype.getLineJoin = function()
{
   return this._sj ;
};

DvtStroke.prototype.setLineJoin = function(join)
{
   if (! this.isLocked()) {
     this._sj = join ;
   }
};

DvtStroke.prototype.getLineEnd = function()
{
   return this._se ;
};

DvtStroke.prototype.setLineEnd = function(end)
{
   if (! this.isLocked()) {
     this._se = end ;
   }
};

DvtStroke.prototype.getMiterLimit = function()
{
   return this._sm ;
};

DvtStroke.prototype.setMiterLimit = function(limit)
{
   if (! this.isLocked()) {
     this._sm = limit ;
   }
};


/*-------------------------------------------------------------------------*/
/*   get/setType()     Gets/Sets the type of stroke (e.g. solid or dashed  */
/*-------------------------------------------------------------------------*/

DvtStroke.prototype.getType = function()
{
   return this._st ;
};

//  dash size is optional.  May also specify alternating sizes  e.g. "3, 5, 3"
/**
  *  Sets the type of the stroke.
  *  @param {number} type  The stroke type, such as {@link DvtStroke#DASHED}. 
  *  @param {number} dashsize  Optional dash details if the stroke type is not {@link DvtStroke#SOLID}.
  *  @param {number} dashoffset  Optional dash offset if the stroke type is not {@link DvtStroke#SOLID}.
  */
DvtStroke.prototype.setType = function(type, dashsize, dashoffset)
{
   if (this.isLocked()) {
     return ;
   }

   if (type === DvtStroke.SOLID) {
     dashsize = null ;
     dashoffset = null;
   }
   else  if (! dashsize) {
     dashsize = DvtStroke.getDefaultDash(type, this._sw);
   }

   this._st  = type ;
   this._sd  = dashsize ;
   this._sdo = dashoffset;

} ;


/*-------------------------------------------------------------------------*/
/*   get/setWidth()                                                        */
/*-------------------------------------------------------------------------*/
/**
  *  Returns the stroke width.
  *  @type number
  */
DvtStroke.prototype.getWidth = function()
{
   return this._sw ;
} ;

/**
  *  Sets the stroke width.
  *  @param {number} sw  The stroke width.
  */
DvtStroke.prototype.setWidth = function(sw)
{
 if (! this.isLocked()) {
   this._sw = sw ;
 }
} ;

DvtStroke.prototype.setFixedWidth = function(bFixedWidth)
{
   this._bFixedWidth = bFixedWidth;
}

DvtStroke.prototype.isFixedWidth = function()
{
   return this._bFixedWidth ;
}

/**
 * Gets a default dash size based on the stroke type and stroke width.
 * @return {string} dash size string, e.g. "6,3,4,3"
 */
DvtStroke.getDefaultDash = function(type, width) {
  width = Math.ceil(width);
  var dashSize = Math.max(6, width * 2); // dash is at least 6px
  var dotSize = Math.max(2, width); // dot is at least 2px
  var dashDotGap = Math.floor((dashSize + dotSize) / 2);
  
  if (type == DvtStroke.DASHED)
    return "" + dashSize;
  if (type == DvtStroke.DOTTED)
    return "" + dotSize;
  if (type == DvtStroke.DASHED_DOTTED)
    return "" + dashSize + "," + dashDotGap + "," + dotSize + "," + dashDotGap; 
  else
    return null;
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------------*/
/*    DvtGradientStroke            Gradient stroke definition class          */
/*---------------------------------------------------------------------------*/
/*  Can be used to share a gradient definition among several shapes, or as a */
/*  convenience to maintain a gradient definition separately from a shape.   */
/*---------------------------------------------------------------------------*/
/**
  *   Creates a gradient specification (shareable by other shapes).
  *   @extends DvtStroke
  *   @class
  *   @constructor
  *   @param {Array} arColors  An array of color specifications (which do not include alpha values).
  *   @param {Array} arAlphas  An optional array of alpha values (between 0 and 1).  If omitted,
  *                            alphas of 1 are assumed.
  *   @param {Array} arStops   An optional array of stop boundary positions (between 0 and 1).
  *                            If omitted, an equal distribution of colors is assumed.
  *   @param {Array} arBounds  An optional bounding box array (x, y, w, h).
  *   @param {number} sw       Stroke width
  *   @param {String} id
  */
var  DvtGradientStroke = function(arColors, arAlphas, arStops, arBounds, sw, id)
{
   this._Init(arColors, arAlphas, arStops, arBounds, sw, id) ;
};


DvtObj.createSubclass(DvtGradientStroke, DvtStroke, "DvtGradientStroke") ;




/**
 * Return the first alpha in the alphas array
 * 
 * @return {number}
 */
DvtGradientStroke.prototype.getAlpha = function()
{
  return 1;
}

/**
  *   Returns an array of alpha's.
  *   @type Array
  */
DvtGradientStroke.prototype.getAlphas = function()
{
   return  this._arAlphas ;
} ;
/**
  *   Returns the bounding box for the gradient as an array (x, y, w, h).
  *   @type Array
  */
DvtGradientStroke.prototype.getBounds = function()
{
   return  this._arBounds ;
} ;

/**
  *   Returns an array of colors.
  *   @type Array
  */
DvtGradientStroke.prototype.getColors = function()
{
   return  this._arColors ;
} ;

/**
  *   Returns an array of stop ratios.
  *   @type Array
  */
DvtGradientStroke.prototype.getStops = function()
{
   return  this._arStops ;
} ;



/*---------------------------------------------------------------------------*/
/*    _Init()                                                                */
/*---------------------------------------------------------------------------*/
/**
  *  @private
  */
DvtGradientStroke.prototype._Init = function(arColors, arAlphas, arStops, arBounds, sw, id)
{
   DvtGradientStroke.superclass._Init.call(this, sw, id) ;

   this._arColors = arColors ;
   this._arBounds = arBounds ;

   var len = 0;
   if (arColors) {
     len = arColors.length - 1;
   }

   if (! arAlphas) {
     arAlphas = DvtGradientStroke.createDefaultAlphas(len);
   }
   this._arAlphas = arAlphas ;

   if (! arStops) {     // if no stops, generate default stops
                        // for a uniform distribution of colors.
      arStops = DvtGradientStroke.createDefaultStops(len) ;
   }

   this._arStops = arStops ;
} ;

/**
 * @param {number} size
 * @return {Array} An array with size number of elements, with each element equal to 1, or null if size 
 *                  is less than 0.
 */
DvtGradientStroke.createDefaultAlphas = function(size)
{
  if(size < 0)
    return null;
    
  var arAlphas = [] ;
  for (var i = 0; i < size; i++) 
  {
    arAlphas.push(1) ;
  }
  return arAlphas;
}

/**
 * Creates a uniformly distributed range of gradient stops
 * 
 * @param {number} numStops
 * @return {Array}
 */
 DvtGradientStroke.createDefaultStops = function(numStops)
 {
  var arStops = [] ;
  var  incr   = 1/numStops ;
  var curStop = 0 ;

  do {
    arStops.push(curStop) ;
    curStop += incr ;
  } while ( --numStops > 0) ;
  arStops.push(1) ;   
  
  return arStops;
 }

/*-------------------------------------------------------------------------*/
/*   mergeProps()                                                          */
/*-------------------------------------------------------------------------*/
/**
  *   Merges the gradient properties in the current object into the supplied gradient object.
  *   Used internally by clone().
  *   @private
  */
DvtGradientStroke.prototype.mergeProps = function(obj)
{
   obj._arColors = this._arColors ;
   obj._arStops  = this._arStops ;
   obj._arAlphas = this._arAlphas ;
   obj._arBounds = this._arBounds ;

   DvtGradientStroke.superclass.mergeProps.call(this, obj) ;
};
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------------*/
/*    DvtLinearGradientStroke       Linear Gradient stroke class             */
/*---------------------------------------------------------------------------*/
/**
  *   Creates a linear gradient stroke.
  *   @extends DvtGradientStroke
  *   @class
  *   <p>
  *   <b>Example usage:</b><br><br><code>
  *
  *   //  Fill rectangle with a left-to-right gradient of red through blue to green.<br> 
  *   //  The colors are evenly graduated because the stop values have been omitted.<br><br> 
  *   rect.setStroke(<b>new DvtLinearGradientStroke(0, ['red', 'green, 'blue']</b>) ;<br><br>
  *
  *   //  Fill rectangle with a north-east direction gradient of red to green
  *   with an unequal gradient distribution of colors.<br><br> 
  *   rect.setStroke(<b>new DvtLinearGradientStroke(45, ['red', 'green], [0, 0.75,1]</b>) ;<br><br>

  *   @constructor
  *   @param {number} angle  Specifies the direction of the gradient as an
  *                          angle in degrees (using the standard anti-clockwise convention
  *                           for positive angles, i.e. 0 = horizontal and 90 = vertically up, etc).
  *   @param {Array} arColors  An array of color specifications (which do not include alpha values).
  *   @param {Array} arColors  An optional array of alpha values (between 0 and 1).  If omitted,
  *                            alphas of 1 are assumed.
  *   @param {Array} arStops   An optional array of stop boundary positions (between 0 and 1).
  *                            If omitted, an equal distribution of colors is assumed.
  *   @param {Array} arBounds  An optional bounding box array (x, y, w, h).
  *   @param {number} sw    An optional number for stroke width. 
  *   @param {String} id
  */
var  DvtLinearGradientStroke = function(angle, arColors, arAlphas, arStops, arBounds, sw, id)
{
   this._Init(angle, arColors, arAlphas, arStops, arBounds, sw, id) ;
};

DvtObj.createSubclass(DvtLinearGradientStroke, DvtGradientStroke, "DvtLinearGradientStroke") ;



/*---------------------------------------------------------------------------*/
/*    clone()                                                                */
/*---------------------------------------------------------------------------*/
/**
  *  Returns an (unlocked) copy of this object.
  *  @type DvtLinearGradientStroke
  */
DvtLinearGradientStroke.prototype.clone = function()
{
   var o = new DvtLinearGradientStroke() ;

   this.mergeProps(o) ;    // merge properties into cloned obj.
   return o ;
};



/*---------------------------------------------------------------------------*/
/*     Init()                                                                */
/*---------------------------------------------------------------------------*/
/**
  *  @private
  */
DvtLinearGradientStroke.prototype._Init = function(angle, arColors, arAlphas, arStops, arBounds, sw, id)
{
   DvtLinearGradientStroke.superclass._Init.call(this, arColors, arAlphas, arStops, arBounds, sw, id) ;

   this._angle = ((angle === null || isNaN(angle)) ? 0 : angle);
} ;



/*---------------------------------------------------------------------------*/
/*   getAngle()    Returns the gradient angle property for linear gradients  */
/*                 see also setAngle().                                      */
/*---------------------------------------------------------------------------*/
/**
  *  Gets the angle of the gradient in degrees.
  *  @type number
  *  @returns The angle of the gradient in degrees.  The zero degree direction is
  *  left-to-right (due east). Positive angles rotate anti-clockwise, and negative
  *  angles rotate clockwise.
  */
DvtLinearGradientStroke.prototype.getAngle = function()
{
    return this._angle ;
} ;


/*-------------------------------------------------------------------------*/
/*   mergeProps()                                                          */
/*-------------------------------------------------------------------------*/
/**
  *   Merges the linear gradient properties in the current object into the
  *   DvtLinearGradientStroke object.
  *   Used internally by clone().
  *   @private
  */
DvtLinearGradientStroke.prototype.mergeProps = function(obj)
{
   DvtLinearGradientStroke.superclass.mergeProps.call(this, obj) ;

   obj._angle  = this._angle ;      // gradient angle
};



/*---------------------------------------------------------------------------*/
/*   setAngle()         Set the gradient angle for linear gradients          */
/*---------------------------------------------------------------------------*/
//  Be default, the gradient angle is zero (due east).  Angles greater
// than zero rotate anti-clockwise.  Angles less than zero rotate clockwise.

/**
  * Sets the angle of the gradient in degrees.  The zero degree direction is
  * left-to-right (due east). Positive angles rotate anti-clockwise, and negative
  * angles rotate clockwise.
  * @param {number} The gradient direction in degrees.
  */
DvtLinearGradientStroke.prototype.setAngle = function(degrees)
{
   if (this.isLocked()) {
     return ;
   }

   if (degrees !== 0) {
     if (Math.abs(degrees) > 360) {
       degrees %= 360 ;
     }
     this._angle = degrees ;
   }
} ;

// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*   DvtSolidStroke            Solid Stroke properties                     */
/*-------------------------------------------------------------------------*/
/**
  * A class representing the stroke properties for a solid line.
  * @class DvtSolidStroke
  * @extends DvtStroke
  * @constructor
  * @param {String} sc  A css color specification for the color (and optionally
  * alpha)of the stroke.
  * @param {number} sa  The stroke alpha.  If the alpha is already specified in
  * the color string, null can be specified. If the alpha is not specified in
  * the color, and this argument is null, the default of 1 will be used. If alpha 
  * is specified and an alpha is also specfied in the color string, then this
  * argument takes precedence.
  * @param {number} sw  The width of the stroke line.  If omitted, the default width is 1.
  * @param {String} id  Optional ID for the object (see also {@link DvtBaseDrawEffect#setId}) 
  */
var  DvtSolidStroke = function(sc, sa, sw, id)
{
   this._Init(sc, sa, sw, id) ;
};

DvtObj.createSubclass(DvtSolidStroke, DvtStroke, "DvtSolidStroke") ;


/*-------------------------------------------------------------------------*/
/*   clone()                                                               */
/*-------------------------------------------------------------------------*/
/**
  *  Returns an (unlocked) copy of this stroke object.
  *  @type DvtSolidStroke
  */
DvtSolidStroke.prototype.clone = function()
{
   var o = new DvtSolidStroke() ;

   this.mergeProps(o) ;    // merge properties into cloned obj.
   return o ;
};







/*-------------------------------------------------------------------------*/
/*   _Init()                                                               */
/*-------------------------------------------------------------------------*/
/** @private */
DvtSolidStroke.prototype._Init = function(sc, sa, sw, id)
{
   this._sc = sc ;
   if (sa !== null && sa !== undefined) {
     this._sa = sa ;
   }
   this._st = DvtStroke.SOLID ;

   DvtSolidStroke.superclass._Init.call(this, sw, id) ;
};


/*-------------------------------------------------------------------------*/
/*   get/setAlpha()                                                        */
/*-------------------------------------------------------------------------*/
/**
  *  Returns the stroke alpha as a value between 0 (invisible) and 1 (opaque).
  *  @type {number}
  */
DvtSolidStroke.prototype.getAlpha = function()
{
   return ((this._sa !== null && this._sa !== undefined) ? this._sa : 1);
};

/**
  *  Sets the stroke alpha.
  *  @param {number} alpha  A value between 0 (invisible) and 1 (opaque).
  */
DvtSolidStroke.prototype.setAlpha = function(alpha)
{
   if (! this.bLocked)
     this._sa = alpha ;
};


/*-------------------------------------------------------------------------*/
/*   get/setColor()                                                        */
/*-------------------------------------------------------------------------*/
/**
  *  Returns the stroke color.
  *  @type String
  */
DvtSolidStroke.prototype.getColor = function()
{
   return this._sc ;
} ;

/**
  *  Sets the stroke color.
  *  @param {String}  sc  A css color specification. e.g.  '#123456',  'rgb(128,42,200)',
  *                       rgba(128,42,200,0.8), or 'green'
  */
DvtSolidStroke.prototype.setColor = function(sc)
{
   if (! this.bLocked)
     this._sc = sc ;
} ;



/*-------------------------------------------------------------------------*/
/*   mergeProps()                                                          */
/*-------------------------------------------------------------------------*/
/**
  *   Merges the stroke properties in the current object into the supplied
  *   stroke object.
  *   Used internally by clone().
  *   @private
  */
DvtSolidStroke.prototype.mergeProps = function(obj)
{
   DvtSolidStroke.superclass.mergeProps.call(this, obj) ;   // merge in the subclass's props

   obj._sc  = this._sc ;      // stroke color
   obj._sa  = this._sa ;      // stroke alpha
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*   DvtBevel                     Base bevel                               */
/*-------------------------------------------------------------------------*/
/**
  * @class DvtBevel
  * Represents a bevel.  The bevel object properties are a superset of values, and 
  *                      each platform implementation should take those required
  *                      by that specfic platform.
  *                             
  * @constructor
  * @param {number} distance The offset distance for the bevel, in pixels.   Default is 4.
  * @param {number} angle  The angle of the bevel. Valid values are 0.0 to 360.0 degrees.  Default is 45 deg.   
  * @param {String} hilightRgba  The color and transparency of the bevel.  Default is 0xffffffff.
  * @param {String} shadowRgba  The color and transparency of the bevel.  Default is 0xff000000.                       
  * @param {number} blurX  The amount of horizontal blur. Valid values are 0.0 to 255.0  Default is 4.                                    
  * @param {number} blurY  The amount of vertical blur (float). Valid values are 0.0 to 255.0  Default is 4.
  * @param {number} strength  The strength of the imprint or spread. Default is 1.
  *                           Note: The higher the value, the more color is imprinted and the
  *                           stronger the contrast between the bevel and the background.
  *                           Valid values are from 0 to 255.0     
  * @param {number} quality  An integer number of times to apply the effect. Valid values are 0
  *                          to 15.  Default is 1.  Note: bevels with lower values are rendered
  *                          more quickly. For most applications, a quality value of 1 (low), 2
  *                          (medium), or 3 (high) is sufficient.  Although you can use additional     
  *                          numeric values up to 15 to achieve different effects, higher values
  *                          are rendered more slowly. Instead of increasing the value of quality,
  *                          you can often get a similar effect, and with faster rendering, by
  *                          simply increasing the values of the blurX and blurY properties.              
  * @param {Boolean} inner  A string value indicating whether the bevel is an inner, outer, or full bevel.
  * @param {Boolean} knockout  Applies a knockout effect if true which effectively makes the object's  
  *                         fill transparent, and reveals the background color of the document.                                         
  *
  */
var  DvtBevel = function(distance, angle, hilightRgba, shadowRgba, blurX, blurY, 
                         strength, quality, type, knockout)

{
   this._Init(distance, angle, hilightRgba, shadowRgba, blurX, blurY, strength, 
              quality, type, knockout) ;
} ;


DvtObj.createSubclass(DvtBevel, DvtBaseDrawEffect, "DvtBevel");


/**
 * 
 */
DvtBevel.TYPE_INNER = "inner";
/**
 * @private
 */
DvtBevel.TYPE_OUTER = "outer";
/**
 * @private
 */
DvtBevel.TYPE_FULL = "full";

/**
 * @private
 */
DvtBevel._uniqueId = 1 ;

/*-------------------------------------------------------------------------*/
/*   _Init()                                                               */
/*-------------------------------------------------------------------------*/
/** @private */
DvtBevel.prototype._Init = function(distance, angle, hilightRgba, shadowRgba, 
                                    blurX, blurY, strength, quality, type, 
                                    knockout)
{
   DvtBevel.superclass._Init.call(this) ;
  
  // The type of this draw effect, used in Flash to differentiate between filter effects
  this.__type = "bevel";

   this._distance   = ((distance === null || isNaN(distance)) ? 4 : distance);
   this._angle      = ((angle === null || isNaN(angle)) ? 45 : angle);
   this._hilightRgba= (hilightRgba ? hilightRgba : "rgba(255,255,255,1)");
   this._shadowRgba = (shadowRgba ? shadowRgba : "rgba(0,0,0,1)");
   this._blurX      = ((blurX === null || isNaN(blurX)) ? 4 : blurX);
   this._blurY      = ((blurY === null || isNaN(blurY)) ? 4 : blurY);
   this._strength   = ((strength === null || isNaN(strength)) ? 1 : strength);
   this._quality    = ((quality === null || isNaN(quality)) ? 1 : quality);
   this._type       = type ;
   this._bKnockout  = false ;
   this._Id         = 'bv' + DvtBevel._uniqueId++ ;
};

/**
  *  Returns an (unlocked) copy of this object.
  *  @type DvtBevel
  */
DvtBevel.prototype.clone = function()
{
   var o = new DvtBevel() ;

   this.mergeProps(o) ;    // merge properties into cloned obj.

   return o ;
};

/**
  *   Merges the fill properties in the current object into the supplied fill
  *   object.
  *   Used internally by clone().
  *   @private
  */
DvtBevel.prototype.mergeProps = function(obj)
{
   DvtBevel.superclass.mergeProps.call(this, obj) ;   // merge in subclass props

   obj._distance = this._distance;
   obj._angle = this._angle;
   obj._hilightRgba = this._hilightRgba;
   obj._shadowRgba = this._shadowRgba;
   obj._blurX = this._blurX;
   obj._blurY = this._blurY;
   obj._strength = this._strength;
   obj._quality = this._quality;
   obj._type = this._type;
   obj._bKnockout = this._bKnockout;
   obj._Id = this._Id;
};


// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*   DvtGlow                     Base glow effect                          */
/*-------------------------------------------------------------------------*/
/**
  * @class DvtGlow
  * Represents a glow effect.  The glow object properties are a superset of values, and 
  *                            each platform implementation should take those required
  *                            by that specfic platform.
  *                             
  * @constructor
  * @param {String} rgba  The color and transparency of the glow.  Default is rgb(255,255,255,1).
  * @param {number} blurX  The amount of horizontal blur. Valid values are 0.0 to 255.0  Default is 4.
  * @param {number} blurY  The amount of vertical blur (float). Valid values are 0.0 to 255.0  Default is 4.    
  * @param {number} strength  The strength of the imprint or spread. Default is 1.
  *                           Note: The higher the value, the more color is imprinted and the
  *                           stronger the contrast between the shadow and the background.
  *                           Valid values are from 0 to 255.
  * @param {number} quality  An integer number of times to apply the effect. Valid values are 0
  *                          to 15.  Default is 1.  Note: glows with lower values are rendered
  *                          more quickly. For most applications, a quality value of 1 (low), 2
  *                          (medium), or 3 (high) is sufficient.  Although you can use additional     
  *                          numeric values up to 15 to achieve different effects, higher values
  *                          are rendered more slowly. Instead of increasing the value of quality,
  *                          you can often get a similar effect, and with faster rendering, by
  *                          simply increasing the values of the blurX and blurY properties.              
  * @param {Boolean} inner  A boolean value indicating whether or not the glow is an inner glow.
  *                         A true value specifies an inner glow. False specifies an outer glow 
  *                         (a glow around the outer edges of the object).  Default is false.
  * @param {Boolean} knockout  Applies a knockout effect if true which effectively makes the object's  
  *                         fill transparent, and reveals the background color of the document.
  *                         Default is false.
  *
  */
var DvtGlow = function(rgba, blurX, blurY, strength, 
                        quality, inner, knockout)

{
   this.Init(rgba, blurX, blurY, strength, 
             quality, inner, knockout) ;
} ;


DvtObj.createSubclass(DvtGlow, DvtBaseDrawEffect, "DvtGlow");


DvtGlow._uniqueId = 1 ;

/** 
  * @protected
  */
DvtGlow.prototype.Init = function(rgba, blurX, blurY, strength,
                                   quality, inner, knockout)
{
  DvtGlow.superclass._Init.call(this) ;
  
  // The type of this draw effect, used in Flash to differentiate between filter effects
  this.__type = "glow";
  
  this._rgba       = (rgba ? rgba : "rgba(255,255,255,1)");
  this._blurX      = ((blurX === null || isNaN(blurX)) ? 4 : blurX);
  this._blurY      = ((blurY === null || isNaN(blurY)) ? 4 : blurY);
  this._strength   = ((strength === null || isNaN(strength)) ? 1 : strength);
  this._quality    = ((quality === null || isNaN(quality)) ? 1 : quality);
  this._bInner     = (inner ? true : false) ;
  this._bKnockout  = (knockout ? true : false) ;
  this._Id         = 'dg' + (DvtGlow._uniqueId++) ;
};

/**
  *  Returns an (unlocked) copy of this object.
  *  @type DvtGlow
  */
DvtGlow.prototype.clone = function()
{
   var o = new DvtGlow() ;

   this.mergeProps(o) ;    // merge properties into cloned obj.

   return o ;
};

/**
  *   Merges the fill properties in the current object into the supplied fill
  *   object.
  *   Used internally by clone().
  *   @private
  */
DvtGlow.prototype.mergeProps = function(obj)
{
   DvtGlow.superclass.mergeProps.call(this, obj) ;   // merge in subclass props

   obj._rgba = this._rgba;
   obj._blurX = this._blurX;
   obj._blurY = this._blurY;
   obj._strength = this._strength;
   obj._quality = this._quality;
   obj._bInner = this._bInner;
   obj._bKnockout = this._bKnockout;
   obj._Id = this._Id;
};
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*   DvtShadow                     Base drop shadow                        */
/*-------------------------------------------------------------------------*/
/**
  * @class DvtShadow
  * Represents a drop shadow.  The shadow object properties are a superset of values, and 
  *                            each platform implementation should take those required
  *                            by that specfic platform.
  *                             
  * @constructor
  * @param {number} sw  The width of the stroke line.  If omitted, default is 1.
  * @param {String} id  Optional ID for the object (see also {@link DvtBaseDrawEffect#setId}). 
  * @param {String} rgba  The color and transparency of the shadow.  Default is      0x59333333.
  * @param {number} distance The offset distance for the shadow, in pixels.   Default is 4.
  * @param {number} strength  The strength of the imprint or spread. Default is 1.
  *                           Note: The higher the value, the more color is imprinted and the
  *                           stronger the contrast between the shadow and the background.
  *                           Valid values are from 0 to 255.0               
  * @param {number} angle  The angle of the shadow. Valid values are 0.0 to 360.0 degrees.  Default is 45 deg.                
  * @param {number} blurX  The amount of horizontal blur. Valid values are 0.0 to 255.0  Default is 4.                                    
  * @param {number} blurY  The amount of vertical blur (float). Valid values are 0.0 to 255.0  Default is 4.                                       
  * @param {number} quality  An integer number of times to apply the effect. Valid values are 0
  *                          to 15.  Default is 1.  Note: shadows with lower values are rendered
  *                          more quickly. For most applications, a quality value of 1 (low), 2
  *                          (medium), or 3 (high) is sufficient.  Although you can use additional     
  *                          numeric values up to 15 to achieve different effects, higher values
  *                          are rendered more slowly. Instead of increasing the value of quality,
  *                          you can often get a similar effect, and with faster rendering, by
  *                          simply increasing the values of the blurX and blurY properties.              
  * @param {Boolean} inner  A boolean value indicating whether or not the shadow is an inner shadow.
  *                         A true value specifies an inner shadow. False specifies an outer shadow 
  *                         (a shadow around the outer edges of the object).
  * @param {Boolean} knockout  Applies a knockout effect if true which effectively makes the object's  
  *                         fill transparent, and reveals the background color of the document.                                                 
  * @param {Boolean} hide   A value indicating whether or not the object is hidden. A true value
  *                         indicates that the object itself is not drawn; only the shadow is visible.    
  *                         If false, the object is shown.                                                     
  *
  */
var  DvtShadow = function(rgba, distance, blurX, blurY, angle, strength,
                          quality,inner,knockout,hide)

{
   this._Init(rgba, distance, blurX, blurY, angle, strength, quality,inner,knockout,hide) ;
} ;


DvtObj.createSubclass(DvtShadow, DvtBaseDrawEffect, "DvtShadow");


DvtShadow._uniqueId = 1 ;

/*-------------------------------------------------------------------------*/
/*   _Init()                                                               */
/*-------------------------------------------------------------------------*/
/** @private */
DvtShadow.prototype._Init = function(rgba, distance, blurX, blurY, angle, strength,
                                     quality,inner,knockout,hide)
{
  DvtShadow.superclass._Init.call(this) ;
  
  // The type of this draw effect, used in Flash to differentiate between filter effects
  this.__type = "shadow";

   this._rgba       = (rgba ? rgba : "rgba(48,48,48,0.35)");
   this._distance   = ((distance === null || isNaN(distance)) ?  4 : distance);

   this._strength   = ((strength === null || isNaN(strength)) ? 1 : strength);
   this._blurX      = ((blurX === null || isNaN(blurX)) ? 4 : blurX);
   this._blurY      = ((blurY === null || isNaN(blurY)) ? 4 : blurY);
   this._angle      = ((angle === null || isNaN(angle)) ? 45 : angle);
   this._quality    = ((quality === null || isNaN(quality)) ? 1 : quality);
   this._bInner     = (inner ? true : false);
   this._bKnockout  = false ;
   this._bHide      = false ;
   this._Id         = 'ds' + DvtShadow._uniqueId++ ;
};

/**
  *  Returns an (unlocked) copy of this object.
  *  @type DvtShadow
  */
DvtShadow.prototype.clone = function()
{
   var o = new DvtShadow() ;

   this.mergeProps(o) ;    // merge properties into cloned obj.

   return o ;
};

/**
  *   Merges the fill properties in the current object into the supplied fill
  *   object.
  *   Used internally by clone().
  *   @private
  */
DvtShadow.prototype.mergeProps = function(obj)
{
   DvtShadow.superclass.mergeProps.call(this, obj) ;   // merge in subclass props

   obj._rgba = this._rgba;
   obj._distance = this._distance;
   obj._strength = this._strength;
   obj._blurX = this._blurX;
   obj._blurY = this._blurY;
   obj._angle = this._angle;
   obj._quality = this._quality;
   obj._bInner = this._bInner;
   obj._bKnockout = this._bKnockout;
   obj._bHide = this._bHide;
   obj._Id = this._Id;
};


// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------------*/
/*    DvtGradientFill            Gradient fill definition class              */
/*---------------------------------------------------------------------------*/
/*  Can be used to share a gradient definition among several shapes, or as a */
/*  convenience to maintain a gradient definition separately from a shape.   */
/*---------------------------------------------------------------------------*/
/**
  *   Creates a gradient specification (shareable by other shapes).
  *   @extends DvtFill
  *   @class
  *   @constructor
  *   @param {Array} arColors  An array of color specifications (which do not include alpha values).
  *   @param {Array} arColors  An optional array of alpha values (between 0 and 1).  If omitted,
  *                            alphas of 1 are assumed.
  *   @param {Array} arStops   An optional array of stop boundary positions (between 0 and 1).
  *                            If omitted, an equal distribution of colors is assumed.
  *   @param {Array} arBounds  An optional bounding box array (x, y, w, h).
  */
var  DvtGradientFill = function(arColors, arAlphas, arStops, arBounds)
{
   this._Init(arColors, arAlphas, arStops, arBounds) ;
};


DvtObj.createSubclass(DvtGradientFill, DvtFill, "DvtGradientFill") ;





/**
  *   Returns an array of alpha's.
  *   @type Array
  */
DvtGradientFill.prototype.getAlphas = function()
{
   return  this._arAlphas ;
} ;
/**
  *   Returns the bounding box for the gradient as an array (x, y, w, h).
  *   @type Array
  */
DvtGradientFill.prototype.getBounds = function()
{
   return  this._arBounds ;
} ;

/**
  *   Returns an array of colors.
  *   @type Array
  */
DvtGradientFill.prototype.getColors = function()
{
   return  this._arColors ;
} ;

/**
  *   Returns an array of stop ratios.
  *   @type Array
  */
DvtGradientFill.prototype.getStops = function()
{
   return  this._arStops ;
} ;



/*---------------------------------------------------------------------------*/
/*     Init()                                                                */
/*---------------------------------------------------------------------------*/
/**
  *  @private
  */
DvtGradientFill.prototype._Init = function(arColors, arAlphas, arStops, arBounds)
{
   DvtGradientFill.superclass._Init.call(this) ;

   this._arColors = arColors ;
   this._arBounds = arBounds ;

   var len = 0;
   if (arColors) {
       len = arColors.length - 1;
   }

   if (! arAlphas) {
     arAlphas = [] ;
     for (var i = 0; i < len; i++) {
        arAlphas.push(1) ;
     }
   }
   this._arAlphas = arAlphas ;

   if (! arStops) {     // if no stops, generate default stops
                        // for a uniform distribution of colors.
      arStops = [] ;
      var  incr   = (len > 0) ? 1/len : 0 ;
      var curStop = 0 ;

      do {
         arStops.push(curStop) ;
         curStop += incr ;
      } while ( --len > 0) ;
      arStops.push(1) ;
   }

   this._arStops = arStops ;
} ;



/*-------------------------------------------------------------------------*/
/*   mergeProps()                                                          */
/*-------------------------------------------------------------------------*/
/**
  *   Merges the gradient properties in the current object into the supplied gradient object.
  *   Used internally by clone().
  *   @private
  */
DvtGradientFill.prototype.mergeProps = function(obj)
{
   obj._arColors = this._arColors ;
   obj._arStops  = this._arStops ;
   obj._arAlphas = this._arAlphas ;
   obj._arBounds = this._arBounds ;

   DvtGradientFill.superclass.mergeProps.call(this, obj) ;
};
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------------*/
/*    DvtLinearGradientFill       Linear Gradient fill class                     */
/*---------------------------------------------------------------------------*/
/**
  *   Creates a linear gradient fill.
  *   @extends DvtGradientFill
  *   @class
  *   <p>
  *   <b>Example usage:</b><br><br><code>
  *
  *   //  Fill rectangle with a left-to-right gradient of red through blue to green.<br> 
  *   //  The colors are evenly graduated because the stop values have been omitted.<br><br> 
  *   rect.setFill(<b>new DvtLinearGradientFill(0, ['red', 'green, 'blue']</b>) ;<br><br>
  *
  *   //  Fill rectangle with a north-east direction gradient of red to green
  *   with an unequal gradient distribution of colors.<br><br> 
  *   rect.setFill(<b>new DvtLinearGradientFill(45, ['red', 'green], [0, 0.75,1]</b>) ;<br><br>

  *   @constructor
  *   @param {number} angle  Specifies the direction of the gradient as an
  *                          angle in degrees (using the standard anti-clockwise convention
  *                           for positive angles, i.e. 0 = horizontal and 90 = vertically up, etc).
  *   @param {Array} arColors  An array of color specifications (which do not include alpha values).
  *   @param {Array} arColors  An optional array of alpha values (between 0 and 1).  If omitted,
  *                            alphas of 1 are assumed.
  *   @param {Array} arStops   An optional array of stop boundary positions (between 0 and 1).
  *                            If omitted, an equal distribution of colors is assumed.
  *   @param {Array} arBounds  An optional bounding box array (x, y, w, h).
  */
var  DvtLinearGradientFill = function(angle, arColors, arAlphas, arStops, arBounds)
{
   this._Init(angle, arColors, arAlphas, arStops, arBounds) ;
};

DvtObj.createSubclass(DvtLinearGradientFill, DvtGradientFill, "DvtLinearGradientFill") ;



/*---------------------------------------------------------------------------*/
/*    clone()                                                                */
/*---------------------------------------------------------------------------*/
/**
  *  Returns an (unlocked) copy of this object.
  *  @type DvtLinearGradientFill
  */
DvtLinearGradientFill.prototype.clone = function()
{
   var o = new DvtLinearGradientFill() ;

   this.mergeProps(o) ;    // merge properties into cloned obj.
   return o ;
};



/*---------------------------------------------------------------------------*/
/*     Init()                                                                */
/*---------------------------------------------------------------------------*/
/**
  *  @private
  */
DvtLinearGradientFill.prototype._Init = function(angle, arColors, arAlphas, arStops, arBounds)
{
   DvtLinearGradientFill.superclass._Init.call(this, arColors, arAlphas, arStops, arBounds) ;

   this._angle = ((angle === null || isNaN(angle)) ? 0 : angle) ;
} ;



/*---------------------------------------------------------------------------*/
/*   getAngle()    Returns the gradient angle property for linear gradients  */
/*                 see also setAngle().                                      */
/*---------------------------------------------------------------------------*/
/**
  *  Gets the angle of the gradient in degrees.
  *  @type number
  *  @returns The angle of the gradient in degrees.  The zero degree direction is
  *  left-to-right (due east). Positive angles rotate anti-clockwise, and negative
  *  angles rotate clockwise.
  */
DvtLinearGradientFill.prototype.getAngle = function()
{
    return this._angle ;
} ;


/*-------------------------------------------------------------------------*/
/*   mergeProps()                                                          */
/*-------------------------------------------------------------------------*/
/**
  *   Merges the linear gradient properties in the current object into the
  *   DvtLinearGradientFill object.
  *   Used internally by clone().
  *   @private
  */
DvtLinearGradientFill.prototype.mergeProps = function(obj)
{
   DvtLinearGradientFill.superclass.mergeProps.call(this, obj) ;

   obj._angle  = this._angle ;      // gradient angle
};



/*---------------------------------------------------------------------------*/
/*   setAngle()         Set the gradient angle for linear gradients          */
/*---------------------------------------------------------------------------*/
//  Be default, the gradient angle is zero (due east).  Angles greater
// than zero rotate anti-clockwise.  Angles less than zero rotate clockwise.

/**
  * Sets the angle of the gradient in degrees.  The zero degree direction is
  * left-to-right (due east). Positive angles rotate anti-clockwise, and negative
  * angles rotate clockwise.
  * @param {number} The gradient direction in degrees.
  */
DvtLinearGradientFill.prototype.setAngle = function(degrees)
{
   if (this.isLocked()) {
     return ;
   }

   if (degrees !== 0) {
     if (Math.abs(degrees) > 360) {
       degrees %= 360 ;
     }
     this._angle = degrees ;
   }
} ;

// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------------*/
/*    DvtImageFill            Image fill definition class                    */
/*---------------------------------------------------------------------------*/
/*  Can be used to share an image fill definition among several shapes, or   */
/*  as a convenience to maintain an image fill definition separately from a  */
/*  shape.                                                                   */
/*---------------------------------------------------------------------------*/
/**
  *   Creates an image fill specification (shareable by other shapes).
  *   @extends DvtFill
  *   @class
  *   @constructor
  *   @param {String} src  image source
  *   @param {dimension} bound  bounding box (x, y, w, h).
  *   @param {String} repeat  specify how background image is repeated.
  *                           valid values: repeat-x, repeat-y, repeat, no-repeat
  */
var DvtImageFill = function(src, bound, repeat)
{
  this._Init(src, bound, repeat) ;
};


DvtObj.createSubclass(DvtImageFill, DvtFill, "DvtImageFill") ;






/**
  *   Returns the bounding box for the image fill as (x, y, w, h).
  *   @type (x, y, w, h)
  */
DvtImageFill.prototype.getBound = function()
{
   return this._bound ;
} ;

/**
  *   Returns the image source
  *   @type String
  */
DvtImageFill.prototype.getSrc = function()
{
   return this._src ;
} ;

/**
  *   Returns how the image is repeated
  *   @type String
  */
DvtImageFill.prototype.getRepeat = function()
{
   return this._repeat ;
} ;


/*---------------------------------------------------------------------------*/
/*     Init()                                                                */
/*---------------------------------------------------------------------------*/
/**
  *  @private
  */
DvtImageFill.prototype._Init = function(src, bound, repeat)
{
   DvtImageFill.superclass._Init.call(this) ;

   this._src = src ;
   this._bound = bound ;
   this._repeat = repeat ;

} ;



/*-------------------------------------------------------------------------*/
/*   mergeProps()                                                          */
/*-------------------------------------------------------------------------*/
/**
  *   Merges the image fill properties in the current object into the 
  *   supplied image fill object.
  *   Used internally by clone().
  *   @private
  */
DvtImageFill.prototype.mergeProps = function(obj)
{
   obj._src = this._src ;
   obj._bound = this._bound ;
   obj._repeat = this._repeat ;

   DvtImageFill.superclass.mergeProps.call(this) ;
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------------*/
/*    DvtPatternFill            Pattern definition class                     */
/*---------------------------------------------------------------------------*/
/*  Can be used to share a pattern definition among several shapes, or as a  */
/*  convenience to maintain a pattern definition separately from a shape.    */
/*---------------------------------------------------------------------------*/
/**
 *   Creates a pattern specification (shareable by other shapes).
 *   @extends DvtFill
 *   @class
 *   @constructor
 *   @param {String} pattern  constant for the type of pattern
 *   @param {String} fillColor  color of the pattern
 *   @param {String} backgroundColor  background color of the pattern
 */
var DvtPatternFill = function(pattern, fillColor, backgroundColor)
{
  this.Init(pattern, fillColor, backgroundColor);
};


DvtObj.createSubclass(DvtPatternFill, DvtFill, "DvtPatternFill");


/**   @final @type String  */
DvtPatternFill.SM_DIAG_UP_LT = "sDUL";
/**   @final @type String  */
DvtPatternFill.LG_DIAG_UP_LT = "lDUL";
/**   @final @type String  */
DvtPatternFill.SM_DIAG_UP_RT = "sDUR";
/**   @final @type String  */
DvtPatternFill.LG_DIAG_UP_RT = "lDUR";
/**   @final @type String  */
DvtPatternFill.SM_CROSSHATCH = "sC";
/**   @final @type String  */
DvtPatternFill.LG_CROSSHATCH = "lC";
/**   @final @type String  */
DvtPatternFill.SM_CHECK = "sCh";
/**   @final @type String  */
DvtPatternFill.LG_CHECK = "lCh";
/**   @final @type String  */
DvtPatternFill.SM_TRIANGLE_CHECK = "sTCh";
/**   @final @type String  */
DvtPatternFill.LG_TRIANGLE_CHECK = "lTCh";
/**   @final @type String  */
DvtPatternFill.SM_DIAMOND_CHECK = "sDCh";
/**   @final @type String  */
DvtPatternFill.LG_DIAMOND_CHECK = "lDCh";

/**
 *  @protected
 */
DvtPatternFill.prototype.Init = function(pattern, fillColor, backgroundColor)
{
  DvtPatternFill.superclass._Init.call(this);

  this._pattern = (pattern ? DvtPatternFill._convertPatternValue(pattern) : DvtPatternFill.SM_DIAG_UP_LT);
  this._fillColor = (fillColor ? fillColor : "#000000");
  this._backgroundColor = (backgroundColor ? backgroundColor : "#ffffff");
};

/**
 *   Returns the type of pattern
 *   @type String
 */
DvtPatternFill.prototype.getPattern = function()
{
  return this._pattern;
};

/**
 *   Returns the color of the pattern
 *   @type String
 */
DvtPatternFill.prototype.getColor = function()
{
  return this._fillColor;
};

/**
 *   Returns the background color of the pattern
 *   @type String
 */
DvtPatternFill.prototype.getBackgroundColor = function()
{
  return this._backgroundColor;
};

/**
 *   Merges the pattern properties in the current object into the supplied
 *   pattern object.
 *   Used internally by clone().
 *   @private
 */
DvtPatternFill.prototype.mergeProps = function(obj)
{
  obj._pattern = this._pattern;
  obj._fillColor = this._fillColor;
  obj._backgroundColor = this._backgroundColor;

  DvtPatternFill.superclass.mergeProps.call(this);
};

/**
 * Return the transformation matrix applied to this container.
 *  @type DvtMatrix
 *  @returns transformation matrix
 */
DvtPatternFill.prototype.getMatrix = function () {
  if (this._matrix)
    return this._matrix;

  return new DvtMatrix();
};

/**
 * Set the transformation matrix to apply to this container.
 *  When set, the matrix is locked so that it becomes immutable.
 *  To make changes, clone the matrix, apply changes to it, and then
 *  set the matrix again.
 *  @param {DvtMatrix} mat   The transformation matrix to apply.
 */
DvtPatternFill.prototype.setMatrix = function (mat) {    
  this._matrix = mat;
  
  // Lock the new matrix if it isn't already locked. 
  // The same matrix can be set on more than one object irregardless of locking.
  if (this._matrix && !this._matrix.isLocked())
    this._matrix.__lock();
    

};

/** 
 * Returns the pattern constant given the API string name for the pattern.  Returns the unmodified string if the value
 * is already a predefined constant or if it's not recognized.
 * @return {string}
 * @private 
 */
DvtPatternFill._convertPatternValue = function(patternStr) {
  if(patternStr == "smallDiagonalLeft")
    return DvtPatternFill.SM_DIAG_UP_LT;
  else if(patternStr == "largeDiagonalLeft")
    return DvtPatternFill.LG_DIAG_UP_LT;
  else if(patternStr == "smallDiagonalRight")
    return DvtPatternFill.SM_DIAG_UP_RT;
  else if(patternStr == "largeDiagonalRight")
    return DvtPatternFill.LG_DIAG_UP_RT;
  else if(patternStr == "smallCrosshatch")
    return DvtPatternFill.SM_CROSSHATCH;
  else if(patternStr == "largeCrosshatch")
    return DvtPatternFill.LG_CROSSHATCH;
  else if(patternStr == "smallChecker")
    return DvtPatternFill.SM_CHECK;
  else if(patternStr == "largeChecker")
    return DvtPatternFill.LG_CHECK;
  else if(patternStr == "smallTriangle")
    return DvtPatternFill.SM_TRIANGLE_CHECK;
  else if(patternStr == "largeTriangle")
    return DvtPatternFill.LG_TRIANGLE_CHECK;
  else if(patternStr == "smallDiamond")
    return DvtPatternFill.SM_DIAMOND_CHECK;
  else if(patternStr == "largeDiamond")
    return DvtPatternFill.LG_DIAMOND_CHECK;
  else
    return patternStr;
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------------*/
/*    DvtRadialGradientFill       Radial Gradient fill class                 */
/*---------------------------------------------------------------------------*/
/**
  *   Creates a radial gradient specification (shareable by other shapes).
  *   @extends DvtGradientFill
  *   @class
  *   <p>
  *   <b>Example usage:</b><br><br><code>
  *
  *   //  Fill a circle with a radial gradient of red to green.<br><br> 
  *   circle.setFill(<b>new DvtRadialGradientFill(0, ['red', 'green]</b>) ;<br><br>
  *
  *   //  Fill rectangle with a north-east direction gradient of red to green
  *   with an unequal gradient distribution of colors.<br><br> 
  *   rect.setFill(<b>new DvtRadialGradientFill(45, ['red', 'green], [0, 0.75,1]</b>) ;<br><br>

  *   @constructor
  *   @param {number} radius 
  *   @param {Array} arColors  An array of color specifications (which do not include alpha values).
  *   @param {Array} arAlphas  An optional array of alpha values (between 0 and 1).  If omitted,
  *                            alphas of 1 are assumed.
  *   @param {Array} arStops   An optional array of stop boundary positions (between 0 and 1).
  *                            If omitted, an equal distribution of colors is assumed.
  *   @param {Number} cx       the radial gradient center x position.
  *   @param {Number} cy       the radial gradient center y position.
  *   @param {Number} r        the radius of the radial gradient.
  *   @param {Array} arBounds  An optional bounding box array (x, y, w, h).
  */
var  DvtRadialGradientFill = function(arColors, arAlphas, arStops, cx, cy, r, arBounds)
{
   this._Init(arColors, arAlphas, arStops, cx, cy, r, arBounds) ;
};

DvtObj.createSubclass(DvtRadialGradientFill, DvtGradientFill, "DvtRadialGradientFill") ;



/*---------------------------------------------------------------------------*/
/*    clone()                                                                */
/*---------------------------------------------------------------------------*/
/**
  *  Returns an (unlocked) copy of this object.
  *  @type DvtRadialGradientFill
  */
DvtRadialGradientFill.prototype.clone = function()
{
   var o = new DvtRadialGradientFill() ;

   this.mergeProps(o) ;    // merge properties into cloned obj.

   return o ;
};



/*---------------------------------------------------------------------------*/
/*    getRadius()                                                            */
/*---------------------------------------------------------------------------*/
/**
  *  Returns the radial-gradient radius.
  *  @type number
  */
DvtRadialGradientFill.prototype.getRadius = function()
{
    return this._r ;
};


/*---------------------------------------------------------------------------*/
/*    getCx()                                                                */
/*---------------------------------------------------------------------------*/
/**
  *  Returns the radial-gradient center x coordinate.
  *  @type number
  */
DvtRadialGradientFill.prototype.getCx = function()
{
    return this._cx ;
};


/*---------------------------------------------------------------------------*/
/*    getCy()                                                                */
/*---------------------------------------------------------------------------*/
/**
  *  Returns the radial-gradient center y coordinate.
  *  @type number
  */
DvtRadialGradientFill.prototype.getCy = function()
{
    return this._cy ;
};


/*---------------------------------------------------------------------------*/
/*     Init()                                                                */
/*---------------------------------------------------------------------------*/
/**
  *  @private
  */
DvtRadialGradientFill.prototype._Init = function(arColors, arAlphas, arStops, cx, cy, r, arBounds)
{
   DvtRadialGradientFill.superclass._Init.call(this, arColors, arAlphas, arStops, arBounds) ;

   this._cx = cx;
   this._cy = cy;
   this._r  = r;


} ;


/*-------------------------------------------------------------------------*/
/*   mergeProps()                                                          */
/*-------------------------------------------------------------------------*/
/**
  *   Merges the radial gradient properties in the current object into the
  *   supplied DvtRadialGradientFill object.
  *   Used internally by clone().
  *   @private
  */
DvtRadialGradientFill.prototype.mergeProps = function(obj)
{
   DvtRadialGradientFill.superclass.mergeProps.call(this, obj) ;

   obj._cx = this._cx ;
   obj._cy = this._cy ;
   obj._r  = this._r ;
};

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

/**
  *   Selection effect utilities.
  *   @class DvtSelectionEffectUtils
  *   @constructor
  */
var DvtSelectionEffectUtils = {};

DvtObj.createSubclass(DvtSelectionEffectUtils, DvtObj, "DvtSelectionEffectUtils");

/** 
 * @protected
 */
DvtSelectionEffectUtils.SEL_TYPE_STROKE_FILTERS  = 0;
/** 
 * @protected
 */
DvtSelectionEffectUtils.SEL_TYPE_STROKE_NO_FILTERS = 1;

/** 
 * @protected
 */
DvtSelectionEffectUtils.DEFAULT_SEL_TYPE = DvtSelectionEffectUtils.SEL_TYPE_STROKE_FILTERS;

/**
 * Get the border color to use for a selectable marker when the mouse hovers over it.
 * @param {String} color The color of the data marker.
 * @return String The resulting color. 
 * @private
 */
DvtSelectionEffectUtils._getHoverBorderColor = function(color)
{
  return DvtColorUtils.adjustHSL(color, 0, 0, 0.15);
};

/**
 * Get an array of draw effects to use to indicate selection on markers.
 * 
 * @param {DvtContext}  context  platform specific context object
 * @param {string} dataColor The CSS color string of the primary color of the data item.
 * @param {string} innerColor The CSS color string of the inner color of the selection effect.
 * @param {string} outerColor The CSS color string of the outer color of the selection effect.
 * @type Array 
 */
DvtSelectionEffectUtils.createMarkerSelectionEffects = function(context, dataColor, innerColor, outerColor)
{
  var bUseSimpleEffects = innerColor || outerColor; // New selection effects used if either inner or outer colors are passed
  var bSvg = (context instanceof DvtSvgContext);
  
  // Create the array of draw effects
  var drawEffects = [];
  if(!bUseSimpleEffects) {
    // Inner Glow
    var fBlurX = bSvg ? 3 : 2;
    var fBlurY = bSvg ? 3 : 2;
    drawEffects.push(new DvtGlow(DvtColorUtils.setAlpha("#ffffff", 0.8), fBlurX, fBlurY, 10, 3, false, false));
    
    // Mid Glow
    var midBlurX = bSvg ? 2 * fBlurX : fBlurX;
    var midBlurY = bSvg ? 2 * fBlurY : fBlurY;
    var midGlowColor = DvtColorUtils.setAlpha(DvtColorUtils.getDarker(dataColor, 0.8), 0.8);
    drawEffects.push(new DvtGlow(midGlowColor, midBlurX, midBlurY, 12, 3, false, false));
    
    // Outer Glow
    var outerBlurX = bSvg ? 16 : 7;
    var outerBlurY = bSvg ? 16 : 7;
    var outerStrength = bSvg ? 6 : 3;
    var outerGlowColor = DvtColorUtils.setAlpha(DvtColorUtils.getBrighter(dataColor, 0.5), 0.5);
    drawEffects.push(new DvtGlow(outerGlowColor, outerBlurX, outerBlurY, outerStrength, 3, false, false));
  }
  else {
    // Simplified Effects for Alta and beyond: Use two glows only
    drawEffects.push(new DvtGlow(innerColor, 3, 3, 10, 3, false, false));
    drawEffects.push(new DvtGlow(outerColor, 5, 5, 20, 1, false, false));
  }
  
  // For SVG, effects overwrite each other instead of accumulating outward, so we need to add them in reverse
  if (bSvg) 
    drawEffects.reverse();
    
  return drawEffects;
};

/**
 * Apply a draw effect to a given displayable object.
 * 
 * @param {DvtDisplayable}  displayable  object to apply draw effect to
 * @param {DvtBaseDrawEffect}  drawEffect  effect to apply
 */
DvtSelectionEffectUtils.applyDrawEffect = function(displayable, drawEffect)
{
  var numEffects = displayable.getNumDrawEffects();
  if (numEffects == 0) {
    displayable.addDrawEffect(drawEffect);
  } else {
    var pos = displayable.getDrawEffectIndex(drawEffect);
    if (pos > -1) {
      displayable.removeDrawEffectAt(pos);
      displayable.addDrawEffectAt(drawEffect, pos);
    } else {
      displayable.addDrawEffect(drawEffect);
    }
  }
};

/**
 * Create a stroke to use when the mouse hovers over a selectable marker.
 * 
 * @param {String}  selColor  color of the data marker
 * 
 * @type DvtStroke
 */
DvtSelectionEffectUtils.createSelectingStroke = function(selColor)
{
  var stroke = new DvtSolidStroke(DvtSelectionEffectUtils._getHoverBorderColor(selColor), 1, 2);
  stroke.setMiterLimit(1); // Set low miter limit to avoid bits sticking out at corners of 3D bars, for example
  return stroke;
};

/**
 * Get the type of cursor to use when the mouse hovers over a selectable marker.
 * @return {string}
 */
DvtSelectionEffectUtils.getSelectingCursor = function()
{
  return "pointer";
};

DvtSelectionEffectUtils.applySelectingState = function(selectable, strokeColor)
{  
  if (selectable._bSelecting) {
    DvtSelectionEffectUtils._saveUpStrokeFill(selectable);
    var stroke = DvtSelectionEffectUtils.createSelectingStroke(strokeColor);
    selectable._savedHoverStroke = stroke;
    selectable._savedHoverFill = selectable._savedUpFill;
  } else {
    selectable._savedHoverStroke = selectable._savedUpStroke;
    selectable._savedHoverFill = selectable._savedUpFill;
  }

  DvtSelectionEffectUtils._applySelectingStrokeFill(selectable);
}
/**
 * Apply any selection effects to this object.
 * @param {DvtDisplayable} selectable
 * @param {string} dataColor The CSS color string of the primary color of the data item.
 * @param {string} innerColor The CSS color string of the inner color of the selection effect.
 * @param {string} outerColor The CSS color string of the outer color of the selection effect.
 */
DvtSelectionEffectUtils.applySelectionState = function(selectable, dataColor, innerColor, outerColor)
{
  var bUseSimpleEffects = innerColor || outerColor; // New selection effects used if either inner or outer colors are passed

  if(selectable._bSelected) 
    DvtSelectionEffectUtils._saveUpStrokeFill(selectable);
  
  if(selectable._bSelecting) {
    selectable._savedSelectionFill = selectable._savedHoverFill;
    selectable._savedSelectionStroke = selectable._savedHoverStroke;
  } else {
    // If not hovering, use the original fill and stroke
    selectable._savedSelectionFill = selectable._savedUpFill;
    selectable._savedSelectionStroke = selectable._savedUpStroke;
  }

  // Apply the selection effects
  if(selectable._selectionType == DvtSelectionEffectUtils.SEL_TYPE_STROKE_NO_FILTERS) {
    // No filter effects for older browsers
    if (selectable._bSelected) {
      selectable._savedSelectionStroke = new DvtSolidStroke(dataColor, 1, 2);
      selectable._savedSelectionFill = new DvtSolidFill("#ffffff", 1);
    }
  }
  else { 
    // Modern browsers with filter support
    // Create the selection effect filters if they don't already exist
    var filters = selectable._selectionEffects;
    if (!filters)
    {
      filters = [];
      
      // Inner Glow: Use slightly different params for backwards compatibility
      if(!innerColor) {
        innerColor = DvtSelectionEffectUtils._getHoverBorderColor(dataColor);
        filters.push(new DvtGlow(DvtColorUtils.setAlpha(innerColor, 1), 4, 4, 10, 3, true, false));
      }
      else // innerColor is defined, graph does not use this path
        filters.push(new DvtGlow(innerColor, 3, 3, 100, 100, true, false));
      
      // Outer Glow
      if(!outerColor && bUseSimpleEffects)
        outerColor = dataColor;
      else if(!outerColor)
        outerColor = "#000000";
      
      if(!outerColor) // backwards compatibility
        filters.push(new DvtGlow(DvtColorUtils.setAlpha(outerColor, 1), 2, 2, 20, 3, true, false));
      else // chart uses this path
        filters.push(new DvtGlow(DvtColorUtils.setAlpha(outerColor, 1), 2, 2, 7, 3, true, false));
      
      // Shadow
      if(selectable._bSelectedShadow)
        filters.push(new DvtShadow(DvtColorUtils.setAlpha("#05283f", .7), 2, 10, 7, 35, 1));
      
      selectable._selectionEffects = filters;
    }
    
    // Apply the filters if the object is selected, otherwise clear the filters
    var i;
    if(selectable._bSelected)
    {
      for (i=0; i<filters.length; i++)
      {
        DvtSelectionEffectUtils.applyDrawEffect(selectable, filters[i]);
      }
    }
    else
    {
      for (i=0; i<filters.length; i++)
      {
        selectable.removeDrawEffect(filters[i]);
      }
    }
  }
  
  // Update the fill and stroke
  var transferObj = new DvtSelectionEffectTransferObj();
  var stroke = null;
  var fill = null;

  if (selectable._bSelected) {
    stroke = selectable._savedSelectionStroke;
    fill = selectable._savedSelectionFill;
  } 
  else if (selectable._bSelecting) { // hover, not selected
    stroke = selectable._savedHoverStroke;
    fill = selectable._savedHoverFill;
  } 
  else { // neither hover nor selected
    stroke = selectable._savedUpStroke;
    fill = selectable._savedUpFill;
  }
    
  transferObj._fill = fill;
  transferObj._stroke = stroke;
  transferObj._applyFill = true;

  DvtSelectionEffectUtils.PerformStrokeFillUpdate(selectable, transferObj, true);
};

/**
 * @private
 */
DvtSelectionEffectUtils._saveUpStrokeFill = function(selectable)
{
  //if there is already a saved stroke, don't save again
  if (!selectable._savedUpStroke && !selectable._upStrokeSaved)
  {
    selectable._savedUpStroke = selectable.getStroke();
    selectable._upStrokeSaved = true;
  }
  //if there is already a saved fill, don't save again
  if (!selectable._savedUpFill && !selectable._upFillSaved)
  {
    selectable._savedUpFill = selectable.getFill();
    selectable._upFillSaved = true;
  }
}

/**
 * @private
 */
DvtSelectionEffectUtils._applySelectingStrokeFill = function(selectable) {
  var transferObj = new DvtSelectionEffectTransferObj();

  var stroke = null;
  var fill = null;
  if (selectable._bSelecting) 
    stroke = selectable._savedHoverStroke;
  else if (selectable._bSelected) {
    stroke = selectable._savedSelectionStroke ;
    fill = selectable._savedSelectionFill ;
  } 
  else {
    stroke = selectable._savedUpStroke ;
    fill = selectable._savedUpFill ;
  }
  
  transferObj._fill = fill;
  transferObj._stroke = stroke;
  transferObj._applyFill = selectable._bSelecting ? false : true;
  
  DvtSelectionEffectUtils.PerformStrokeFillUpdate(selectable, transferObj, true);
}

DvtSelectionEffectUtils.PerformStrokeFillUpdate = function(selectable, transferObj, allowTimeout) {
  transferObj._selectable = selectable;
  if (allowTimeout && selectable.getCtx().getDocumentUtils().isStrokeTimeoutRequired()) {
      //TODO: either figure out a better way to handle this,
      //or only do the defer if in Chrome and rendering to SVG
      //deferring the setStroke call because in Chrome setting
      //the stroke at the same time as changing z-order results
      //in the new stroke not being displayed

      //if there is an existing saved timer, stop and clear it
      if (selectable._applyStrokeTimer)
      {
        selectable._applyStrokeTimer.stop();
        selectable._applyStrokeTimer = null;
      }
      selectable._applyStrokeTimer = new DvtTimer(selectable.getCtx(), 0, transferObj.applyStrokeFill, transferObj, 1);
      transferObj._timer = selectable._applyStrokeTimer ;
      selectable._applyStrokeTimer.start();
  } else {
      transferObj.applyStrokeFill();
  }
}

/*
 * Temporary object used to apply the stroke and fill to the selectable object 
*/
var DvtSelectionEffectTransferObj = function(){};  

DvtObj.createSubclass(DvtSelectionEffectTransferObj, DvtObj, "DvtSelectionEffectTransferObj");


DvtSelectionEffectTransferObj.prototype.applyStrokeFill = function() {
 this._selectable.setStroke(this._stroke);
  
  if(this._applyFill)
    this._selectable.setFill(this._fill);
        
  // Cear the saved timer that called this func
  if(this._timer) {
    this._timer.stop();
    this._timer = null;
  }
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*    DvtKeyboardFocusEffect     Manages keyboard focus effect         */
/*---------------------------------------------------------------------*/
/**
  *  Creates a rectangular shape that represents keyboard focus
  *  @extends DvtRect
  */
var  DvtKeyboardFocusEffect = function(context, displayable, bounds, matrix,id)
{
   this.Init(context, displayable, bounds, matrix, id) ;
};

DvtObj.createSubclass(DvtKeyboardFocusEffect, DvtObj, "DvtKeyboardFocusEffect") ;

DvtKeyboardFocusEffect.FOCUS_COLOR_DEFAULT = "#0645AD";
DvtKeyboardFocusEffect.FOCUS_COLOR_SAFARI = "#6B9ED7";
DvtKeyboardFocusEffect.FOCUS_COLOR_CHROME = "#EDC67B";
DvtKeyboardFocusEffect.FOCUS_COLOR_IE = "#090909";
DvtKeyboardFocusEffect.FOCUS_BORDER_RADIUS = 1;
DvtKeyboardFocusEffect.FOCUS_STROKE_WIDTH = 1;
DvtKeyboardFocusEffect.FOCUS_STROKE_ALPHA = 1;
DvtKeyboardFocusEffect.FOCUS_STROKE_TYPE = DvtStroke.DOTTED;

/*---------------------------------------------------------------------*/
/*  Init()                                                             */
/*---------------------------------------------------------------------*/
/**
  *  Object initializer.
  *  @protected
  */
DvtKeyboardFocusEffect.prototype.Init = function(context, container, bounds, matrix, id)
{
  this._container = container;
  this._focusEffect = new DvtRect(context, bounds.x, bounds.y, bounds.w, bounds.h, id );
  
  var stroke = this.CreateStroke();
  this._focusEffect.setStroke(stroke);
  this._focusEffect.setRx(DvtKeyboardFocusEffect.FOCUS_BORDER_RADIUS);
  this._focusEffect.setRy(DvtKeyboardFocusEffect.FOCUS_BORDER_RADIUS);
  if (matrix)
    this._focusEffect.setMatrix(matrix);
};

DvtKeyboardFocusEffect.prototype.CreateStroke = function()
{
  var color = DvtKeyboardFocusEffect.FOCUS_COLOR_DEFAULT;
  var width = DvtKeyboardFocusEffect.FOCUS_STROKE_WIDTH;
  var alpha = DvtKeyboardFocusEffect.FOCUS_STROKE_ALPHA;
  var type = DvtKeyboardFocusEffect.FOCUS_STROKE_TYPE;
  
  if (DvtAgent.isBrowserSafari()) {
    color = DvtKeyboardFocusEffect.FOCUS_COLOR_SAFARI;
    width = 2;
    type = DvtStroke.SOLID;
  }
  else if (DvtAgent.isBrowserChrome()) {
    color = DvtKeyboardFocusEffect.FOCUS_COLOR_CHROME;
    width = 2;
    type = DvtStroke.SOLID;
  }
  else if (DvtAgent.isPlatformIE()) {
    color = DvtKeyboardFocusEffect.FOCUS_COLOR_IE;
  }
  else {
    color = DvtKeyboardFocusEffect.FOCUS_COLOR_DEFAULT;
  }
  
  var stroke = new DvtSolidStroke(color, 
                              alpha, 
                              width);
  stroke.setType(type, width, width);                              
  return stroke;                              
};

DvtKeyboardFocusEffect.prototype.getEffect = function() 
{
  return this._focusEffect;
};

DvtKeyboardFocusEffect.prototype.setEffect = function(effect) 
{
  this._focusEffect = effect;
};

DvtKeyboardFocusEffect.prototype.show = function() {
  if (this._focusEffect instanceof DvtShape)
    this._container.addChildAt(this._focusEffect, 0);
};

DvtKeyboardFocusEffect.prototype.hide = function() {
  if (this._focusEffect instanceof DvtShape)
    this._container.removeChild(this._focusEffect);
};
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.

/**
  *  Defines a (w,h) dimension.
  *  @class DvtDimension
  *  @extends DvtObj
  *  @constructor
  *  @param {number} w   the dimension width
  *  @param {number} h   the dimension height
  */
var DvtDimension = function(w,h)
{
  this.Init(w, h);
};


DvtObj.createSubclass(DvtDimension, DvtObj, "DvtDimension");

/**
  *   @protected
  */
DvtDimension.prototype.Init = function(w, h)
{
  this.w = ((w === null || isNaN(w)) ? 0 : w);
  this.h = ((h === null || isNaN(h)) ? 0 : h);
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.

/**
 *  Creates a matrix object.
 *  @extends DvtObj
 *  @class DvtMatrix is a platform independent class representing a transformation
 *  matrix.
 *  <p>
 *  The matrix is in the form:<br>
 *  [ a  b  tx ]<br>
 *  [ c  d  ty ]<br>
 *  [ 0  0  1  ]<br>
 *  <p>
 *  <b>Example:</b><br><br> <code>
  *  var mat = new DvtMatrix(context) ;<br>
 *  mat.translate(15, 30) ;<br>
 *</code>
 *  @constructor
 *  @param {number} a Optional
 *  @param {number} b Optional
 *  @param {number} c Optional
 *  @param {number} d Optional
 *  @param {number} tx Optional
 *  @param {number} ty Optional
 */
var DvtMatrix = function (a, b, c, d, tx, ty) 
{
  //don't allow users to set individual elements, because we
  //may need to adjust transforms for different platforms, for
  //example if the angle of rotation increases in different 
  //directions on different platforms, and we don't want to try
  //to deconstruct the matrix
  this.Init(a, b, c, d, tx, ty);
};

DvtObj.createSubclass(DvtMatrix, DvtLockable, "DvtMatrix");

//   [ a  b  tx ]
//   [ c  d  ty ]
//   [ 0  0  1  ]

/**
 *  @private
 */
DvtMatrix._DECOMP_TX = 0;
/**
 *  @private
 */
DvtMatrix._DECOMP_TY = 1;
/**
 *  @private
 */
DvtMatrix._DECOMP_R = 2;
/**
 *  @private
 */
DvtMatrix._DECOMP_SKEWX = 3;
/**
 *  @private
 */
DvtMatrix._DECOMP_SX = 4;
/**
 *  @private
 */
DvtMatrix._DECOMP_SY = 5;

/**
 * Users should becareful when setting individual elements, because we may need to adjust transforms for different
 * platforms, for example if the angle of rotation increases in different directions on different platforms,
 * and we don't want to try to deconstruct the matrix
 *  @protected
 */
DvtMatrix.prototype.Init = function (a, b, c, d, tx, ty) 
{
  this._a = (a == null) ? 1 : a;
  this._b = (b == null) ? 0 : b;
  this._c = (c == null) ? 0 : c;
  this._d = (d == null) ? 1 : d;
  this._tx = (tx == null) ? 0 : tx;
  this._ty = (ty == null) ? 0 : ty;

  this._u = 0;
  this._v = 0;
  this._w = 1;

  DvtMatrix.superclass._Init.call(this);
};

/**
 *  @protected
 *  Make this matrix the identity matrix.
 */
DvtMatrix.prototype.Identity = function () 
{
  this._a = 1;
  this._b = 0;
  this._c = 0;
  this._d = 1;
  this._tx = 0;
  this._ty = 0;
};

/**
 *  Get the A element of this matrix.
 *  @returns {Number}  The A element of this matrix.
 */
DvtMatrix.prototype.getA = function()
{
  return this._a;
};

/**
 *  Get the B element of this matrix.
 *  @returns {Number}  The B element of this matrix.
 */
DvtMatrix.prototype.getB = function()
{
  return this._b;
};

/**
 *  Get the C element of this matrix.
 *  @returns {Number}  The C element of this matrix.
 */
DvtMatrix.prototype.getC = function()
{
  return this._c;
};

/**
 *  Get the D element of this matrix.
 *  @returns {Number}  The D element of this matrix.
 */
DvtMatrix.prototype.getD = function()
{
  return this._d;
};

/**
 *  Get the TX element of this matrix.
 *  @returns {Number}  The TX element of this matrix.
 */
DvtMatrix.prototype.getTx = function()
{
  return this._tx;
};

/**
 *  Get the TY element of this matrix.
 *  @returns {Number}  The TY element of this matrix.
 */
DvtMatrix.prototype.getTy = function()
{
  return this._ty;
};

/**
 *  Clone this matrix.
 *  @returns {DvtMatrix}  A clone of this matrix.
 */
DvtMatrix.prototype.clone = function () 
{
  return new DvtMatrix(this._a, this._b, this._c, this._d, this._tx, this._ty);
};

/**
 *  Concatenate the given matrix with this matrix.
 *  @param {DvtMatrix} mat   The matrix to concatenate with this matrix.
 */
DvtMatrix.prototype.concat = function(mat)
{
  // A * B = B.concat(A) = childMatrix.concat(parentMatrix)
  
  if (this.isLocked())
  {
    return;
  }

  var newA = this._a * mat._a + this._c * mat._b + this._u * mat._tx;
  var newB = this._b * mat._a + this._d * mat._b + this._v * mat._tx;
  var newTX = this._tx * mat._a + this._ty * mat._b + this._w * mat._tx;

  var newC = this._a * mat._c + this._c * mat._d + this._u * mat._ty;
  var newD = this._b * mat._c + this._d * mat._d + this._v * mat._ty;
  var newTY = this._tx * mat._c + this._ty * mat._d + this._w * mat._ty;

  //var newU = this._a * mat._u + this._c * mat._v + this._u * mat._w;
  //var newV = this._b * mat._u + this._d * mat._v + this._v * mat._w;
  //var newW = this._tx * mat._u + this._ty * mat._v + this._w * mat._w;
  
  this._a = newA;
  this._b = newB;
  this._c = newC;
  this._d = newD;
  this._tx = newTX;
  this._ty = newTY;
};

/**
 *  Translate this matrix.
 *  @param {Number} dx   The horizontal distance to translate by, in pixels.
 *  @param {Number} dy   The vertical distance to translate by, in pixels.
 */
DvtMatrix.prototype.translate = function(dx, dy)
{
  if (this.isLocked())
  {
    return;
  }

  var tMat = new DvtMatrix();
  tMat._tx = dx;
  tMat._ty = dy;

  this.concat(tMat);
};

/**
 *  Scale this matrix, optionally around a specified point
 *  @param {number} sx   The horizontal value to scale by.
 *  @param {number} sy   The vertical value to scale by.
 *  @param {number} px   The x value of the point to scale around (optional)
 *  @param {number} py   The y value of the point to scale around (optional)
 */
DvtMatrix.prototype.scale = function(sx, sy, px, py)
{
  if (this.isLocked())
  {
    return;
  }

  if (px || py) {
    this.translate(-px, -py);
  }
  var tMat = new DvtMatrix();
  tMat._a = sx;
  tMat._d = sy;

  this.concat(tMat);
  if (px || py) {
    this.translate(px, py);
  }
};

/**
 *  Rotate this matrix.
 *  @param {Number} angleRads   The angle to rotate by, in radians.
 */
DvtMatrix.prototype.rotate = function(angleRads)
{
  if (this.isLocked())
  {
    return;
  }

  var tMat = new DvtMatrix();
  tMat._a = Math.cos(angleRads);
  tMat._d = tMat._a;
  tMat._c = Math.sin(angleRads);
  tMat._b =  - tMat._c;

  this.concat(tMat);
};

/**
 *  Skew this matrix.
 *  @param {Number} sxRads   The horizontal angle to skew by, in radians.
 *  @param {Number} syRads   The vertical angle to skew by, in radians.
 */
DvtMatrix.prototype.skew = function(sxRads, syRads)
{
  if (this.isLocked())
  {
    return;
  }

  var tMat = new DvtMatrix();
  tMat._b = Math.tan(sxRads);
  tMat._c = Math.tan(syRads);

  this.concat(tMat);
};

/**
 *  @protected
 *  Calculate the determinant of this matrix.
 *  @return {number} determinant of this matrix
 */
DvtMatrix.prototype.Determinant = function()
{
  var determinant = this._a * (this._d * this._w - this._ty * this._v) - 
                    this._b * (this._c * this._w - this._ty * this._u) + 
                    this._tx * (this._c * this._v - this._d * this._u);
  return determinant;
};

/**
 *  Invert this matrix.
 */
DvtMatrix.prototype.invert = function()
{
  if (this.isLocked())
  {
    return;
  }

  var determinant = this.Determinant();
  var A = (this._d * this._w - this._ty * this._v);
  var B = (this._tx * this._v - this._b * this._w);
  var TX = (this._b * this._ty - this._tx * this._d);
  var C = (this._ty * this._u - this._c * this._w);
  var D = (this._a * this._w - this._tx * this._u);
  var TY = (this._tx * this._c - this._a * this._ty);
  //var U = (this._c * this._v - this._d * this._u);
  //var V = (this._b * this._u - this._a * this._v);
  //var W = (this._a * this._d - this._b * this._c);
  
  this._a = A / determinant;
  this._b = B / determinant;
  this._tx = TX / determinant;
  this._c = C / determinant;
  this._d = D / determinant;
  this._ty = TY / determinant;
  //this._u = U / determinant;
  //this._v = V / determinant;
  //this._w = W / determinant;
};

/**
 *  @private
 *  Decompose this matrix into its constituent transforms.
 *  @returns {Array} array of transform values in the form
 *           [translateX, translateY, rotationRadians, skewXRadians, scaleX, scaleY],
 *           of null if decomposition doesn't exist
 */
DvtMatrix.prototype._decompose = function()
{
  var A = this._a;
  var B = this._b;
  var C = this._c;
  var D = this._d;
  var Tx = this._tx;
  var Ty = this._ty;

  if (A * D - B * C === 0)
    return null;

  //x scale factor
  var Sx = Math.sqrt(A * A + C * C);
  A = A / Sx;
  C = C / Sx;

  //xy shear
  var K = A * B + C * D;
  B = B - A * K;
  D = D - C * K;

  //y scale factor
  var Sy = Math.sqrt(B * B + D * D);
  B = B / Sy;
  D = D / Sy;
  K = K / Sy;

  var determinant = A * D - B * C;
  if (determinant === -1)
  {
    K =  - K;
    Sy =  - Sy;
  }

  //rotation
  var R = Math.atan2(C, A);

  //skew
  var skewX = Math.atan(K);

  //to create new matrix with same transforms, use order:
  //1) scale(Sx, Sy)
  //2) skew(skewX, 0);
  //3) rotate(R);
  //4) translate(Tx, Ty);
  return [Tx, Ty, R, skewX, Sx, Sy];
};

/**
 *  @private
 *  Recompose this matrix from the given decomposition.
 *  @param {Array}  arDecomposition  array of transform values returned from
 *         calling decompose()
 */
DvtMatrix.prototype._recompose = function(arDecomposition)
{
  if (this.isLocked())
  {
    return;
  }

  this.Identity();

  var Tx = arDecomposition[0];
  var Ty = arDecomposition[1];
  var R = arDecomposition[2];
  var skewX = arDecomposition[3];
  var Sx = arDecomposition[4];
  var Sy = arDecomposition[5];

  this.scale(Sx, Sy);
  this.skew(skewX, 0);
  this.rotate(R);
  this.translate(Tx, Ty);
};

/**
 *  @private
 *  Determine whether this matrix is equal to another.
 *  @param {DvtMatrix} mat   The matrix to compare with this matrix.
 *  @return {Boolean} True if equal, false if not.
 */
DvtMatrix.prototype.equals = function (mat) {
    if (!this && mat) return false;
    else if (this && !mat) return false;
    else if (!this && !mat) return true;
    else return (this._a == mat._a && this._b == mat._b && this._c == mat._c &&
            this._d == mat._d && this._tx == mat._tx && this._ty == mat._ty);
};

/**
 * Transform a point using this matrix.
 * @param {DvtPoint}  p  point to transform
 * @type DvtPoint
 */
DvtMatrix.prototype.transformPoint = function(p)
{
  var newX = this._a * p.x + this._b * p.y + this._tx * 1;
  var newY = this._c * p.x + this._d * p.y + this._ty * 1;
  return new DvtPoint(newX, newY);
};


/**
 * Test if this matrix is an indentity matrix.  Returns true if an identity matrix, else false.
 * @type boolean
 */
DvtMatrix.prototype.isIdentity = function()
{
  return (this._a == 1 && this._b == 0 && this._c == 0 && this._d == 1 && this._tx == 0 && this._ty == 0);
};

// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.

/**
  *  Defines an (x,y) coordinate.
  *  @class DvtPoint
  *  @extends DvtObj
  *  @constructor
  *  @param {number} x
  *  @param {number} y
  */

var DvtPoint = function(x,y)
{
  this.Init(x, y);
};


DvtObj.createSubclass(DvtPoint, DvtObj, "DvtPoint");

DvtPoint.prototype.Init = function(x, y)
{
  this.x = ((x === null || isNaN(x)) ? 0 : x);
  this.y = ((y === null || isNaN(y)) ? 0 : y);
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.

/**
  *  Defines the geometry of a rectangle. Top-left (x,y), and width and height.
  *  @class DvtRectangle
  *  @extends DvtObj
  *  @constructor
  *  @param {number} x
  *  @param {number} y
  *  @param {number} w
  *  @param {number} h
  *  @type {DvtRectangle}
  */
var   DvtRectangle = function(x,y,w,h)
{
  this.x = ((x === null || isNaN(x)) ? 0 : x);
  this.y = ((y === null || isNaN(y)) ? 0 : y);
  this.w = ((w === null || isNaN(w)) ? 0 : w);
  this.h = ((h === null || isNaN(h)) ? 0 : h);
};

DvtObj.createSubclass(DvtRectangle, DvtObj, "DvtRectangle");


/**
 * Creates a new DvtRectangle from an array of coordinates.
 * @param {Array} ar  an array of rectangle coordinates, where x = ar[0], y = ar[1], w = ar[2], h = ar[3].
 * @returns  {DvtRectangle}
 */ 
DvtRectangle.create = function (ar)
{
  return new DvtRectangle (ar[0], ar[1], ar[2], ar[3]);
};


/**
 * Returns true if the rectangle contains the given point.
 */ 
DvtRectangle.prototype.containsPoint = function(nX, nY) 
{
   return (nX >= this.x && nX <= this.x + this.w && nY >= this.y && nY <= this.y + this.h);
};


DvtRectangle.prototype.getCenter = function ()
{
  return new DvtPoint(this.x + (this.w / 2.0), this.y + (this.h / 2.0));
};



/**
 * Returns the union of this and the supplied rectangle.
 * @param {DvtRectangle}  rect  the supplied rectangle.
 * @returns {DvtRectangle} a new rectangle that is the union of this and the supplied rectangle.
 */ 
DvtRectangle.prototype.getUnion = function(rect) 
{
   var  u = new DvtRectangle() ;

   if (rect && rect.w !== 0 && rect.h !== 0) {  // ignore zero size rect's
     if (this.w !== 0 && this.h !== 0) {        //  ..     ..   ..    ..
       var  thisR = this.x + this.w ;             // this right
       var  thisB = this.y + this.h ;             // this bottom
       var  rectR = rect.x + rect.w ;             // rect right
       var  rectB = rect.y + rect.h ;             // rect bottom

       var  minx  = Math.min(this.x, rect.x) ;
       var  miny  = Math.min(this.y, rect.y) ;

       u.w = (thisR < rectR)? (rectR - minx) : (thisR - minx) ;
       u.h = (thisB > rectB)? (thisB - miny) : (rectB - miny) ;
       u.x = minx ;
       u.y = miny ;
     }
     else {
      u.x = rect.x ;
      u.y = rect.y ;
      u.w = rect.w ;
      u.h = rect.h ;
     }
   }
   else  {
      u.x = this.x ;
      u.y = this.y ;
      u.w = this.w ;
      u.h = this.h ;
   }

   return u ;
};

/**
 * Grow this rectangle to include the given coordinates.
 * @param {number}  xx  x coordinate
 * @param {number}  yy  y coordinate
 */ 
DvtRectangle.prototype.grow = function(xx, yy) 
{
  var minX = this.x;
  var minY = this.y;
  var maxX = this.x + this.w;
  var maxY = this.y + this.h;
  
  if (xx < minX) {
    minX = xx;
  }
  if (xx > maxX) {
    maxX = xx;
  }
  if (yy < minY) {
    minY = yy;
  }
  if (yy > maxY) {
    maxY = yy;
  }
  this.x = minX;
  this.y = minY;
  this.w = maxX - minX;
  this.h = maxY - minY;
};

/**
 * Returns true if the rectangle intersectes the supplied rectangle.
 * @param {DvtRectangle}  rect  the supplied rectangle.
 */ 
DvtRectangle.prototype.intersects = function(rect) 
{
   if (rect && rect.w !== 0 && rect.h !== 0) {  // ignore zero size rect's
     if (this.w !== 0 && this.h !== 0) {        //  ..     ..   ..    ..
       var  thisR = this.x + this.w ;             // this right
       var  thisB = this.y + this.h ;             // this bottom
       var  rectR = rect.x + rect.w ;             // rect right
       var  rectB = rect.y + rect.h ;             // rect bottom

       return !(rect.x > thisR || rectR < this.x || rect.y > thisB || rectB < this.y);
     }
  }
  return false;
};

/**
 * Returns a clone of itself.
 * @return {DvtRectangle} Its clone.
 */
DvtRectangle.prototype.clone = function() {
  return new DvtRectangle(this.x, this.y, this.w, this.h);
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*  DvtGeomUtils()       Utility geometry functions                        */
/*-------------------------------------------------------------------------*/

var DvtGeomUtils = {} ;

DvtObj.createSubclass(DvtGeomUtils, DvtObj, "DvtGeomUtils");

DvtGeomUtils.getCenterPoint = function (rect) {
    return new DvtPoint(rect.x+rect.w/2, rect.y+rect.h/2);
}

DvtGeomUtils.intersects = function (rect1, rect2) {
    if (rect1.x > rect2.x + rect2.w || rect1.x + rect1.w < rect2.x || rect1.y > rect2.y + rect2.h || rect1.y + rect1.h < rect2.y)
        return false;
    return true;
}

DvtGeomUtils.getPaddedRectangle = function (rect, buffer) {
    return DvtGeomUtils.getSpecificPaddedRectangle(rect, buffer, buffer, buffer, buffer);
}

DvtGeomUtils.getSpecificPaddedRectangle = function (rect, bufferTop, bufferBottom, bufferLeft, bufferRight) {
    return new DvtRectangle(rect.x - bufferLeft, rect.y - bufferTop, rect.w + bufferLeft + bufferRight, rect.h + bufferTop + bufferBottom);
}

DvtGeomUtils.GetOffsetValues = function(objBounds, containerBounds, padding) {
    var offsetY = 0;
    var lowerY = objBounds.y;
    var higherY = objBounds.y + objBounds.h;     
    if (lowerY < padding) {
        offsetY += padding - lowerY;
    } else if (higherY > containerBounds.h - padding) {
        offsetY += containerBounds.h - padding - higherY;
    }

    // Adjust position of box depending on stage bounds    
    var lowerX = objBounds.x;
    var higherX = objBounds.x + objBounds.w;     

    var offsetX = 0;
    if (lowerX < padding) {
        offsetX += padding - lowerX;
    } else if (higherX > containerBounds.w - padding) {
        offsetX += containerBounds.w - padding - higherX;
    }
    return {offsetX: offsetX, offsetY: offsetY};
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*  DvtLineUtils()       Utility functions for lines                       */
/*-------------------------------------------------------------------------*/

var DvtLineUtils = {} ;

DvtObj.createSubclass(DvtLineUtils, DvtObj, "DvtLineUtils");

/**
 * Computes the point of intersection of two lines in the same plane.
 * @param   {DvtPoint} p1 end point on line 1
 * @param   {DvtPoint} p2 other end point on line 1
 * @param   {DvtPoint} p3 end point on line 2
 * @param   {DvtPoint} p4 other end point on line 2
 * @param   {DvtPoint} pt optional DvtPoint object that will be updated with the
 *                     point of intersection.  If omitted, a new DvtPoint will be created
 *                     and returned.
 * @returns {DvtPoint} the point of intersection. If null is returned, then either
 *                     the lines are parallel, or both of the lines have a zero length.
 */
DvtLineUtils.intersect2Lines = function(p1, p2, p3, p4, pt)
{
   //  Using Penner's Method

   var x1  = p1.x; 
   var y1  = p1.y;
   var x4  = p4.x;
   var y4  = p4.y;
   var dx1 = p2.x - x1;            // the x diff
   var dx2 = p3.x - x4;            //  .. ..  ..

   if (dx1 === 0 && dx2 === 0)     // both lines vertical? (i.e. undefined slope)
     return null ;

   var m1 = (p2.y - y1) / dx1;
   var m2 = (p3.y - y4) / dx2;

   if (m1 === m2)                  // both lines parallel?
     return null ;

   if (! pt) {
     pt =  new DvtPoint() ;        // return value
   }

   if (dx1 === 0) {
     pt.x = x1 ;
     pt.y = (m2 * (x1 - x4) + y4) ;
     return pt ;
   }
   else if (dx2 === 0) {
     pt.x = x4 ;
     pt.y = (m1 * (x4 - x1) + y1) ;
     return pt ;
   }

   var xInt  = (-m2 * x4 + y4 + m1 * x1 - y1) / (m1 - m2);
   var yInt  = m1 * (xInt - x1) + y1;

   pt.x = xInt ;
   pt.y = yInt ;
   return pt ;
} ;


/**
  * Returns the length of a line segment with end points (x1, y1) and (x2, y2).
  * @param {number} x1  endpoint x
  * @param {number} y1  endpoint y
  * @param {number} x2  other endpoint x
  * @param {number} y2  other endpoint y
  * @returns {number}   the length of the line segment with end points (x1, y1) and (x2, y2).
  */
DvtLineUtils.lineLen = function(x1, y1, x2, y2)
{
   var dx = (x2 - x1) ;
   var dy = (y2 - y1) ;
   return Math.sqrt((dx * dx) + (dy * dy)) ;
};


/**
* Returns the midpoint (x,y) of a line segment with end points (x1,y1) and (x2,y2).
* @param {number}   x1 end point x.
* @param {number}   y1 end point y.
* @param {number}   x2 other end point x
* @param {number}   y2 other end point y.
* @param {DvtPoint} pt optional DvtPoint object that will be updated with the mid-point.
*                   If omitted, a new DvtPoint will be created.
* @returns (DvtPoint} the midpoint (x,y) of the line segment in the supplied DvtPoint,
*                     or a new DvtPoint if not supplied.
*/
DvtLineUtils.midPt = function(x1, y1, x2, y2, pt)
{
   if (! pt) {
     pt = new DvtPoint() ; 
   }
   pt.x  =  (x1 + x2)/2 ;
   pt.y  =  (y1 + y2)/2 ;
   return pt ;
};

// Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.

var DvtVectorUtils = {} ;

DvtObj.createSubclass(DvtVectorUtils, DvtObj, "DvtLineUtils");

/**
 * Creates a vector from the origin to the specified coordinate
 * 
 * @param {number} x the x coordinate of the vector
 * @param {number} y the y coordinate of the vector
 * @return {object} an object with x and y properties representing the vector
 */
DvtVectorUtils.createVector = function(x, y) {
  var v = {};
  v.x = x;
  v.y = y;
  return v;
}

/**
 * Adds two vectors
 * 
 * @param {object} v1 an object with x and y properties representing the first addend
 * @param {object} v2 an object with x and y properties representing the second addend
 * @return {object} an object with x and y properties representing the sum
 */
DvtVectorUtils.addVectors = function(v1, v2) {
  return DvtVectorUtils.createVector(v1.x + v2.x, v1.y + v2.y);
}

/**
 * Subtracts two vectors
 * 
 * @param {object} v1 an object with x and y properties representing the minuend
 * @param {object} v2 an object with x and y properties representing the subtrahend
 * @return {object} an object with x and y properties representing the difference
 */
DvtVectorUtils.subtractVectors = function(v1, v2) {
  return DvtVectorUtils.createVector(v1.x - v2.x, v1.y - v2.y);
}

/**
 * Scales a vector
 * 
 * @param {object} v an object with x and y properties representing the vector
 * @param {number} s the scalar by which to scale the vector
 * @return {object} an object with x and y properties representing the scaled vector
 */
DvtVectorUtils.scaleVector = function(v, s) {
  return DvtVectorUtils.createVector(v.x * s, v.y * s);
}

/**
 * Scales a vector
 * 
 * @param {object} v an object with x and y properties representing the vector
 * @return {number} the magnitude of the vector
 */
DvtVectorUtils.getMagnitude = function(v) {
  return Math.sqrt(v.x * v.x + v.y * v.y);
}
/**
 *  A static class for chart layout.
 *  @class DvtLayoutUtils
 *  @constructor
*/
var DvtLayoutUtils = function()
{};

DvtObj.createSubclass(DvtLayoutUtils, DvtObj, "DvtLayoutUtils");

/**
 * Positions the specified displayable in the available space.
 * @param {DvtRectangle} availSpace The available space.
 * @param {string} position The position within the available space.  Valid values are "top", "bottom", "left", and "right".
 * @param {DvtDisplayable} displayable The displayable to be positioned.
 * @param {number} width The width of the displayable.
 * @param {number} height The height of the displayable.
 * @param {number} gap The gap to leave between the displayable and other content.  This gap is applied only if
 *                     the displayable's area is greater than 0.
 */
DvtLayoutUtils.position = function(availSpace, position, displayable, width, height, gap) {
  if(!displayable)
    return;
    
  // Adjust the gap if the displayable has no area
  gap = (width*height > 0) ? gap : 0;
  
  if(position == "top") {
    displayable.setTranslateX(availSpace.x + availSpace.w/2 - width/2);
    displayable.setTranslateY(availSpace.y);
    availSpace.y += (height + gap);
    availSpace.h -= (height + gap);
  }
  else if(position == "bottom") {
    displayable.setTranslateX(availSpace.x + availSpace.w/2 - width/2);
    displayable.setTranslateY(availSpace.y + availSpace.h - height);
    availSpace.h -= (height + gap);
  }
  else if(position == "left") {
    displayable.setTranslateX(availSpace.x);
    displayable.setTranslateY(availSpace.y + availSpace.h/2 - height/2);
    availSpace.x += (width + gap);
    availSpace.w -= (width + gap);
  }
  else if(position == "right") {
    displayable.setTranslateX(availSpace.x + availSpace.w - width);
    displayable.setTranslateY(availSpace.y + availSpace.h/2 - height/2);
    availSpace.w -= (width + gap);
  }
}

/**
 * Aligns the specified displayable in the available space.
 * @param {DvtRectangle} availSpace The available space.
 * @param {string} position The position within the available space.  Valid values are "start", "center", and "end".
 * @param {DvtDisplayable} displayable The displayable to be positioned.
 * @param {number} width The width of the displayable.
 */
DvtLayoutUtils.align = function(availSpace, align, displayable, width) {
  if(!displayable)
    return;
    
  // Account for the locale and find the position  
  var position = align;
  if(position == "start")
    position = DvtAgent.isRightToLeft(displayable.getCtx()) ? "right" : "left";
  else if(position == "end")
    position = DvtAgent.isRightToLeft(displayable.getCtx()) ? "left" : "right";
    
  // Align the text  
  if(position == "left") 
    displayable.setX(availSpace.x);
  else if(position == "center") 
    displayable.setX(availSpace.x + availSpace.w/2 - width/2);
  else if(position == "right") 
    displayable.setX(availSpace.x + availSpace.w - width);
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*--------------------------------------------------------------------------*/
/*   DvtMath             Miscellaneous Math constants and routines          */
/*--------------------------------------------------------------------------*/
/**
  *  @class DvtMath  Defines miscellaneous math constants and routines.
  */
var  DvtMath = new Object();

DvtObj.createSubclass(DvtMath, DvtObj, "DvtMath");

/**
  *  Number of radians in 1 degree.
  *  @type number
  */
DvtMath.RADS_PER_DEGREE = (Math.PI/180) ;
/**
  *  Number of degrees in 1 radian.
  *  @type number
  */
DvtMath.DEGREES_PER_RAD = (180/Math.PI) ;
/**
  *  Half the value of Pi radians.
  *  @type number
  */
DvtMath.HALF_PI  = (Math.PI/2) ;
/**
  *  One quarter the value of Pi radians.
  *  @type number
  */
DvtMath.QUARTER_PI  = (Math.PI/4); 

/**
  *  Fudge factor deal with floating point rounding error
  *  @type number
  */
DvtMath.TOLERANCE  = 0.1;

/**
 *   Converts degrees to radians.
 *   @param {number}  deg  The value in degrees to be converted. 
 *   @type number
 */
DvtMath.degreesToRads = function(deg)
{
   return (deg * DvtMath.RADS_PER_DEGREE);
};

/**
 *   Converts radians to degrees.
 *   @param {number}  rad  The valie in radians to be converted. 
 *   @type number
 */
DvtMath.radsToDegrees = function(rad)
{
   return (rad * DvtMath.DEGREES_PER_RAD);
};

/**
  * Interpolate a number between the original and destination values for the 
  * given percent.
  * @param {number}  origVal  original value
  * @param {number}  destVal  destination value
  * @param {number}  percent  percent value to interpolate
  * @type number
  */
DvtMath.interpolateNumber = function(origVal, destVal, percent)
{
  return (origVal + percent * (destVal - origVal));
};
/**
 * @constructor
 * property map
 */
var DvtPropMap = function() {
  this.Init();
};

DvtPropMap.REGEXP = new RegExp("#\\{([^\\}]*)\\}", "g");


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


/**
 * Initializes the instance. 
 */
DvtPropMap.prototype.Init = function() {
  //Note: there is no Init method in superclass
  // DvtPropMap.superclass.Init.call(this);
  this._props = {};
};


/*-------------------------------------------------------------------------*/
/*   AfComponent propibutes                                                */
/*-------------------------------------------------------------------------*/
DvtPropMap.prototype.getProperty = function(propName) {
  return this._props[propName];
};

DvtPropMap.prototype.setProperty = function(propName, val) {
  this._props[propName] = val;
};


DvtPropMap.prototype.getStringProp = function(propName, defString) {
  var val = this._props[propName];
  return val ? val : defString;
};

/**
 * Returns boolean value of the specified property
 * @param {string} propName  property name
 * @param {boolean} defBool default value
 * @return {boolean} boolean value of the specified property
 */
DvtPropMap.prototype.getBooleanProp = function(propName, defBool) {
  var val = this._props[propName];
  var ret = defBool;
  if (typeof val === "boolean")
    ret = val;
  else if (val === 0)
    ret = false;
  else if (val === 1)
    ret = true;
  else if (typeof val === "string")
    ret = (val.toLowerCase() === "true");

  return ret;
};

DvtPropMap.prototype.getIntegerProp = function(propName, defInt) {
  var val = this._props[propName];
  var ret;
  if (val)
    ret = parseInt(val);
  return (ret) ? ret : defInt;
};

DvtPropMap.prototype.getFloatProp = function(propName, defFloat) {
  var val = this._props[propName];
  var ret;
  if (val)
    ret = parseFloat(val);
  return (ret) ? ret : defFloat;
};

/**
 * Returns the id of this component.
 * @type String
 */
DvtPropMap.prototype.getId = function() {
  return this.getProperty("id");
};

/**
 * Set the id of this component.
 * @param {String} id  ID for the component
 */
DvtPropMap.prototype.setId = function(id) {
  this.setProperty("id", id);
};

/**
 * set a list of properties
 * @param {NamedNodeMap} attributes array
 */
DvtPropMap.prototype.setProperties = function(attrArray) {
  for (var i = 0; i < attrArray.length; i++) {
    var attr = attrArray[i];
    if (attr.name && attr.value !== undefined) {
      this.setProperty(attr.name, attr.value);
    }
  }
};


/*-------------------------------------------------------------------------*/
/*   stamp                                                                 */
/*-------------------------------------------------------------------------*/

/**
 * Stamp out this template object only. 
 * @param {} elcontext EL binding context
 * @return {DvtAfComponent} a new DvtAfComponent tree
 */
DvtPropMap.prototype.stamp = function(elcontext) {

  // create a new DvtAfComponent object of the same type
  var result = new this.constructor();

  // copy properties
  var newProps = {};
//   var regexp = /#{([^\}]*)}/g;
  for (var attr in this._props) {
    //TODO: test for string
    if (this._props[attr].replace) {
        newProps[attr] = this._props[attr].replace(DvtPropMap.REGEXP, 
          function(str, bindVar) {
            if (elcontext && elcontext[bindVar] !== undefined) {
              return elcontext[bindVar];
            }
            return str;
          }
        );
    } else {
        newProps[attr] = this._props[attr];
    }
  }

  result._props = newProps;
  return result;
};


/**
 * Convert an attribute array of a EL node to an EL context object
 * @param {DvtXmlNode} xmlNode The EL xml node
 */
DvtPropMap.toELContext = function(xmlNode) {
  var arr = xmlNode.getAttributes();
  var prop;
  var obj = {};
  for (var i = 0; i < arr.length; i++) {
    prop = arr[i];
    obj[prop.name] = prop.value;
  }
  return obj;
}

// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.

/**
 * usage
 * DvtImageLoader.loadImage("pic.png", function(image) {
 *   alert(image.width);
 *   alert(image.height);
 * });
 * 
 */


/**
 * DvtImageLoader
 */
var DvtImageLoader = {};

DvtObj.createSubclass(DvtImageLoader, DvtObj, "DvtImageLoader");

/**
 * Load an image.
 * 
 * @param {DvtContext} context The context object
 * @param src URL of the image to load
 * @param onComplete function to call when the image is loaded
 * 
 * @return image if image is already loaded and onComplete
 *         function is null, otherwise null
 */
DvtImageLoader.loadImage = function(context, src, onComplete) {

  var loader = context.getImplFactory().getImageLoader();
  return loader.loadImage(src, onComplete);

};


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

/**
 * JSON utilities.
 * @class
 */
var DvtJSONUtils = new Object();

DvtObj.createSubclass(DvtJSONUtils, DvtObj, "DvtJSONUtils");

/**
 * Returns a deep clone of the object.
 * @param {object} obj The object to clone.
 * @return {object} The clone.
 */
DvtJSONUtils.clone = function(obj) {
  var ret = null;
  
  if(obj instanceof Array) {
    ret = [];
    
    // Loop through and copy the Array
    for(var i=0; i<obj.length; i++) {
      if(DvtJSONUtils._isDeepClonable(obj[i])) // deep clone objects
        ret[i] = DvtJSONUtils.clone(obj[i]);
      else // copy values
        ret[i] = obj[i];
    }
  }
  else if(obj instanceof DvtCSSStyle) {
    ret = obj.clone();
  }
  else if(obj instanceof Object) {
    ret = {};
    
    // Loop through all properties of the object
    var key;
    for(key in obj) {
      var value = obj[key];
      if(DvtJSONUtils._isDeepClonable(value)) // deep clone objects
        ret[key] = DvtJSONUtils.clone(value);
      else // copy values
        ret[key] = value;
    }
  }
  
  return ret;
}

/**
 * Returns a new object with the merged properties of the given objects.  Properties
 * in the first object take precedence.
 * @param {object} a
 * @param {object} b
 * @return {object} A new object containing the merged properties.
 */
DvtJSONUtils.merge = function(a, b) {
  // Clone so that contents aren't modified
  var one = DvtJSONUtils.clone(a);
  var two = DvtJSONUtils.clone(b);
  if (one == null)
    return two;
  else if (two == null)
    return one;
  else  {
    DvtJSONUtils._copy(one, two);
    return two;
  }
}

/**
 * Copys the properties from the first object onto the second.
 * @param {object} a The source of the properties to copy.
 * @param {object} b The destination of the copied properties.
 */
DvtJSONUtils._copy = function(a, b) {
  var key;
  for(key in a) {
    var value = a[key];
    if(value && (value instanceof Array)) {
      // Copy the array over, since we don't want arrays to be merged
      b[key] = value;      
    }
    else if(b[key] && (b[key] instanceof DvtCSSStyle)) {
      // If an object is defined as CSS in the base object, merge the CSS
      if(value instanceof DvtCSSStyle) 
        b[key].merge(value);
      else // value is String
        b[key].merge(new DvtCSSStyle(value));
    }
    else if(DvtJSONUtils._isDeepClonable(value)) {
      // Deep clone if object exists in b, copy otherwise
      if(b[key])
        DvtJSONUtils._copy(value, b[key]);
      else
        b[key] = value;
    }
    else
      b[key] = value;
  }
}

/**
 * Checks the type of an object and returns whether it is deep clonable
 * @param {Object} The object to check the type of
 * @return {boolean} Whether the object is deep clonable
 * @private
 */
DvtJSONUtils._isDeepClonable = function(obj) {
  if(typeof obj == 'undefined')
    return false;
  else
    return (obj instanceof Object) && !(obj instanceof Boolean) && !(obj instanceof String) && !(obj instanceof Number) && !(obj instanceof Function);
}
/*---------------------------------------------------------------------*/
/*   DvtDisplayableUtils          Static Utility Functions             */
/*---------------------------------------------------------------------*/

/**
 *   Static Utility Functions for DvtDisplayable
 *   @class DvtDisplayableUtils
 *   @constructor
 */
var  DvtDisplayableUtils = function()
{} ;

DvtObj.createSubclass(DvtDisplayableUtils, DvtObj, "DvtDisplayableUtils");

/*
 * Temporarily add the display object to the stage to get dimensions.
 * Remove it from stage after done
 */
DvtDisplayableUtils.getDimensionsForced = function(context, obj) {
  //save original parent and index
  var oParent = obj.getParent();
  var oIndex;
  if (oParent)
    oIndex = oParent.getChildIndex(obj);

  var stage = context.getStage();
  stage.addChild(obj);

  var dim = obj.getDimensions();
  stage.removeChild(obj);

  //restore original parent
  if (oParent) {
    oParent.addChildAt(obj, oIndex);
  }

  return dim;
}


/*
 * Temporarily add the display object to the stage to get dimensions.
 * Remove it from stage after done
 * cached dimensions in obj._dim
 */
DvtDisplayableUtils._getDimForced = function(context, obj) {
  //if there is a cache or context is null, return it.
  if (obj._dim || ! context) {
    return obj._dim;
  }

  var dim = DvtDisplayableUtils.getDimensionsForced(context, obj);

  //cached the dimensions
  DvtDisplayableUtils._setDimForced(obj, dim);
  return dim;
}


/*
 * cached dimensions in obj._dim
 */
DvtDisplayableUtils._setDimForced = function(obj, dim) {
  //cached the dimensions
  obj._dim = dim;
}

/**
 * Determine if a displayable is an ancestor of another displayable.
 * 
 * @param {DvtDisplayable} ancestor  The potential ancestor displayable.
 * @param {DvtDisplayable} descendant  The potential descendant displayable.
 * @type Boolean
 */
DvtDisplayableUtils.isAncestor = function(ancestor, descendant) {
  var disp = descendant;
  while (disp) {
    if (disp == ancestor) {
      return true;
    }
    if (disp instanceof DvtContainer) {
      disp = disp.getParent();
    }
  }
  return false;
}


// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*  DvtPathUtils()       Utility functions for SVG paths               */
/*---------------------------------------------------------------------*/

var DvtPathUtils = {} ;

DvtObj.createSubclass(DvtPathUtils, DvtObj, "DvtPathUtils");

/**
 * Returns a path command for a move to the specified coordinates
 * @param x the destination x coordinate
 * @param y the destination y coordinate
 * @return the moveTo path command
 */
DvtPathUtils.moveTo = function(x,y) {
  return "M" + x + "," + y;
};

/**
 * Returns a path command for a line to the specified coordinates
 * @param x the destination x coordinate
 * @param y the destination y coordinate
 * @return the lineTo path command
 */
DvtPathUtils.lineTo = function(x,y) {
  return "L" + x + "," + y;
};

DvtPathUtils.quadTo = function(x1,y1,x,y) {
  return "Q" + x1 + "," + y1 + "," + x + "," + y;
};

DvtPathUtils.cubicTo = function(x1,y1,x2,y2,x,y) {
  return "C" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + x + "," + y;
};

/**
 * Returns a path command for an arc to the specified coordinates
 * @param rx the x radius of the ellipse whose arc will be drawn
 * @param ry the y radius of the ellipse whose arc will be drawn
 * @param angleExtent the sweep of the arc to be drawn
 * @param direction 1 for clockwise, 0 for counter-clockwise 
 * @param x the ending x coordinate
 * @param y the ending y coordinate
 */
DvtPathUtils.arcTo = function(rx, ry, angleExtent, direction, x, y) {
  var cmd = "A" + rx + "," + ry + ",0,";
  if(angleExtent > Math.PI) {
    cmd += "1,";
  }
  else {
    cmd += "0,";
  }
  cmd += (direction + "," + x + "," + y);
  return cmd;
};

/**
 * Returns a path command that closes the path.
 */
DvtPathUtils.closePath = function() {
  return "Z";
};

/**
 * Returns a path command for a rounded rectangle.
 * @param {number} x Rectangle x.
 * @param {number} y Rectangle y.
 * @param {number} w Rectangle width.
 * @param {number} h Rectangle height.
 * @param {number} tlcr Top left corner radius.
 * @param {number} trcr Top right corner radius.
 * @param {number} brcr Bottom right corner radius.
 * @param {number} blcr Bottom left corner radius.
 * @return {string} Path command.
 */
DvtPathUtils.roundedRectangle = function(x, y, w, h, tlcr, trcr, brcr, blcr) {
  var cmd = DvtPathUtils.moveTo(x+tlcr, y) +
            DvtPathUtils.lineTo(x+w-trcr, y) +
            DvtPathUtils.arcTo(trcr, trcr, Math.PI/2, 1, x+w, y+trcr) +
            DvtPathUtils.lineTo(x+w, y+h-brcr) +
            DvtPathUtils.arcTo(brcr, brcr, Math.PI/2, 1, x+w-brcr, y+h) +
            DvtPathUtils.lineTo(x+blcr, y+h) +
            DvtPathUtils.arcTo(blcr, blcr, Math.PI/2, 1, x, y+h-blcr) +
            DvtPathUtils.lineTo(x, y+tlcr) +
            DvtPathUtils.arcTo(tlcr, tlcr, Math.PI/2, 1, x+tlcr, y) +
            DvtPathUtils.closePath();
  
  return cmd;
}

/**
  *  Creates an array of path/coords from an Svg path string.
  *  @param {String} cmds A string containing SVG path command sequences.
  *  @returns {Array}  an array of consecutive path command/coords, or null
  *                    if no command string supplied.
  */
DvtPathUtils.createPathArray = function(sCmds)
{
  if (! sCmds)
     return null ;

  //  Unpack into an array of commands and coords.
   var cmds = sCmds.replace(/([mlqhvzca])/gi, ',$1,'); // create array of coords from the string
   var ar   = cmds.split(/[ ,]/g) ;
   var len  = ar.length;
   var i ;
   //  Convert coordinates in command array to floats.
   for (i = 0; i < len;  i++) {
      var s = ar[i] ;
      if (!s) {
        ar.splice(i, 1);
        i--;
        len--;
      }
      else if (!isNaN(s)) {
        ar[i] = parseFloat(s) ;
      }
   }

   return ar ;
} ;

/**
 * Returns the bounding box of the supplied path commands.
 * @param {Array} arCmds the path commands.
 * @returns {DvtRectangle} the bounding box of the supplied path commands.
 */
DvtPathUtils.getDimensions = function(aCmds)
{
  if (! (aCmds && aCmds.length)) {
    return new DvtRectangle() ;
  }

  var  len = aCmds.length ;
  var  c ;
  var  xSubPath, ySubPath ;
  var  bFirst = true ;           // false after first command
  var  bRel ;                    // true if relative command
  var  x, y, x2, y2, x3, y3 ;

  var minX = Number.MAX_VALUE ;
  var maxX = Number.MIN_VALUE ;
  var minY = Number.MAX_VALUE ;
  var maxY = Number.MIN_VALUE ;
  var  aPos = [] ;
  var  i, j, k ;
  
  for (i = 0; i < len;  i++)  {
     bRel = false ;
     var iMulti = 0 ;
     j = 0 ;

     c = aCmds[i] ;

     switch (c)
     {
        case 'm' :  bRel = true ;
        case 'M' :  do {
                      x = aCmds[i+1] ;
                      y = aCmds[i+2] ;

                      if (bFirst) {        // note if first is 'm', it is treated as absolute.
                        bFirst = false ;
                      }
                      else if (bRel) {
                        x += xSubPath ;
                        y += ySubPath ;
                      }
                      xSubPath = x ;
                      ySubPath = y ;

                      aPos[j++] = x ;
                      aPos[j++] = y ;
                      iMulti++ ;
                      i += 2 ;
                    } while (! isNaN(aCmds[i +1])) ;
                    break ;

        case 'c' :  bRel = true ;
        case 'C' :  do {
                      x  = aCmds[i+1] ;
                      y  = aCmds[i+2] ;
                      x2 = aCmds[i+3] ;
                      y2 = aCmds[i+4] ;
                      x3 = aCmds[i+5] ;
                      y3 = aCmds[i+6] ;

                      if (bRel) {
                        x  += xSubPath ;
                        y  += ySubPath ;
                        x2 += xSubPath ;
                        y2 += ySubPath ;
                        x3 += xSubPath ;
                        y3 += ySubPath ;
                      }
                      xSubPath = x3 ;
                      ySubPath = y3 ;

                      aPos[j++] = x3 ;
                      aPos[j++] = y3 ;
                      iMulti++ ;
                      i += 6 ;
                    } while (! isNaN(aCmds[i +1])) ;
                    break ;

        case 'q' :  bRel = true ;
        case 'Q' :  do {
                      x  = aCmds[i+1] ;
                      y  = aCmds[i+2] ;
                      x2 = aCmds[i+3] ;
                      y2 = aCmds[i+4] ;
                      if (bRel) {
                        x  += xSubPath ;
                        y  += ySubPath ;
                        x2 += xSubPath ;
                        y2 += ySubPath ;
                      }
                      xSubPath = x2 ;
                      ySubPath = y2 ;

                      aPos[j++] = x2 ;
                      aPos[j++] = y2 ;
                      iMulti++ ;
                      i += 4 ;
                    } while (! isNaN(aCmds[i +1])) ;
                    break ;

        case 'l' :  bRel = true ;
        case 'L' :
                    do {
                      x = aCmds[i+1] ;
                      y = aCmds[i+2] ;
                      if (bRel) {
                        x += xSubPath ;
                        y += ySubPath ;
                      }
                      xSubPath = x ;
                      ySubPath = y ;

                      aPos[j++] = x ;
                      aPos[j++] = y ;
                      iMulti++ ;
                      i += 2 ;
                    } while (! isNaN(aCmds[i+1])) ;
                    break ;

        case 'h' :  bRel = true ;
        case 'H' :
                    do {
                      x = aCmds[i+1] ;
                      if (bRel) {
                        x += xSubPath ;
                      }
                      xSubPath = x ;

                      aPos[j++] = x ;
                      aPos[j++] = ySubPath ;
                      iMulti++ ;
                      i += 1 ;
                    } while (! isNaN(aCmds[i+1])) ;
                    break ;

        case 'v' :  bRel = true ;
        case 'V' :
                    do {
                      y = aCmds[i+1] ;
                      if (bRel) {
                        y += ySubPath ;
                      }
                      ySubPath = y ;

                      aPos[j++] = xSubPath ;
                      aPos[j++] = y ;
                      iMulti++ ;
                      i += 1 ;
                    } while (! isNaN(aCmds[i+1])) ;
                    break ;

        case 'z' :
        case 'Z' :  break ;

        default :   break ;
     }                                    // end switch

     j = 0 ;
     for (k = 0 ; k < iMulti; k++) {
        x = aPos[j++] ;
        y = aPos[j++] ;
        minX = Math.min(minX, x) ;
        maxX = Math.max(maxX, x) ;
        minY = Math.min(minY, y) ;
        maxY = Math.max(maxY, y) ;
     }
  }

  return new DvtRectangle(minX, minY, Math.abs(maxX - minX), Math.abs(maxY - minY)) ;
};


/**
 *  Converts a platform independent array of consecutive commands and coords
 *  to an SVG path string.
 *  @param {Array} ar  The array of commands and coordinates to be converted.
 *  @type String
 */
DvtPathUtils.getPathString = function(ar)
{
   var sOut = '' ;
   var len = ar.length ;
   var s ;

   for (var i = 0; i < len;  i++) {
      s = ar[i] ;
      if (s !== undefined && s !== null)
        sOut += (((i > 0)? ' ' : '') + ar[i]) ;
   }

   return sOut ;
};



/**
  *  Initializes the shape to the specified coordinates.
  *  @private
  */
DvtPathUtils.transformPath = function(sCmds, x, y, sx, sy)
{
   var scaledPath = "" ;                   // return string
   if (! sCmds)
      return ;

  // Split the commands (command is recognized as a single letter followed
  // by any number of non-letter characters)

  var commands = sCmds.match(/[a-z][^a-z]*/ig);

  for (var i = 0; i < commands.length; i++)
  {
     var command = commands[i];
     var cmdType = command.charAt(0);
     var absCmd  = cmdType === cmdType.toUpperCase();

     scaledPath += (cmdType + " ");
     var strArgs = command.substring(1);

     strArgs = strArgs.replace(/,/g, ' ');         // replace all commas with spaces
     strArgs = strArgs.replace(/^\s+|\s+$/g, '');  // trim leading and trailing whitespace

     if (strArgs.length > 0)
     {
        var args = strArgs.split(/\s+/g); // split on whitespace
        if (cmdType.toUpperCase() === 'A')
        {
          // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)  only rx, ry, x, y should be scaled
          // only x,y should be translated for 'A', no translation for 'a'
 
          for (var j = 0; j < args.length; j += 7)            // loop to support multi-arc
          {
            scaledPath += (parseFloat(args[j]) * sx + " ");                      // rx
            scaledPath += (parseFloat(args[j+1]) * sy + " ");                    // ry
            scaledPath += (args[j+2] + " ");                                     // x-axis-rotation
            scaledPath += (args[j+3] + " ");                                     // large-arc-flag
            scaledPath += (args[j+4] + " ");                                     // sweep-flag
            scaledPath += (parseFloat(args[j+5])* sx +(absCmd ? x : 0) + " ");   // x
            scaledPath += (parseFloat(args[j+6])* sy +(absCmd ? y : 0) + " ");   // y
          }
        }
        else
        {
          // For all other cmdTypes, all numbers should be scaled
          // For all absolute cmdTypes, all numbers should be translated
     
          var scales     = [];
          var translates = [];
          if (cmdType.toUpperCase() === 'H')
          {
            scales.push(sx); // All numbers should be scaled by sx
            translates.push(absCmd ? x : 0); // All numbers should be translated by x
          }
          else if (cmdType.toUpperCase() === 'V')
          {
            scales.push(sy); // All numbers should be scaled by sy
            translates.push(absCmd ? y : 0); // All numbers should be translated by y
          }
          else
          {
            // All other commands take a set of points, so even indices should be scaled
            //  by sx, odd indices by sy
            scales.push(sx);
            scales.push(sy);
            // For absolute commands, even indices should be translated by x, odd indices by y
            translates.push(absCmd ? x : 0);
            translates.push(absCmd ? y : 0);
          }

          for (var j = 0; j < args.length; j++)
          {
            var s = scales[j % scales.length];
            var t = translates[j % translates.length];

            scaledPath += ((parseFloat(args[j])*s+t) + " ");  // scale and translate
          }
        }
     }
  }         // end for

  return scaledPath ;
};

/**
 * Helper function for path simplification
 * @param {number} xMove The number of pixels that will be moved in the horizontal coordinate space.
 * @param {number} yMove The number of pixels that will be moved in the vertical coordinate space.
 * @param {number} scale The scale to test if the the x/y movement will show
 * @return {boolean} Whether or not the x/y movement will show in the given scale.
 */
DvtPathUtils._fitsInScale = function(xMove, yMove, scale) {
  return (Math.abs(xMove) > scale || Math.abs(yMove) > scale);
}

/**
 * Simplifies a given path by scaling it down and discarding pixel movements that are too small to be seen in the 
 * given scale.
 * @param {array} cmdAr The array of commands and coordinates to be converted.
 * @param {number} scale The scale to simplify the path to.
 * @return {string} The simplified string of path commands and coordinates.
 */
DvtPathUtils.simplifyPath = function(cmdAr, scale) {
  var cmd;
  var simplifiedCmdStr = '';
  var partialX = 0;
  var partialY = 0;
  var simplifiedCmd;
  for (var i=0; i<cmdAr.length; i++) {
    if (isNaN(cmdAr[i])) {
      cmd = cmdAr[i];
      if (cmd == 'Z' || cmd=='z') {
        simplifiedCmd = cmd;
        simplifiedCmdStr += cmd;
        partialX = 0;
        partialY = 0;
      }
      continue;
    }
    
    switch (cmd) {
      case 'M':
        partialX = 0;
        partialY = 0;
      case 'm':
        simplifiedCmdStr = simplifiedCmdStr + cmd + cmdAr[i]+ " " +cmdAr[i+1];
        simplifiedCmd = cmd;
        i++;
        break;
      case 'l':
        partialX += cmdAr[i];
        partialY += cmdAr[i+1];
        if (DvtPathUtils._fitsInScale (partialX, partialY, scale)) {
          if (simplifiedCmd != cmd) {
            simplifiedCmd = cmd;
            simplifiedCmdStr += cmd;
          } else {
            simplifiedCmdStr += ' ';
          }
          simplifiedCmdStr = simplifiedCmdStr + partialX + " " + partialY;
          partialX = 0;
          partialY = 0;
        }
        i++;
        break;
      case 'h':
      case 'v':
        if (cmd == 'h')
          partialX += cmdAr[i];
        else
          partialY += cmdAr[i];
        if (DvtPathUtils._fitsInScale (partialX, partialY, scale)) {
          if (partialX != 0 && partialY != 0) {
            if (simplifiedCmd != 'l') {
              simplifiedCmd = 'l';
              simplifiedCmdStr += 'l';
            } else {
              simplifiedCmdStr += ' ';
            }
            simplifiedCmdStr = simplifiedCmdStr + partialX + " " + partialY;
          } else {
            simplifiedCmd = cmd;
            simplifiedCmdStr = simplifiedCmdStr + cmd + (cmd == 'h' ? partialX : partialY);
          }
          partialX = 0;
          partialY = 0;
        }
        break;
      default:
    }
  }
  return simplifiedCmdStr;
}
/**
 * @constructor
 * DvtMarkerDefElem 
 */
function DvtMarkerDefElem() {
  this.Init();
}

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

// DvtMarkerDef Attributes
DvtMarkerDefElem.ATTR_ELEMENTS = "elements";
DvtMarkerDefElem.ATTR_SHAPE = "shape";
DvtMarkerDefElem.ATTR_BORDER_COLOR = "bc";
DvtMarkerDefElem.ATTR_FILL_COLOR = "fc";
DvtMarkerDefElem.ATTR_FILL_PATTERN = "fp";
DvtMarkerDefElem.ATTR_FILL_GRADIENT = "fg";
DvtMarkerDefElem.ATTR_DATA = "d";
DvtMarkerDefElem.ATTR_POINTS = "p";
DvtMarkerDefElem.ATTR_LINE_WIDTH = "lw";

DvtMarkerDefElem.ATTR_FILL_GRAD_COLORS = "c";
DvtMarkerDefElem.ATTR_FILL_GRAD_STOPS = "p";
DvtMarkerDefElem.ATTR_FILL_GRAD_BOUNDS = "b";
DvtMarkerDefElem.ATTR_FILL_GRAD_DIR = "dir";
DvtMarkerDefElem.ATTR_FILL_GRAD_DIR_RADIAL = "gdR";
DvtMarkerDefElem.ATTR_FILL_GRAD_DIR_RIGHT = "gdRi" ;
DvtMarkerDefElem.ATTR_FILL_GRAD_DIR_DOWN = "gdD" ;
DvtMarkerDefElem.ATTR_FILL_GRAD_DIR_45 = "gdD45" ;
DvtMarkerDefElem.ATTR_FILL_GRAD_DIR_135 = "gdD135" ;
DvtMarkerDefElem.ATTR_FILL_GRAD_CX = "cx";
DvtMarkerDefElem.ATTR_FILL_GRAD_CY = "cy";
DvtMarkerDefElem.ATTR_FILL_GRAD_RADIUS = "r";
DvtMarkerDefElem.ATTR_FILL_GRAD_RADIUSX = "rx";
DvtMarkerDefElem.ATTR_FILL_GRAD_RADIUSY = "ry";

DvtMarkerDefElem.ATTR_ANGLES = "ang";
DvtMarkerDefElem.ATTR_CLOSURE_TYPE = "ct";

DvtMarkerDefElem.ATTR_TRANSFORM = "transform";


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


/*-------------------------------------------------------------------------*/
/*   DvtMarkerDef attributes                                                */
/*-------------------------------------------------------------------------*/

/**
 * Gets the shape of the marker. The list of shapes are :
 * "circle"
 * "ellipse"
 * "line"
 * "path"
 * "polygone"
 * "polyline"
 * "rectangle"
 *
 * @return the shape of the marker
 */
DvtMarkerDefElem.prototype.getShape = function() {
  return this.getProperty(DvtMarkerDefElem.ATTR_SHAPE);
};


/**
 * Specifies the shape of the marker. The list of shapes are :
 * "circle"
 * "ellipse"
 * "line"
 * "path"
 * "polygone"
 * "polyline"
 * "rectangle"
 *
 * @param shape the shape of the marker
 */
DvtMarkerDefElem.prototype.setShape = function(shape) {
  this.setProperty(DvtMarkerDefElem.ATTR_SHAPE, shape);
};



/**
 * Gets the data of the component. 
 * @return data of the component
 */
DvtMarkerDefElem.prototype.getData = function() {
  return this.getProperty(DvtMarkerDefElem.ATTR_DATA);
};

/**
 * Sets the data of the component. 
 * @param data data of the component
 */
DvtMarkerDefElem.prototype.setData = function(data) {
  return this.setProperty(DvtMarkerDefElem.ATTR_DATA, data);
};

/**
 * Gets the points of the component. 
 * @return points of the component
 */
DvtMarkerDefElem.prototype.getPoints = function() {
  return this.getProperty(DvtMarkerDefElem.ATTR_POINTS);
};

/**
 * Sets the points of the component. 
 * @param points points of the component
 */
DvtMarkerDefElem.prototype.setPoints = function(points) {
  this.setProperty(DvtMarkerDefElem.ATTR_POINTS, points);
};

/**
 * Gets the closure type of the component. 
 * @return closure type of the component
 */
DvtMarkerDefElem.prototype.getClosureType = function() {
  return this.getProperty(DvtMarkerDefElem.ATTR_CLOSURE_TYPE);
};

/**
 * Sets the closure type of the component. 
 * @param closureType closure type of the component
 */
DvtMarkerDefElem.prototype.setClosureType = function(closureType) {
  this.setProperty(DvtMarkerDefElem.ATTR_CLOSURE_TYPE, closureType);
};


/**
 * Gets the angles of the component. 
 * @return angles of the component
 */
DvtMarkerDefElem.prototype.getAngles = function() {
  return this.getProperty(DvtMarkerDefElem.ATTR_ANGLES);
};

/**
 * Sets the angles of the component. 
 * @param angles angles of the component
 */
DvtMarkerDefElem.prototype.setAngles = function(angles) {
  return this.setProperty(DvtMarkerDefElem.ATTR_ANGLES, angles);
};

/**
 * Gets the border color of the component. 
 * @return border color of the component
 */
DvtMarkerDefElem.prototype.getBorderColor = function() {
  return this.getProperty(DvtMarkerDefElem.ATTR_BORDER_COLOR);
};

/**
 * Sets the border color of the component. 
 * @param borderColor border color of the component
 */
DvtMarkerDefElem.prototype.setBorderColor = function(borderColor) {
  this.setProperty(DvtMarkerDefElem.ATTR_BORDER_COLOR, borderColor);
};


/**
 * Gets the line width of the component. 
 * @return line width of the component
 */
DvtMarkerDefElem.prototype.getLineWidth = function() {
  return this.getProperty(DvtMarkerDefElem.ATTR_LINE_WIDTH);
};

/**
 * Sets the line width of the component. 
 * @param lineWidth line width of the component
 */
DvtMarkerDefElem.prototype.setLineWidth = function(lineWidth) {
  this.setProperty(DvtMarkerDefElem.ATTR_LINE_WIDTH, lineWidth);
};


/**
 * Gets the fill color of the component. 
 * @return fill color of the component
 */
DvtMarkerDefElem.prototype.getFillColor = function() {
  return this.getProperty(DvtMarkerDefElem.ATTR_FILL_COLOR);
};

/**
 * Sets the fill color of the component. 
 * @param fillColor fill color of the component
 */
DvtMarkerDefElem.prototype.setFillColor = function(fillColor) {
  this.setProperty(DvtMarkerDefElem.ATTR_FILL_COLOR, fillColor);
};


/**
 * Gets the fill pattern of the component. 
 * @return fill pattern of the component
 */
DvtMarkerDefElem.prototype.getFillPattern = function() {
  return this.getProperty(DvtMarkerDefElem.ATTR_FILL_PATTERN);
};

/**
 * Sets the fill pattern of the component. 
 * @param fillPattern fill pattern of the component
 */
DvtMarkerDefElem.prototype.setFillPattern = function(fillPattern) {
  this.setProperty(DvtMarkerDefElem.ATTR_FILL_PATTERN, fillPattern);
};


/**
 * Gets the fill gradient of the component. 
 * @return fill gradient of the component
 */
DvtMarkerDefElem.prototype.getFillGradient = function() {
  return this.getProperty(DvtMarkerDefElem.ATTR_FILL_GRADIENT);
};

/**
 * Sets the fill gradient of the component. 
 * @param fillGradient fill gradient of the component
 */
DvtMarkerDefElem.prototype.setFillGradient = function(fillGradient) {
  this.setProperty(DvtMarkerDefElem.ATTR_FILL_GRADIENT, fillGradient);
};


/**
 *   Gets the gradient color array of the component.
 */

DvtMarkerDefElem.prototype.getGradColors = function() {
  var s = this.getProperty(DvtMarkerDefElem.ATTR_FILL_GRAD_COLORS) ;  
  var a = s.split(',') ;
  return a ;
}

/**
 *   Gets the gradient stops of the component.
 */

DvtMarkerDefElem.prototype.getGradStops = function() {
  var s = this.getProperty(DvtMarkerDefElem.ATTR_FILL_GRAD_STOPS) ;  
  var a = s.split(',') ;
  DvtArrayUtils.toFloat(a) ;
 
  return a ;
}

/**
 *   Gets the gradient bounds of the component.
 */

DvtMarkerDefElem.prototype.getGradBounds = function() {
  var s = this.getProperty(DvtMarkerDefElem.ATTR_FILL_GRAD_BOUNDS) ;  
  var a = s.split(',') ;
  DvtArrayUtils.toFloat(a) ;  
  return a ;
} 


/**
 *   Gets the gradient direction.
 */

DvtMarkerDefElem.prototype.getGradDir = function() {
  return  this.getProperty(DvtMarkerDefElem.ATTR_FILL_GRAD_DIR) ;  
} 



/**
 *   Gets the radial gradient radius
 */

DvtMarkerDefElem.prototype.getGradRadius = function() {
  return  this.getProperty(DvtMarkerDefElem.ATTR_FILL_GRAD_RADIUS) ;  
} 


/**
 *   Gets the radial gradient x-radius
 */

DvtMarkerDefElem.prototype.getGradRadiusX = function() {
  return  this.getProperty(DvtMarkerDefElem.ATTR_FILL_GRAD_RADIUSX) ;  
}


/**
 *   Gets the radial gradient y-radius
 */

DvtMarkerDefElem.prototype.getGradRadiusY = function() {
  return  this.getProperty(DvtMarkerDefElem.ATTR_FILL_GRAD_RADIUSY) ;  
}

/**
 *   Gets the radial gradient cx.
 */

DvtMarkerDefElem.prototype.getGradCx = function() {
  return  this.getProperty(DvtMarkerDefElem.ATTR_FILL_GRAD_CX) ;  
} 


/**
 *   Gets the radial gradient cxy
 */

DvtMarkerDefElem.prototype.getGradCy = function() {
  return  this.getProperty(DvtMarkerDefElem.ATTR_FILL_GRAD_CY) ;  
} 


/**
 * Gets the transform of the component. 
 * @return transform of the component
 */
DvtMarkerDefElem.prototype.getTransform = function() {
  return this.getProperty(DvtMarkerDefElem.ATTR_TRANSFORM);
};

/**
 * Sets the transform of the component. 
 * @param transform transform of the component
 */
DvtMarkerDefElem.prototype.setTransform = function(transform) {
  return this.setProperty(DvtMarkerDefElem.ATTR_TRANSFORM, transform);
};

/**
 * @constructor
 * DvtMarkerDef
 */
var DvtMarkerDef = function() {
  this.Init();
}

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

DvtMarkerDef.MARKER_DEF = "markerDef";

// List of marker shapes
DvtMarkerDef.MARKER_DEF_CIRCLE = "c";
DvtMarkerDef.MARKER_DEF_ELLIPSE = "o";
DvtMarkerDef.MARKER_DEF_LINE = "l";
DvtMarkerDef.MARKER_DEF_PATH = "p";
DvtMarkerDef.MARKER_DEF_POLYGON = "pg";
DvtMarkerDef.MARKER_DEF_POLYLINE = "pl";
DvtMarkerDef.MARKER_DEF_RECT = "r";


// DvtMarkerDef Attributes
DvtMarkerDef.ATTR_ELEMENTS = "elements";
DvtMarkerDef.ATTR_BORDER_COLOR = "bc";
DvtMarkerDef.ATTR_LINE_WIDTH = "lw";
DvtMarkerDef.ATTR_FILL_COLOR = "fc";
DvtMarkerDef.ATTR_FILL_PATTERN = "fp";
DvtMarkerDef.ATTR_FILL_GRADIENT = "fg";



DvtMarkerDef.BI_DEFAULT_MARKER_SIZE = 9;

DvtMarkerDef.HUMAN_CMDS = "M 38.07,36.467856 q 13.414,0 13.414,-13.406 l 0,-9.258 q 0,-13.4039999 -13.414," +
"-13.4039999 -13.414,0 -13.414,13.4039999 l 0,9.258 q 0,13.406 13.414,13.406 l 0,0 z m 16.219,7.275 -32.435999," +
"0 q -10.139552,0 -15.9400009,7.443875 Q 0.5,58.133383 0.5,69.156856 l 0,54.396004 12.746001,0 0,-51.609004 q 0," +
"-2.824 0.793,-2.824 0.742,0 0.742,2.709 l 0,124.267994 q 0,2.82401 2.823999,2.82401 l 12.531,0 q 2.824,0 2.824," +
"-2.824 l 0,-66.25 10.219,0 0,66.25 q 0,2.824 2.824,2.824 l 12.528,0 q 2.825,0 2.825,-2.824 l 0,-124.268004 q 0," +
"-2.709 0.839,-2.709 0.792,0 0.792,2.824 l 0,51.609004 12.65,0 0,-54.396004 Q 75.6386,58.132927 70.227626," +
"51.186731 64.428999,43.742856 54.289,43.742856 l 0,0 z";

DvtMarkerDef.HUMAN2_CMDS = "M 306.40625 386.78125 C 304.19988 386.78125 302.40625 389.07579 302.40625 391.90625 " +
"C 302.40625 394.73671 304.19988 397.03125 306.40625 397.03125 C 308.61263 397.03125 310.40625 394.73671 310.40625 " +
"391.90625 C 310.40625 389.07579 308.61263 386.78125 306.40625 386.78125 z M 301.78125 396.0625 C 300.43025 397.2945 " +
"298.28125 400.28125 298.90625 403.15625 C 302.41725 405.79925 309.20225 406.154 314.03125 403 C 314.21825 399.828 " +
"312.68325 397.5635 310.90625 396.0625 C 308.65625 400.7185 304.28125 399.7815 301.78125 396.0625 z ";



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



/*-------------------------------------------------------------------------*/
/*   DvtMarkerDef attributes                                                */
/*-------------------------------------------------------------------------*/

/**
 * Gets the border color of the component. 
 * @return border color of the component
 */
DvtMarkerDef.prototype.getBorderColor = function() {
  return this.getProperty(DvtMarkerDef.ATTR_BORDER_COLOR);
};

/**
 * Sets the border color of the component. 
 * @param borderColor border color of the component
 */
DvtMarkerDef.prototype.setBorderColor = function(borderColor) {
  this.setProperty(DvtMarkerDef.ATTR_BORDER_COLOR, borderColor);
};


/**
 * Gets the line width of the component. 
 * @return line width of the component
 */
DvtMarkerDef.prototype.getLineWidth = function() {
  return this.getProperty(DvtMarkerDef.ATTR_LINE_WIDTH);
};

/**
 * Sets the line width of the component. 
 * @param lineWidth line width of the component
 */
DvtMarkerDef.prototype.setLineWidth = function(lineWidth) {
  this.setProperty(DvtMarkerDef.ATTR_LINE_WIDTH, lineWidth);
};


/**
 * Gets the fill color of the component. 
 * @return fill color of the component
 */
DvtMarkerDef.prototype.getFillColor = function() {
  return this.getProperty(DvtMarkerDef.ATTR_FILL_COLOR);
};

/**
 * Sets the fill color of the component. 
 * @param fillColor fill color of the component
 */
DvtMarkerDef.prototype.setFillColor = function(fillColor) {
  this.setProperty(DvtMarkerDef.ATTR_FILL_COLOR, fillColor);
};


/**
 * Gets the fill pattern of the component. 
 * @return fill pattern of the component
 */
DvtMarkerDef.prototype.getFillPattern = function() {
  return this.getProperty(DvtMarkerDef.ATTR_FILL_PATTERN);
};

/**
 * Sets the fill pattern of the component. 
 * @param fillPattern fill pattern of the component
 */
DvtMarkerDef.prototype.setFillPattern = function(fillPattern) {
  this.setProperty(DvtMarkerDef.ATTR_FILL_PATTERN, fillPattern);
};


/**
 * Gets the fill gradient of the component. 
 * @return fill gradient of the component
 */
DvtMarkerDef.prototype.getFillGradient = function() {
  return this.getProperty(DvtMarkerDef.ATTR_FILL_GRADIENT);
};

/**
 * Sets the fill gradient of the component. 
 * @param fillGradient fill gradient of the component
 */
DvtMarkerDef.prototype.setFillGradient = function(fillGradient) {
  this.setProperty(DvtMarkerDef.ATTR_FILL_GRADIENT, fillGradient);
};


/**
 * Gets the dimensions of the component. 
 * @return dimensions of the component
 */
DvtMarkerDef.prototype.getDimensions = function() {
  var x = this.getProperty("dx");
  var y = this.getProperty("dy");
  var w = this.getProperty("dw");
  var h = this.getProperty("dh");

  if (w && h) {
    return new DvtRectangle(x, y, w, h);
  }
  return null;
};


/**
 * Gets the elements of the marker. The list of elementss are :
 *
 * @return the elements of the marker
 */
DvtMarkerDef.prototype.getElements = function() {
  return this.getProperty(DvtMarkerDef.ATTR_ELEMENTS);
};


/**
 * Add an element to the marker.
 *
 * @param {DvtMarkerDefElem} element to be added
 */
DvtMarkerDef.prototype.addElement = function(element) {
  var elems = this.getElements();
  if (! elems) {
    elems = [];
    this.setProperty(DvtMarkerDef.ATTR_ELEMENTS, elems);
  }
  elems.push(element);
};


var DvtMarkerGradient = function (){}

DvtObj.createSubclass(DvtMarkerGradient, DvtObj, "DvtMarkerGradient");

DvtMarkerGradient.createMarkerGradient = function (color, marker, opacity)
{
  var arColors = [];
  var arRatios = [];
  var arAlphas = [opacity, opacity, opacity, opacity];
  var gfs = null;

  var shapeType = marker.getType();
  var dim = DvtDisplayableUtils._getDimForced (marker.getCtx(), marker);
  var center = dim.getCenter();
  var size = Math.min (dim.w, dim.h);

  if (shapeType != DvtMarker.HUMAN)
  {
     arRatios = [0.0, 0.5, 0.75, 1];
     var c0 = DvtColorUtils.getPastel(color, 0.20);
     var c1 = DvtColorUtils.getPastel(color, 0.10);
     var c2 = DvtColorUtils.getDarker(color, 0.8);
     
     var radius = size / 2.0;
     var cx = center.x;
     var cy = center.y;
     var arColors = [DvtColorUtils.getPound(c0), DvtColorUtils.getPound(c1), color, DvtColorUtils.getPound(c2)];    
     var bound = new DvtRectangle(-radius, -radius, radius * 2.0, radius* 2.0);
     var arBound = [bound.x, bound.y, bound.w, bound.h];
     gfs = new DvtRadialGradientFill(arColors, arAlphas, arRatios, cx, cy, radius, arBound);
  } else
  {
     arRatios = [0.0, 0.3, 0.7, 1];
     var c0 = DvtColorUtils.getPastel(color, 0.20);
     var c1 = DvtColorUtils.getDarker(color, 0.9);
     var c2 = DvtColorUtils.getDarker(color, 0.8);
     var arColors = [DvtColorUtils.getPound(c0), DvtColorUtils.getPound(c1), color, DvtColorUtils.getPound(c2)];    
     var bound = new DvtRectangle(-size/ 2.0, -size /2.0, size, size);
     var arBound = [bound.x, bound.y, bound.w, bound.h];
     gfs = new DvtLinearGradientFill(135, arColors, arAlphas, arRatios, arBound);
  }

  return gfs ;
}
/**
 * DvtMarkerUtils
 */
var DvtMarkerUtils = {_cache:{}};

DvtObj.createSubclass(DvtMarkerUtils, DvtObj, "DvtMarkerUtils");

/**
 * @this {DvtMarkerUtils}
 * parse markerDefXmlString and return a markerDef object
 */
DvtMarkerUtils.createMarkerDef = function(context, markerDefNode) {
  
  var markerDef = new DvtMarkerDef();
  markerDef.setProperties(markerDefNode.getAttributes());

  var childNodes = markerDefNode.getChildNodes();
  var childElems;

  for (var i = 0; i < childNodes.length; i++) {
    var child = childNodes[i];
    if (child) {
      childElems = new DvtMarkerDefElem();
      
      if (child.getName() == 'fillDef') {
        if (child.getChildNodes()) {
          child = child.getChildNodes()[0] ;    // want the child ( e.g. <g> )
        }
      }

      childElems.setProperties(child.getAttributes());
      childElems.setShape(child.getName());
      markerDef.addElement(childElems);
    }
  }

  if (markerDef) {
    this._addMarkerDef(context, markerDef);
  }
  return markerDef;
};



/**
 * @this {DvtMarkerUtils}
 * add a markerDef object to the marker list
 */
DvtMarkerUtils._addMarkerDef = function(context, markerDef) {
  var stageId = context.getStage().getId();
  var markerId = markerDef.getId();
  var markerList = DvtMarkerUtils.getMarkerList(stageId);

  //first look for a cached copy of the custom marker 
  //if not found, add to the custom marker list
  if (! markerList[markerId]) {
    var marker = this.createMultiPaths(context, markerDef, markerId);
    if (marker) {
      // set id on the root 
      marker.setId("custom" + markerId);
      markerList[markerId] = marker;

      // get custom dimensions and cache it in the shape object
      var dim = markerDef.getDimensions();
      if (dim) {
        DvtDisplayableUtils._setDimForced(marker, dim);
      }
    }
  }
};


/**
 * @this {DvtMarkerUtils}
 * Get Custom Marker List
 */
DvtMarkerUtils.getMarkerList = function(stageId)
{
  if (!this._cache[stageId]) {
    this._cache[stageId] = {};
  }
  return this._cache[stageId];
};


/**
 * @this {DvtMarkerUtils}
 * Returns a DvtPath
 * 
 * @param context  the context
 * @param markerDef the custom marker definition
 */
DvtMarkerUtils.createMarkerShape = function(context, markerDefElem, markerDef, markerId) {

  var marker;
  var type = markerDefElem.getShape();

  if (type == DvtMarkerDef.MARKER_DEF_PATH) {
    marker = this.createPathMarker(context, markerDefElem, markerId);
  }
  /*
  else if (type == DvtMarkerDef.MARKER_DEF_CIRCLE ||
      type == DvtMarkerDef.MARKER_DEF_ELLIPSE) {
    marker = this.createCircleMarker(context, markerDef, markerId, type);
  }
  else if (type == DvtMarkerDef.MARKER_DEF_LINE) {
    marker = this.createLineMarker(context, markerDef, markerId);
  }
  else if (type == DvtMarkerDef.MARKER_DEF_POLYGON) {
    marker = this.createPolygoneMarker(context, markerDef, markerId);
  }
  else if (type == DvtMarkerDef.MARKER_DEF_POLYLINE) {
    marker = this.createPolylineMarker(context, markerDef, markerId);
  }
  else if (type == DvtMarkerDef.MARKER_DEF_RECT_TYPE) {
    marker = this.createRectMarker(context, markerDef, markerId);
  }
  */

  //set common attributes
  if (marker) {
    DvtMarkerUtils.setCommonAttrs(markerDefElem, markerDef, marker);
  }

  return marker;
};


/**
 * @type DvtPath or DvtContainer (contains a list of DvtPaths)
 */
DvtMarkerUtils.createMultiPaths = function(context, markerDef, markerId) {
  var shapes = markerDef.getElements();
  if (! shapes || shapes.length == 0)
    return null;

  var root;
  if (shapes.length == 1) {
    root = DvtMarkerUtils.createMarkerShape(context, shapes[0], markerDef, markerId);
  }
  else {
    root = new DvtContainer(context, markerId);
    var child;
    var childElem;
    for (var i = 0; i < shapes.length; i++) {
      child = shapes[i];
      childElem = DvtMarkerUtils.createMarkerShape(context, child, markerDef, markerId + "_" + i);
      if (childElem)
         root.addChild(childElem);
    }
  }
  return root;

};

// type: circle or ellipse
DvtMarkerUtils.createCircleMarker = function(context, markerDef, markerId, type) {
  var points = markerDef.getPoints();
  var cx = points[0];
  var cy = points[1];
  var rx = points[2];
  var ry;
  if (type == DvtMarkerDef.MARKER_DEF_ELLIPSE) {
    ry = points[3];
  }
  var marker ;
  var closureType = markerDef.getClosureType();
  if (closureType) {
    var angles = markerDef.getAngles();
    var anglesStart;
    var anglesExtent;
    if (angles) {
      anglesStart = angles[0];
      anglesExtent = angles[1];
    }
    marker = new DvtArc(context, cx, cy, rx, ry, 
                        anglesStart, anglesExtent, closureType, markerId);
  }
  else {
    marker = new DvtCircle(context, cx, cy, rx, markerId);
  }

  return marker;
};


DvtMarkerUtils.createRectMarker = function(context, markerDef, markerId) {
  var points = markerDef.getPoints();
  var x = points[0];
  var y = points[1];
  var w = points[2];
  var h = points[3];

  return new DvtRect(context, x, y, w, h, markerId);
};


DvtMarkerUtils.createLineMarker = function(context, markerDef, markerId) {
  var points = markerDef.getPoints();
  var x1 = points[0];
  var y1 = points[1];
  var x2 = points[2];
  var y2 = points[3];

  return new DvtLine(context, x1, y1, x2, y2, markerId);
};

DvtMarkerUtils.createPathMarker = function(context, markerDefElem, markerId) {
  var data = markerDefElem.getData();

  return new DvtPath(context, data, markerId);
};


DvtMarkerUtils.createPolygoneMarker = function(context, markerDef, markerId) {
  var points = markerDef.getPoints();

  return new DvtPolygon(context, points, markerId);
};


DvtMarkerUtils.createPolylineMarker = function(context, markerDef, markerId) {
  var points = markerDef.getPoints();

  return new DvtPolyline(context, points, markerId);
};


DvtMarkerUtils.setCommonAttrs = function(markerDefElem, markerDef, marker) {
  // solid, gradient, pattern fill?
  var fc = markerDefElem.getFillColor();
  var fa = null; //TODO getAlpha
  var fg = markerDefElem.getFillGradient();
  var fp = markerDefElem.getFillPattern();

  if (fp) {
    marker.setFill(new DvtPatternFill(fp, fc));
  }
  else if (fg) {                           // look for matching fillDef gradient id
    var elems = markerDef.getElements() ;
    var fd ;
    var len = elems.length ;
    for (var i = 0; i < len; i++) {
       var fillDef = elems[i] ;
       if (fillDef.getShape() == 'g' && fillDef.getId() == fg) {
         fd = fillDef ;
         break ;
       } 
    }   
    
    var stops ;
    var bounds ;
    var dir ;
    var gradCx ;
    var gradCy ;
    var rad ;
    if (fd) {
      fc     = fd.getGradColors() ;
      stops  = fd.getGradStops() ;
      bounds = fd.getGradBounds() ;
      dir    = fd.getGradDir() ;
      if (dir == DvtMarkerDefElem.ATTR_FILL_GRAD_DIR_RADIAL) {
        gradCx =  parseFloat(fd.getGradCx()) ;
        gradCy =  parseFloat(fd.getGradCy()) ;
        rad    =  parseFloat(fd.getGradRadius()) ;
        if (! rad) {
          var radX = parseFloat(fd.getGradRadiusX()) ;    // svg doesn't support rx, ry
          var radY = parseFloat(fd.getGradRadiusY()) ;          
          rad = Math.max(radX, radY) ;           
        }
        marker.setFill(new DvtRadialGradientFill(fc, fa , stops, gradCx, gradCy, rad, bounds));
       }
       else  {
          var angle = 0 ;
          if (dir == DvtMarkerDefElem.ATTR_FILL_GRAD_DIR_45)
            angle = -135 ;
          else if (dir == DvtMarkerDefElem.ATTR_FILL_GRAD_DIR_135)
            angle = -45 ;
          else if (dir == DvtMarkerDefElem.ATTR_FILL_GRAD_DIR_DOWN)
            angle = -90 ;
          marker.setFill(new DvtLinearGradientFill(angle, fc, fa, stops, bounds)); 
       }
    }   
    else { 
      marker.setFill(new DvtLinearGradientFill(0, ["#000", "#fff"])); 
    }
  }
  else if (fc || fa) {
    marker.setSolidFill(fc, fa);
  }

  var lw = parseFloat(markerDefElem.getLineWidth());
  var bc = markerDefElem.getBorderColor();

  if (lw || bc) {
    if (! lw)
      lw = 1 ;       
    if (! bc)
      bc = "black";
    
    var stroke = new DvtSolidStroke(bc, 1, lw);
    marker.setStroke(stroke);    
  }

};


/**
 * get a markerDef from the marker list
 * @type DvtPath or DvtContainer (contains a list of DvtPaths)
 */
DvtMarkerUtils.getCustomMarkerInfo = function(context, markerId) {
  var stageId = context.getStage().getId();
  var markerList = DvtMarkerUtils.getMarkerList(stageId);

  if (markerList) {
    return markerList[markerId];
  }
  else {
    return undefined;
  }
};


/**
 * Returns the built in marker shape given the marker type and skin
 * @param {DvtContext} The platform specific context object
 * @param {String} markerType The marker type
 * @param {String} skin The skin name
 * @return {DvtShape} The built in marker shape
 */
DvtMarkerUtils.getBuiltinMarkerInfo = function(context, markerType, skin) {
  var stageId = context.getStage().getId();
  var markerList = DvtMarkerUtils.getMarkerList(stageId);
  var markerId = skin ? markerType + '_' + skin : markerType;
  var defId = markerList[markerId];
  // if the marker is not in cache, add it to markerList
  if (! defId) {
    if (markerType) {
      var tmarker = new DvtPath(context, skin == DvtCSSStyle.SKIN_ALTA ? DvtMarkerDef.HUMAN2_CMDS : DvtMarkerDef.HUMAN_CMDS, "dvtHuman");
      // cache the dimensions in the shape object
      DvtDisplayableUtils._getDimForced(context, tmarker);
      markerList[markerId] = tmarker;
    }
  }
  return markerList[markerId];
};


/**
 * @this {DvtMarkerUtils}
 * For internal use only
 */
DvtMarkerUtils.clearCached = function(context) {
  var stageId = context.getStage().getId();
  this._cache[stageId] = undefined;
}


// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*  DvtPolygonUtils()       Utility functions for Polygons             */
/*---------------------------------------------------------------------*/

var DvtPolygonUtils = {} ;

DvtObj.createSubclass(DvtPolygonUtils, DvtObj, "DvtPolygonUtils");

/**
 * Returns the bounding box of the supplied polygon coords.
 * @param {Array} aPts an array of consecutive x,y coordinate pairs.
 * @returns {DvtRectangle} the bounding box of the supplied polygon.
 */
DvtPolygonUtils.getDimensions = function(aPts)
{
  if ((! aPts) || (aPts.length=== 0)) {
    return new DvtRectangle() ;
  }

  var minX = Number.MAX_VALUE ;
  var maxX = Number.MIN_VALUE ;
  var minY = Number.MAX_VALUE ;
  var maxY = Number.MIN_VALUE ;

  var len = aPts.length ;
  var x, y ;
  for (var i = 0; i < len; i++) {

     x = aPts[i++];
     y = aPts[i];
     minX = Math.min(minX, x) ;
     maxX = Math.max(maxX, x) ;
     minY = Math.min(minY, y) ;
     maxY = Math.max(maxY, y) ;
  }

  return new DvtRectangle(minX, minY, Math.abs(maxX - minX), Math.abs(maxY - minY)) ;
};


// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*  DvtTextUtils()       Utility functions for text                    */
/*---------------------------------------------------------------------*/

var DvtTextUtils = {} ;

DvtObj.createSubclass(DvtTextUtils, DvtObj, "DvtTextUtils");

/** private **/
DvtTextUtils._cachedTextDimensions = {};

/*---------------------------------------------------------------------*/
/*   applyFont()                                                       */
/*---------------------------------------------------------------------*/
DvtTextUtils.applyFont = function(text, font) {
   
   text.setSolidFill(font.color, font.alpha) ;
   text.alignBaseline() ;
   
   var style = '';                        //  Handle bold, italic, underline
   if (font.italic)  {
     style = 'font-style:italic; ' ;
   }
   if (font.bold)  {
     style += 'font-weight:bold; ' ;
   }
   if (font.underline)  {
     style += 'text-decoration:underline;' ;
   }
   style += 'font-size:' + font.size+';';
   
   if(font.name)
    style += 'font-family:' + font.name + ';' ;

   if (style) {
     style = new DvtCSSStyle(style) ;
     text.setCSSStyle(style) ;
   }
};

DvtTextUtils.formatTextString = function(tooltip)
{
    var fullText = "";
    if (!tooltip)
        return fullText;

    tooltip = tooltip.replace(/\n/g, '<br>'); // replace logical newlines sequences
    var tooltipTextArray = tooltip.split("<br>");        
    if (tooltipTextArray) {
        var shortArray = new Array();
        for (var i=0; i< tooltipTextArray.length; i++) {
            var txt = tooltipTextArray[i];
            if (txt != null && txt != "") {
                shortArray.push(txt);
            }
        }
        for (var i=0; i< shortArray.length; i++) {
            fullText += shortArray[i];
            if (i < shortArray.length - 1) {
                fullText += DvtTextArea.NEW_LINE;
            }
        }
    }
    return fullText;
}

/**
 * Conservatively estimates the width and height of the specified text object.  This function uses a cache to 
 * guess the width and height of the specified text string.  This function relies on the fact that
 * all text strings with the same CSS style return the same height from getDimensions.  Users of 
 * this function can avoid expensive getDimensions calls when checking to see if a text string 
 * would fit in a certain size.
 * @param {DvtText} text
 * @return {DvtDimension} An object defining the estimated width and height.  This should always be larger than the 
 *                        actual width and height.
 */
DvtTextUtils.guessTextDimensions = function(text) {
  var textString = text.getTextString();
  var cssStyle = text.getCSSStyle();
  var cssStyleStr =  cssStyle ? cssStyle.toString() : "";
  
  // Check whether a cached size is already available
  var cachedDims = DvtTextUtils._cachedTextDimensions[cssStyleStr];
  
  // Compute the dimensions of a representative character and add to the cache
  if(cachedDims == null) {    
    // Measure the width and height of a single character, so that we can estimate for all strings
    text.setTextString("W");
    
    // Saves the parent and the index
    var parent = text.getParent();
    var index;
    if (parent)
      index = parent.getChildIndex(text);
    
    var stage = text.getCtx().getStage();
    var dims = text.measureDimensions(stage);
    
    // Use a fudge factor for the width, since 'W' may not be the largest character
    cachedDims = {w: 1.1 * dims.w, h: dims.h};
    DvtTextUtils._cachedTextDimensions[cssStyleStr] = cachedDims;
    
    // Restore the stage and text string
    if (parent && text.getParent() != parent)
      parent.addChildAt(text, index);
    
    text.setTextString(textString);
  }
  
  // Use the cached size to guess at the string length
  return new DvtDimension(cachedDims.w * textString.length, cachedDims.h);
}


/**
 * Fits text in the provided space. This function uses DvtTextUtils.guessTextDimensions before calling setMaxWidth
 * in order to avoid expensive getDimensions calls. It adds the text to the container, and removes it if the text cannot
 * fit at all in the container.
 * @param {DvtText} text
 * @param {Number} maxWidth The maximum width of the text
 * @param {Number} maxHeight The maximum height of the text
 * @param {DvtContainer} container The parent of the text
 * @param {Number} minChars (optional) The minimum number of characters that should be displayed before ellipsis if 
 *     truncation occurs. If this argument is skipped, the default is 1 character.
 * @return {Boolean} False if the text cannot fit at all, true otherwise.
 */
DvtTextUtils.fitText = function(text, maxWidth, maxHeight, container, minChars) {
  // estimate the dims conservatively
  var estimatedDims = DvtTextUtils.guessTextDimensions(text);
  if (estimatedDims.h > maxHeight) {
    return false;
  } if (estimatedDims.w < maxWidth) {
    container.addChild(text);
    return true;
  }
  
  // truncate the text if needed
  container.addChild(text);
  if (isNaN(minChars) || minChars == null)
    minChars = 1;
  text.setMaxWidth(maxWidth, minChars);
  
  // check if the truncated text can fit
  if (text.getTextString() == "") {
    container.removeChild(text);
    return false;
  } else {
    return true;
  }
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*  DvtDisplayable         Base object for displayable objects         */
/*---------------------------------------------------------------------*/
/**
 *  Base class for displayable objects.
 *  @extends DvtObj
 *  @class DvtDisplayable is the base class for all displayable objects (derived from {@link DvtDisplayable}).
 *  Must be superclassed  - do not use directly.
 *  <p>Example:<br><br><code>
 *  var line = new DvtLine(context, 10,10, 30, 50) ; &nbsp; &nbsp; ({@link DvtLine} is a descendent
 *  of DvtDisplayable). <br>
 *  <code>
 *  @constructor
 *  @param {DvtContext} context The platform specific context object.
 */
var DvtDisplayable = function (context) {
  this.Init(context);
};

DvtObj.createSubclass(DvtDisplayable, DvtObj, "DvtDisplayable");

/**
 *  Object initializer.
 *  @protected
 */
DvtDisplayable.prototype.Init = function (context) {
  this._parent = null;// parent obj
  this._impl.setObj(this);// point implementation back to the real
  // controlling object.
  this._context = context;

  this._listenerObj = null;// placeholder for object to store event listeners
  
  this.ClipPathId = null;

  this._matrix = null;

  this._drawEffects = null;

  this._ariaRole = null;
  this._ariaProperties = {};
};

/**
 * Internal DVT toolkit framework use only. Returns the platform specific
 * implementation for this object (such as {@link DvtSvgRect}).
 * @protected
 * @type Object
 */
DvtDisplayable.prototype.getImpl = function () {
  return this._impl;
};

/**
 * Returns the application context.
 * @return {DvtContext}
 */
DvtDisplayable.prototype.getCtx = function () {
  return this._context;
};

/**
 *  Sets a clipping region for this object.
 *  @param {DvtGraphClipPath}  cp  the DvtGraphClipPath object specifying the clipping region.
 */
DvtDisplayable.prototype.setClipPath = function (cp) {
  // Update clip path reference count on the context
  if (cp) {
    if (this.ClipPathId != cp.getId()) {
      this._context.removeClipPath(this.ClipPathId);
      this.ClipPathId = cp.getId();
      this._context.addClipPath(this.ClipPathId);
    }
  } else {
    this._context.removeClipPath(this.ClipPathId);
    this.ClipPathId = null;
  }
  this._impl.setClipPath(cp);
};


/**
 *  Set the platform specific implementation.  Dvt internal use only
 */
DvtDisplayable.prototype.setImpl = function (impl) {
  this._impl = impl;
};

/**
 * Returns the current pixel hinting state.
 * @type Boolean
 */
DvtDisplayable.prototype.getPixelHinting = function () {
  return this._impl.getPixelHinting();
};

/**
 * Enables/disables pixel hinting.
 * @param {Boolean} id  true if pixel hinting should be interpreted/applied by the
 * implementation platform.
 */
DvtDisplayable.prototype.setPixelHinting = function (bHint) {
  this._impl.setPixelHinting(bHint);
};

/*---------------------------------------------------------------------*/
/*   get/setId()                                                       */
/*---------------------------------------------------------------------*/
/**
 * Returns the id of this object.
 * @type String
 */
DvtDisplayable.prototype.getId = function () {
  return this._impl.getId();
};
/**
 * Sets the id of this object.
 * @param {String} id  The id for the object.
 */
DvtDisplayable.prototype.setId = function (id) {
  this._impl.setId(id);
};

/**
 * Returns the parent of this object.
 * @type DvtDisplayable
 */
DvtDisplayable.prototype.getParent = function () {
  return this._parent;
};

/**
 * Sets the parent of this object.
 * @param {DvtDisplayable} parent
 */
DvtDisplayable.prototype.setParent = function (parent) {
  this._parent = parent;
};

DvtDisplayable.prototype.ConvertCoordSpaceRect = function (rect, targetCoordinateSpace) {
  // First calculate relative to the stage
  var stageP1 = this.localToStage(new DvtPoint(rect.x, rect.y));
  var stageP2 = this.localToStage(new DvtPoint(rect.x + rect.w, rect.y + rect.h));

  // Then convert relative to target
  var targetP1 = targetCoordinateSpace.stageToLocal(stageP1);
  var targetP2 = targetCoordinateSpace.stageToLocal(stageP2);

  return new DvtRectangle(targetP1.x, targetP1.y, targetP2.x - targetP1.x, targetP2.y - targetP1.y);
};

/**
 * Returns the bounds of the displayable relative to the target coordinate space.  If the target
 * coordinate space is not specified, returns the bounds relative to this displayable.
 * @param {DvtDisplayable} targetCoordinateSpace The displayable defining the target coordinate space.
 * @return {DvtRectangle} The bounds of the displayable relative to the target coordinate space.
 */
DvtDisplayable.prototype.getDimensions = function (targetCoordinateSpace) {
  var bounds = this.getImpl().getDimensions();
  if (!targetCoordinateSpace || targetCoordinateSpace === this)
    return bounds;
  else { // Calculate the bounds relative to the target space
    return this.ConvertCoordSpaceRect(bounds, targetCoordinateSpace);
  }
};

//BUG FIX 13842194:
/**
 * Returns the bounds of the displayable relative to the target coordinate space, including stroke width.  If the target
 * coordinate space is not specified, returns the bounds relative to this displayable.
 * @param {DvtDisplayable} targetCoordinateSpace The displayable defining the target coordinate space.
 * @return {DvtRectangle} The bounds of the displayable relative to the target coordinate space.
 */
DvtDisplayable.prototype.getDimensionsWithStroke = function (targetCoordinateSpace) {
  var bounds = this.getImpl().getDimensionsWithStroke();
  if (!targetCoordinateSpace || targetCoordinateSpace === this)
    return bounds;
  else { // Calculate the bounds relative to the target space
    return this.ConvertCoordSpaceRect(bounds, targetCoordinateSpace);
  }
};

/**
 *  Gets the visibility of this object.
 *  @type {Boolean}
 *  @returns True if the object is visible, else false.
 */
DvtDisplayable.prototype.getVisible = function()
{
  return this._impl.getVisible();
};
/**
 *  Enables/disables the visibility of this object.
 *  @param {Boolean}  bVis  True if the object is to be visible, else false if
 *  it is to be hidden.
 */
DvtDisplayable.prototype.setVisible = function(bVis)
{
  this._impl.setVisible(bVis);
};

/**
 * Adds an event listener.
 * @param {string} type the event type
 * @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
 */
DvtDisplayable.prototype.addEvtListener = function (type, listener, useCapture, obj) {
  // Store a reference to the listener
  var listenersArray = this._getListeners(type, useCapture, true);
  listenersArray.push(listener);
  listenersArray.push(obj);

  // Call the impl so that it can add the actual listener
  this._impl.addListener(type, useCapture);
}

/**
 * Removes an event listener.
 * @param {string} type the event type
 * @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
 */
DvtDisplayable.prototype.removeEvtListener = function (type, listener, useCapture, obj) {
  // Remove the listener
  var listenersArray = this._getListeners(type, useCapture, false);
  if (listenersArray) {
    for (var i = 0;i < listenersArray.length;i += 2) {
      if (listenersArray[i] === listener && 
           listenersArray[i+1] === obj) {
        listenersArray.splice(i, 2);
        break;
      }
    }
  }

  // Call the impl so that it can remove the actual listener.
  // Note that the array itself is intentionally not removed.
  if (listenersArray && listenersArray.length <= 0)
    this._impl.removeListener(type, useCapture);
}

/**
 * Returns the listeners of the given event type and capture mode.
 * @param {string} type the event type
 * @param {string} useCapture whether the listener operates in the capture phase
 * @param {boolean} createNew whether the array should be created if it doesn't exist
 * @return {array} the mutable Array of listeners
 * @private
 */
DvtDisplayable.prototype._getListeners = function (type, useCapture, createNew) {
  // First find the object where the listener arrays are stored
  if (!this._listenerObj) {
    if (createNew) {
      this._listenerObj = {
      };
    }
    else {
      return null;
    }
  }

  // Then find the array for this event type, creating if necessary 
  var eventKey = type + "_" + (useCapture ? true : false);// for example: "click_true"  
  var listenersArray = this._listenerObj[eventKey];
  if (!listenersArray && createNew) {
    listenersArray = [];
    this._listenerObj[eventKey] = listenersArray;
  }

  return listenersArray;
};

/**
 * Notifies all applicable event listeners of the given event.
 * @param {DvtMouseEvent} event
 * @param {boolean} useCapture whether the listener operates in capture phase
 */
DvtDisplayable.prototype.FireListener = function (event, useCapture) {
  // workaround for bug 15880934
  var type = event.getType ? event.getType() : event.type;
  var listenersArray = this._getListeners(type, useCapture, false);
  if (listenersArray) {
    for (var i = 0;i < listenersArray.length;i += 2) {
      var obj = listenersArray[i + 1];
      if (listenersArray[i]) {
        listenersArray[i].call(obj, event);
      }
    }
  }
};

/*---------------------------------------------------------------------*/
/*   CSS Style Support                                                 */
/*---------------------------------------------------------------------*/

/**
 * Returns the DvtCSSStyle of this object.
 * @type DvtCssStyle
 */
DvtDisplayable.prototype.getCSSStyle = function () {

  return this._impl.getCSSStyle();
}

/**
 * Sets the DvtCSSStyle of this object.
 * @param {DvtCssStyle} style The DvtCSSStyle of this object.
 */
DvtDisplayable.prototype.setCSSStyle = function(style)
{
  this._impl.setCSSStyle(style);
}


/**
 * Sets the cursor on this object.
 * @param {String} cursor type
 */
DvtDisplayable.prototype.setCursor = function(cursorType)
{
  this._impl.setCursor(cursorType);
};


/**
 * Gets the cursor used on this object.
 * @type {String}
 */
DvtDisplayable.prototype.getCursor = function()
{
  return this._impl.getCursor();
};


/**
 * Sets whether mouse events are enabled on this object.
 * @param {boolean} whether mouse events are enabled
 */
DvtDisplayable.prototype.setMouseEnabled = function(bEnabled)
{
  this._impl.setMouseEnabled(bEnabled);
};


/**
 * Gets whether mouse events are enabled on this object.
 * @type {boolean}
 */
DvtDisplayable.prototype.isMouseEnabled = function()
{
  return this._impl.isMouseEnabled();
};

/**
 *  Returns the alpha-channel.
 *  @type number
 */
DvtDisplayable.prototype.getAlpha = function()
{
  return this._impl.getAlpha();
};

/**
 *  Sets the alpha-channel.
 *  @param {number} alpha A value between 0 (invisible) and 1 (opaque).
 */
DvtDisplayable.prototype.setAlpha = function(alpha)
{
  this._impl.setAlpha(alpha);
};

/**  Returns the horizontal translation applied to this container.
 *  @type Number
 *  @returns horizontal translation, in pixels
 */
DvtDisplayable.prototype.getTranslateX = function()
{
  //if (this._translateX)
   if (this._matrix)
   {
    //return this._translateX ;
     
    //get the value from the matrix because other 
    //transformations may have changed it
    return this._matrix._decompose()[DvtMatrix._DECOMP_TX];
  }

  return 0;
};

/**  Set the horizontal translation to apply to this container.
 *  @param {Number} tx   The horizontal translation to apply, in pixels.
 */
DvtDisplayable.prototype.setTranslateX = function(tx)
{
  //the matrix is the ultimate source of truth, because
  //it contains all the transform information
  var mat = this.getMatrix().clone();
  var decomp = mat._decompose();
  decomp[DvtMatrix._DECOMP_TX] = tx;
  mat._recompose(decomp);
  this.setMatrix(mat);
};

/**  Return the vertical translation applied to this container.
 *  @type Number
 *  @returns vertical translation, in pixels
 */
DvtDisplayable.prototype.getTranslateY = function()
{
  //if (this._translateY)
   if (this._matrix)
   {
    //return this._translateY ;
     
    //get the value from the matrix, because other 
    //transformations may have changed it
    return this._matrix._decompose()[DvtMatrix._DECOMP_TY];
  }

  return 0;
};

/**  Set the vertical translation to apply to this container.
 *  @param {Number} ty   The vertical translation to apply, in pixels.
 */
DvtDisplayable.prototype.setTranslateY = function(ty)
{
  //the matrix is the ultimate source of truth, because
  //it contains all the transform information
  var mat = this.getMatrix().clone();
  var decomp = mat._decompose();
  decomp[DvtMatrix._DECOMP_TY] = ty;
  mat._recompose(decomp);
  this.setMatrix(mat);
};

/**  Return the horizontal scale applied to this container.
 *  @type Number
 *  @returns horizontal scale
 */
DvtDisplayable.prototype.getScaleX = function()
{
  //if (this._scaleX != null)
   if (this._matrix)
   {
    //return this._scaleX ;
     
    //get the value from the matrix, because other 
    //transformations may have changed it
    return this._matrix._decompose()[DvtMatrix._DECOMP_SX];
  }

  return 1;
};

/**  Set the horizontal scale to apply to this container.
 *  @param {Number} sx   The horizontal scale to apply.
 */
DvtDisplayable.prototype.setScaleX = function(sx)
{
  //the matrix is the ultimate source of truth, because
  //it contains all the transform information
  var mat = this.getMatrix().clone();
  var decomp = mat._decompose();
  decomp[DvtMatrix._DECOMP_SX] = sx;
  mat._recompose(decomp);
  this.setMatrix(mat);
};

/**  Return the vertical scale applied to this container.
 *  @type Number
 *  @returns vertical scale
 */
DvtDisplayable.prototype.getScaleY = function()
{
  //if (this._scaleY != null)
   if (this._matrix)
   {
    //return this._scaleY ;
     
    //get the value from the matrix, because other 
    //transformations may have changed it
    return this._matrix._decompose()[DvtMatrix._DECOMP_SY];
  }

  return 1;
};

/**  Set the vertical scale to apply to this container.
 *  @param {Number} sy   The horizontal scale to apply.
 */
DvtDisplayable.prototype.setScaleY = function(sy)
{
  //the matrix is the ultimate source of truth, because
  //it contains all the transform information
  var mat = this.getMatrix().clone();
  var decomp = mat._decompose();
  decomp[DvtMatrix._DECOMP_SY] = sy;
  mat._recompose(decomp);
  this.setMatrix(mat);
};

/**  Return the rotation applied to this container.
 *  @type Number
 *  @returns rotation, in radians
 */
DvtDisplayable.prototype.getRotation = function()
{
  //if (this._rotation != null)
   if (this._matrix)
   {
    //return this._rotation ;
     
    //get the value from the matrix, because other 
    //transformations may have changed it
    return this._matrix._decompose()[DvtMatrix._DECOMP_R];
  }

  return 0;
};

/**  Set the rotation to apply to this container.
 *  @param {Number} angleRads   The rotation to apply, in radians.
 */
DvtDisplayable.prototype.setRotation = function(angleRads)
{
  //the matrix is the ultimate source of truth, because
  //it contains all the transform information
  var mat = this.getMatrix().clone();
  var decomp = mat._decompose();
  decomp[DvtMatrix._DECOMP_R] = angleRads;
  mat._recompose(decomp);
  this.setMatrix(mat);
};

/**  Return the transformation matrix applied to this container.
 *  @type DvtMatrix
 *  @returns transformation matrix
 */
DvtDisplayable.prototype.getMatrix = function()
{
   if (this._matrix)
   {
    return this._matrix;
  }

  return new DvtMatrix();
};

/**
 * Set the transformation matrix to apply to this container.
 * When set, the matrix becomes permanently locked so that it becomes immutable.
 * A clone must be created if any updates to the matrix need to be made.
 * The same matrix can be set on more than one displayable irregardless of locking.
 * @param {DvtMatrix} mat   The transformation matrix to apply.
 */
DvtDisplayable.prototype.setMatrix = function (mat) 
{
  // treat setting identity matrix same as setting null matrix
  if (mat && mat.isIdentity())
    mat = null;
    
  if (this._matrix != mat) {
    this._matrix = mat;

    // Lock the new matrix if it isn't already locked, before setting it on this displayable.  
    // The same matrix can be set on more than one displayable irregardless of locking.
    if (this._matrix && !this._matrix.isLocked())
      this._matrix.__lock();
    
    this._impl.setMatrix(mat);
  }
};


DvtDisplayable.prototype.getNumDrawEffects = function()
{
  if (!this._drawEffects)
  {
    return 0;
  }

  return this._drawEffects.length;
};

DvtDisplayable.prototype.getDrawEffectAt = function(index)
{
  if (!this._drawEffects || 
      index < 0 || 
      index >= this._drawEffects.length)
  {
    return null;
  }

  return this._drawEffects[index];
};

DvtDisplayable.prototype.addDrawEffect = function (effect) 
{
  if (!this._drawEffects) 
  {
    this._drawEffects = [];
  }

  //lock the effect
  effect.__lock();

  this._drawEffects.push(effect);

  this.getImpl().applyDrawEffects(this._drawEffects);
};

DvtDisplayable.prototype.addDrawEffectAt = function (effect, index) 
{
  if (!this._drawEffects) 
  {
    this._drawEffects = [];
  }

  //lock the effect
  effect.__lock();

  if (index >= this._drawEffects.length)
  {
    this._drawEffects.push(effect);
  }
  else
  {
    this._drawEffects.splice(index, 0, effect);
  }

  this.getImpl().applyDrawEffects(this._drawEffects);
};

DvtDisplayable.prototype.removeDrawEffect = function(effect)
{
  var index = this.getDrawEffectIndex(effect);
  if (index >  - 1) 
  {
    this._drawEffects.splice(index, 1);
    this.getImpl().applyDrawEffects(this._drawEffects);
  }
};

DvtDisplayable.prototype.removeDrawEffectAt = function(index)
{
  if (this._drawEffects)
  {
    var numEffects = this._drawEffects.length;
    if (index >= 0 && index < numEffects) 
    {
      this._drawEffects.splice(index, 1);
      this.getImpl().applyDrawEffects(this._drawEffects);
    }
  }
};

DvtDisplayable.prototype.removeAllDrawEffects = function () 
{
  if (this._drawEffects) 
  {
    this._drawEffects = null;
    this.getImpl().applyDrawEffects(this._drawEffects);
  }
};

DvtDisplayable.prototype.getDrawEffectIndex = function(effect)
{
  if (this._drawEffects)
  {
    var numEffects = this._drawEffects.length;
    for (var i = 0; i < numEffects; i++)
    {
      if (effect === this._drawEffects[i])
      {
        return i;
      }
    }
  }
  return  - 1;
};

DvtDisplayable.prototype.applyDrawEffects = function ()
{
  this.getImpl().applyDrawEffects(this._drawEffects);
}


/**
 * Sets the WAI-ARIA role. Only supported in SVG and XML toolkits.
 * @param {String} role The role attribute value.
 */
DvtDisplayable.prototype.setAriaRole = function (role) {
  if (this._impl.setAriaRole)
    this._impl.setAriaRole(role);

  this._ariaRole = role;
}

/**
 * Gets the WAI-ARIA role.
 * @return {String} The role attribute value.
 */
DvtDisplayable.prototype.getAriaRole = function () {
  return this._ariaRole;
}

/**
 * Sets the WAI-ARIA property/state. Only supported in SVG and XML toolkits.
 * @param {String} property The property/state attribute name. The prefix "aria-" should be skipped.
 * @param {String} value The property/state attribute value.
 */
DvtDisplayable.prototype.setAriaProperty = function (property, value) {
  if (!property)
    return;

  if (this._impl.setAriaProperty) {
    if (property == 'label' && value) {
      // replace line break characters (\n and <br/>) with semicolons
      value = value.replace(/\n/g, "; ");
      value = value.replace(/\<br\/\>/gi, "; ");
    }
    this._impl.setAriaProperty(property, value);
  }

  this._ariaProperties[property] = value;

  if (property != 'hidden')
    this.setAriaProperty('hidden', null);
}

/**
 * Gets the WAI-ARIA property/state.
 * @param {String} property The property/state attribute name. The prefix "aria-" should be skipped.
 * @return {String} The property/state attribute value.
 */
DvtDisplayable.prototype.getAriaProperty = function (property) {
  if (property) {
    return this._ariaProperties[property];
  } else {
    return null;
  }
}



/**
 * Convert a point from stage coords to local coords.
 * @param {DvtPoint}  point  point in stage coords
 * @type DvtPoint
 */
DvtDisplayable.prototype.stageToLocal = function(point)
{
  return this._impl.stageToLocal(point);
};

/**
 * Convert a point from local coords to stage coords.
 * @param {DvtPoint}  point  point in local coords
 * @type DvtPoint
 */
DvtDisplayable.prototype.localToStage = function(point)
{
  return this._impl.localToStage(point);
};

/**
 * Get an array of objects in the tree from this displayable to the stage.
 * The returned array is ordered with this displayable first and the stage
 * last, like [this, this.getParent(), ... , stage].
 * @type array
 */
DvtDisplayable.prototype.getPathToStage = function()
{
  var displayable = this;
  var array = [];
   while (displayable)
   {
    array.push(displayable);
    displayable = displayable.getParent();
  }
  return array;
};

/*---------------------------------------------------------------------*/
/*    destroy()                                                        */
/*---------------------------------------------------------------------*/
/**
 *    Destroy the displayable.  It should no longer be used or displayed
 */
DvtDisplayable.prototype.destroy = function()
{
  //FIX BUG 12875374: Remove all draw effects at once, because removing
  //them one by one results in the remaining ones being reapplied at each
  //iteration.  Since this object is being destroyed, it is most likely 
  //disconnected from the display list.  When the draw effects are applied,
  //they may call getBBox() on the element, which can result in errors in
  //Firefox when the object is disconnected, and the view not being rendered.
  this.removeAllDrawEffects();
  if (this.ClipPathId)
    this.setClipPath(null);
};

/**
 * Creates a copy of this displayable wrapped by a new container
 * @return {DvtContainer} a copy of this displayable
 */
DvtDisplayable.prototype.createCopy = function() {
  var copy = new DvtContainer(this.getCtx(), "clonedContainer");
  if (this._impl.createCopy) {
    this._impl.createCopy(copy);
  }
  return copy;
}

/**
 * Dispatch event
 * @param {String} eventType
 * @param {String} params Optional parameters used to initialize the event
 */
DvtDisplayable.prototype.dispatchDisplayableEvent = function(eventType, params) 
{
  this._impl.dispatchDisplayableEvent(eventType, params);
}

/**
 * Sets/adds desc element to the SVG node.
 * @param {String} descTxt The description value.
 */
DvtDisplayable.prototype.setDesc = function(descTxt) 
{
  if (this._impl.setDesc)
    this._impl.setDesc(descTxt);
}

/**
 * Dispatches the native event to the internal dom element
 * Note: Use with caution
 * @param {Object} event
 */
DvtDisplayable.prototype.dispatchNativeEvent = function(event) 
{
  if (this._impl.dispatchNativeEvent)
    this._impl.dispatchNativeEvent(event);
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/* DvtContainer   A container object for shapes and shape derivatives  */
/*---------------------------------------------------------------------*/
/** 
 * A container for shapes, shape derivatives, and objects extended from DvtObj.
 * @extends DvtDisplayable
 * @class DvtContainer is a class that is a subclass of {@link DvtDisplayable}.
 * @constructor
 * @param {DvtContext} context  The platform dependent context object (such
 *                              as {@link DvtSvgContext}).
 * @param {String}     id      Optional id for this object.
 */ 
var DvtContainer = function(context, id)
{
  this.Init(context, id) ; 
};

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



/*---------------------------------------------------------------------*/
/*   contains()                                                        */
/*---------------------------------------------------------------------*/
/**  Returns true if object is contained by this container.
  *  @param {Object} obj   The object to be searched for.
  *  @param {Boolean} bDeep   An optional boolean specifying whether the search
  *                           is to be shallow or deep.  If omitted (or false
  *                           is specified), a shallow  search of the container's
  *                           direct children is made. If true. the full child tree
  *                           hierachy is searched.
  *  @returns {boolean}  True if object is contained by this container, else false.
  */ 
DvtContainer.prototype.contains = function(obj, bDeep)
{
  var  bRet = false ;

  if (bDeep === undefined) {
    bDeep = false ;
  }

  if (! bDeep) {
     bRet = (this._findChild(obj) >= 0) ;
  }
  else {
    var  iter = this.getIterator(bDeep) ;
    var  o ;
    while (o = iter.getNext()) {
       if (o === obj) {
         bRet = true ;
         break ;
       }
    }
  }

  return bRet ;
} ;



/**
 * @protected
 */
DvtContainer.prototype.Init = function(context, id)
{
  if (! this.getImpl()) {
    this.setImpl(context.getImplFactory().newContainer(id)) ;
  }
  DvtContainer.superclass.Init.call(this, context) ;

  this._arList = null ;    // children
} ;


/*---------------------------------------------------------------------*/
/*   addChild()           Add a child to this container                */
/*---------------------------------------------------------------------*/
/**
  *  Adds the specified object as a child of this container. The object is
  *  added to the end of the child list.  If the object is already a child,
  *  it will be moved to the end of the child list.
  *  @param {DvtObj} obj  The object to be added (must be a descendent of {@link DvtObj}).
  */
DvtContainer.prototype.addChild = function(obj)
{
  if (obj) {
    // Remove the object from its current parent
    var oldParent = obj.getParent();
    if(oldParent)
      oldParent.removeChild(obj);

    //initialize _arList after removing child from parent in case
    //this container was the parent, which could result in removeChild()
    //setting _arList to null if the last child was removed
    if (!this._arList) {
      this._arList = [] ;
    }
    
    // Add the object to this container
    obj.setParent(this) ;
    this._impl.addChild(obj.getImpl()) ;  // perform platform specific add operation 
    this._arList.push(obj) ;
  }
} ;

/**
  *  Adds the specified object as a child of this container at the specified index. Returns
  *  immediately if the index position doesn't already exist in the child list (unless the
  *  index equals the number of children).  If a currently occupied index is specified, the
  *  current child at that position and all subsequent children are moved one position
  *  higher in the list.  If the object is a child of another container, it is removed and
  *  reparented in the new container. Note that the action of this method is to remove the
  *  object from the current container (which may also be the container that it will be
  *  added to), so for an object "move" in the same container, all objects with a higher
  *  index will now move one position lower.  The object is now inserted at the supplied
  *  index.
  *  @param {DvtObj} obj  The object to be added (must be a descendent of {@link DvtObj}).
  *  @param {number} index The index at which to add this object
  */
DvtContainer.prototype.addChildAt = function(obj, index)
{
  this.AddChildAt(obj, index);
};

DvtContainer.prototype.AddChildAt = function(obj, index) {
  if((index >= 0) && (index <= this.getNumChildren()) && obj)     // validate index
  {
     var curIndex = this._findChild(obj) ;        // note if obj is child of this container

     if ((curIndex >= 0) && (curIndex == index))  // do nothing if child of this container
       return ;                                   // and move is to current position

     var oldParent = obj.getParent();             // remove object from current parent
     if (oldParent)
       oldParent.removeChild(obj);
     obj.setParent(this) ;                        // reparent


     // If obj is child of this container and the intent is to place it after the last
     // object (before the above remove was done), decrement the index.

     if (curIndex >= 0)  {
       if (index == (this.getNumChildren() + 1))
          index-- ;
     }

     this._impl.addChildAt(obj.getImpl(), index) ;  // perform platform specific add operation 
    
     if (! this._arList) {
       this._arList = [] ;
     }
     this._arList.splice(index, 0, obj) ;
  }
}

/*---------------------------------------------------------------------*/
/*  _findChild()  Return index of specified child obj in the internal  */
/*                list, or -1 if not found.                            */
/*---------------------------------------------------------------------*/
/**
  * Returns index of specified child obj in the internal list, or -1 if not
  * found. 
  * @type DvtObj
  * @private
  */
DvtContainer.prototype._findChild = function(obj)
{
   var  idx = -1 ;

   if (this._arList) {
      var len = this._arList.length ;
      for (var i = 0; i < len; i++) {            // find the obj
         if (this._arList[i] === obj) {
           idx = i ;
           break ;
         }
      }
   }

   return idx ;
} ;



/*---------------------------------------------------------------------*/
/*    getChildAt()                                                     */
/*---------------------------------------------------------------------*/
/**
  *   Returns the child at the specified zero-relative position, or null if
  *   there is no child represented by the position.
  *   @param {number} idx  The zero-relative index to the child object.
  *   @type {DvtObj}
  */
DvtContainer.prototype.getChildAt = function(idx)
{
   var  o = null ;
   
   if (this._arList && (this._arList.length > idx) && (idx >= 0)) {
     o = this._arList[idx] ;
   }

   return o ;
} ;



/*---------------------------------------------------------------------*/
/*    getChildAfter()                                                  */
/*---------------------------------------------------------------------*/
/**
  *   Returns the child after the specified object, or null if it is the last
  *   in the list.
  *   @param {DvtObj} obj  The object whose successor is required.
  *   @type {DvtObj}
  */
DvtContainer.prototype.getChildAfter = function(obj)
{
   var  o = null ;
   var  idx = this._findChild(obj) ;
   
   if ((idx >= 0) && (++idx < this._arList.length)) {
     o = this._arList[idx] ;
   }

   return o ;
} ;



/*---------------------------------------------------------------------*/
/*    getChildBefore()                                                 */
/*---------------------------------------------------------------------*/
/**
  *   Returns the child before the specified object, or null if the is the first
  *   in the list.
  *   @param {DvtObj} obj  The object whose predecessor is required.
  *   @type {DvtObj}
  */
DvtContainer.prototype.getChildBefore = function(obj)
{
   var  o = null ;
   var  idx = this._findChild(obj) ;
   
   if (idx > 0) {
     o = this._arList[--idx] ;
   }

   return o ;
} ;



/*---------------------------------------------------------------------*/
/*    _getChildren()                                                   */
/*---------------------------------------------------------------------*/
/**
  * @private
  * For use by DvtIterator ONLY!  Do not use!!
  * @type Array
  */
DvtContainer.prototype._getChildren = function()
{
   return this._arList ;
} ;


/*---------------------------------------------------------------------*/
/*    getChildIndex()                                                  */
/*---------------------------------------------------------------------*/
/**
  *   Returns the container index of the specified object, or -1 if the object
  *   is not a direct child of the container.
  *   @param {DvtObj} obj  The object whose container index is required.
  *   @type {number}
  */
DvtContainer.prototype.getChildIndex = function(obj)
{
   return  this._findChild(obj) ;
} ;


/*---------------------------------------------------------------------*/
/*    getIterator()                                                    */
/*---------------------------------------------------------------------*/
/**
  *  Returns an interator object for walking the container's tree of children.
  *  @param {Boolean} bDeep  If omitted or set to false, the iterator confines
  *  its search to direct children of the container.  If specified as true,
  *  a deep walk of the container tree is made.
  *  @type DvtIterator
  *
  */
DvtContainer.prototype.getIterator = function(bDeep)
{
   return  new DvtIterator(this._arList, bDeep) ;
} ;



/*---------------------------------------------------------------------*/
/*    getNumChildren()                                                 */
/*---------------------------------------------------------------------*/
/**
  *   Returns the number of direct children of this container
  *   @type number
  */
DvtContainer.prototype.getNumChildren = function()
{
   return (this._arList? this._arList.length : 0) ;
} ;


/*---------------------------------------------------------------------*/
/*    removeChild()                                                    */
/*---------------------------------------------------------------------*/
/**
  *   Removes the specified child object from this container.
  *   @param {DvtObj} obj  The object to be removed.
  */
DvtContainer.prototype.removeChild = function(obj)
{
   var idx = this._findChild(obj) ;    // is child in container

   if (idx !== -1) {
     this._removeObj(obj, idx) ;
   }
} ;


/*---------------------------------------------------------------------*/
/*    removeChildAt()                                                  */
/*---------------------------------------------------------------------*/
/**
  *   Removes the specified child object at the index specfied from this container.
  *   @param {number} idx  The index of the object to be removed.
  *   @returns {DvtObj}  The object removed.
  */
DvtContainer.prototype.removeChildAt = function(idx)
{
   var obj = this.getChildAt(idx) ;

   if (obj) {
     this._removeObj(obj, idx) ;
   }

   return obj ;
} ;


/*---------------------------------------------------------------------*/
/*    removeChildren()                                                 */
/*---------------------------------------------------------------------*/
/**
  *   Removes all child object's from this container.
  */
DvtContainer.prototype.removeChildren = function()
{
   if (this._arList) {
      var ar  = this._arList ;

      while(this.getNumChildren() > 0) {
        this._removeObj(ar[0], 0) ;
      }
   }
} ;



/*---------------------------------------------------------------------*/
/*    _removeObj()                                                     */
/*---------------------------------------------------------------------*/
/**
  *   Removes the specified object from this container.
  *   @param {DvtObj}  obj   The object to be removed.
  *   @param {number}  idx   The index position (zero relative) of the child
  *                          in the container's list of chhildren.
  *    @private
  */ 
DvtContainer.prototype._removeObj = function(obj, idx)
{
   //  obj must be in the container.

   this._impl.removeChild(obj.getImpl()) ;  // perform platform remove
   obj.setParent(null) ;

   this._arList.splice(idx,1) ;             // remove from list
   if (this._arList.length === 0) {
     this._arList = null ;
   }
} ;


/*---------------------------------------------------------------------*/
/*    swap()                                                           */
/*---------------------------------------------------------------------*/
/**
  *    Exchanges the positions of two objects in the container.
  *    DONT USE YET - IN PROGRESS JRM  TDO
  */ 
DvtContainer.prototype.swap = function(obj1, obj2)
{
   //  Both obj's must be in this same container.

   var idx1 = this.getChildIndex(obj1) ;
   var idx2 = this.getChildIndex(obj2) ;

   this._arList[idx1] = obj2 ;        // swap entries in list.
   this._arList[idx2] = obj1 ;

   this._impl.swap(obj1.getImpl(), obj2.getImpl()) ;   // perform platform swap.

} ;

/*---------------------------------------------------------------------*/
/*    destroy()                                                        */
/*---------------------------------------------------------------------*/
/**
  *    Destroy the container.  It should no longer be used or displayed
  */ 
DvtContainer.prototype.destroy = function()
{
    DvtContainer.superclass.destroy.call(this) ;
    var childCount = this.getNumChildren();
    for (var i=0;i<childCount;i++) {
        var child = this.getChildAt(i);      
        child.destroy();
    }
}

/**
 * Adds a hyperlink to this object, remove the old hyperlink if exists.
 * @param {DvtObj} linkObj contains destination (url) and targetFrame properties
 */
DvtContainer.prototype.setHyperlink = function(linkObj) 
{
  //Bug 15865017 - GET RID OF DVTGOLINK AND IMPLS
  this._linkObj = linkObj;
  if (this._impl.setHyperlink)
    this._impl.setHyperlink(linkObj);
}

/**
 * Gets the hyperlink information for this object
 */
DvtContainer.prototype.getHyperlink = function() 
{
  return this._linkObj;
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*  DvtStage()   Contains all objects associated with a specific SVG Root  */
/*-------------------------------------------------------------------------*/
/**
  *  Represents the highest level container under a specific platform DOM element
  *  such as  &lt;svg&gt; or &lt;canvas&gt;.
  *  @extends DvtContainer
  *  @class DvtStage   Represents the highest level container under a specific
  *                    platform DOM element such as  &lt;svg&gt; or &lt;canvas&gt;.
  *  DvtStage should not be used directly, retrieve via the platform context.
  *  <p>
  *  Example for SVG :<br><br>
  *  <code>
  *  context = new DvtSvgContext() ;<br>
  *  stage   = context.getStage();<br>
  *  </code>
  *  @constructor
  *  @export
  */
var  DvtStage = function(context, root, id)
{
  this.Init(context, root, id) ; 
};

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

/**
  *  Object initializer.
  *  @protected
  */
DvtStage.prototype.Init = function(context, root, id)
{
  if (! this.getImpl()) {
    this.setImpl(context.getImplFactory().newStage(root, id)) ;
  }
  DvtStage.superclass.Init.call(this, context) ;
};

/**
 * @override
 * @export
 */
DvtStage.prototype.addChild = function(obj) {
  // Note: This function exists to ensure that applications can add children to the stage.  Do not remove.
  DvtStage.superclass.addChild.call(this, obj);
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * An abstract base class for shapes.  Provides common drawing support
  * for properties such as such as stroke and fill types and colors. DvtShape
  * is intended to be subclassed.
  * @extends DvtContainer
  * @class DvtShape is an abstract base class for displayable shapes and
  * provides common drawing support for properties such as stroke and fill. DvtShape
  * is intended to be subclassed.
  * @constructor
  * @param {DvtContext} context The context object
  */
var DvtShape = function(context)
{
   this.Init(context) ;
};

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

/** 
 * Object initializer.
 * @param {DvtContext} context the rendering context
 * @protected
 */
DvtShape.prototype.Init = function(context)
{
  DvtShape.superclass.Init.call(this, context);
  this._bSelectable = false;
  this.IsSelected = false;
  this.InnerShape = null;
  this.HoverInnerStroke = null;
  this.HoverOuterStroke = null;
  this.SelectedInnerStroke = null;
  this.SelectedOuterStroke = null;
  this.SelectedHoverInnerStroke = null;
  this.SelectedHoverOuterStroke = null;
  this.OriginalStroke = null;
  this._isOriginalStrokeSet = false;
  this.IsShowingHoverEffect = false;
  this.setAriaProperty('hidden', 'true');
}

/**
  *   Returns the fill object (a derivative of {@link DvtFill}).
  *   @type DvtFill
  *   @returns The fill object or null if no fill has been applied.
  */
DvtShape.prototype.getFill = function()
{
    return this._impl.getFill() ;
};

/**
  *  Sets the fill properties on this shape from the supplied {@link DvtFill}
  *  subclass object. The fill object become immutable at this point (refer
  *  to {@link DvtObj#clone}). 
  *  @param {DvtFill}  fill  A fill object (derivative of {@link DvtFill}).
  *  If null is specified, a transparent fill is applied.
  */
DvtShape.prototype.setFill = function(fill)
{
   this._impl.setFill(fill) ;
   if (fill)
       fill.__lock() ;                // fill object now immutable
   this.UpdateSelectionEffect();
};

/**
  *  Sets a solid fill on this shape.
  *  @param {String}  color  The hex or RGB representation of the solid fill
  *  @param {number}  alpha  The fill opacity. Defaults to 1 if none specified.
  *  If null is specified, a transparent fill is applied.
  */
DvtShape.prototype.setSolidFill = function(color, alpha)
{
   this.setFill(new DvtSolidFill(color, alpha)) ;
};

/**
  *  Sets an invisible fill on this shape.
  */
DvtShape.prototype.setInvisibleFill = function()
{
   this.setFill(DvtSolidFill.invisibleFill()) ;
}

/**
  *   Returns the stroke object (a derivative of {@link DvtStroke}).
  *   @type DvtStroke
  *   @returns The stroke object.
  */
DvtShape.prototype.getStroke = function()
{
    return this._impl.getStroke() ;
};

/**
  *  Sets the stroke properties on this shape from the supplied {@link DvtStroke} 
  *  subclass object. The stroke object become immutable at this point (refer to
  *  {@link DvtObj#clone})
  *  @param {DvtStroke}  stroke  A stroke object (derivative of {@link DvtStroke}).
  */
DvtShape.prototype.setStroke = function(stroke)
{
   this._impl.setStroke(stroke) ;
   if (stroke)
   {
     stroke.__lock() ;                // stroke object now immutable
   }

};

/**
  *  Sets a solid stroke on this shape.
  *  @param {String}  color  The hex or RGB representation of the solid fill
  *  @param {number}  alpha  The fill opacity. Defaults to 1 if none specified.
  *  @param {number}  strokeWidth  The stroke width. Defaults to 1 if none specified.
  *  If null is specified, a transparent fill is applied.
  */
DvtShape.prototype.setSolidStroke = function(color, alpha, strokeWidth)
{
   this.setStroke(new DvtSolidStroke(color, alpha, strokeWidth));
};

/**  Changes the shape to an outline shape format.  Used for legends
  *  markers that represent a hidden state for the associated series risers.
  *  @param {String} color Border color for hollow shape in format of #aarrggbb
  */
DvtShape.prototype.setHollow = function(color)
{
    this._impl.setHollow(color)
};

/**  Returns whether a shape is hollow.
  *  @returns {boolean} True if the shape is hollow
  */
DvtShape.prototype.isHollow = function() {
    return this._impl.isHollow();
}

/**
  *    Destroy the shape.  It should no longer be used or displayed
  */ 
DvtShape.prototype.destroy = function()
{
  DvtShape.superclass.destroy.call(this) ;
  this.setFill(null);
  this.setStroke(null);
}

/**
 * Makes a copy of the shape with the same geometries. 
 * The original shape's fill, stroke, and id will not be copied over.
 * @return {DvtShape} A copy of this shape with the same constructor arguments.
 */
DvtShape.prototype.copyShape = function()
{
  // subclasses should implement
  return null;
}

/**
 * Updates the geometries of the DvtShape used for the selection effects.
 * @protected
 */
DvtShape.prototype.UpdateSelectionEffect = function() {
  if (this.isSelected() || this.isHoverEffectShown()) {
    var stroke;
    if (this.InnerShape) {
      stroke = this.InnerShape.getStroke();
      this.removeChild(this.InnerShape);
    }
    this.InnerShape = this.copyShape();
    this.InnerShape.setMouseEnabled(false);
    this.InnerShape.setFill(this.getFill());
    if (stroke)
      this.InnerShape.setStroke(stroke);
    this.InnerShape.setMouseEnabled(false);
    this.InnerShape.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
    this.AddChildAt(this.InnerShape, 0);
  }
}

/**
 * Sets the hover inner and outer strokes for this shape. 
 * If there is only one stroke color for this effect, only innerStroke needs to be set.
 * @param {DvtStroke} innerStroke The inner stroke color for this effect
 * @param {DvtStroke} outerStroke The outer stroke color for this effect
 * @return {DvtShape} To be used for chaining
 */
DvtShape.prototype.setHoverStroke = function (innerStroke, outerStroke) {
  this.HoverInnerStroke = innerStroke;
  this.HoverOuterStroke = outerStroke;
  return this;
}

/**
 * Sets the selected inner and outer strokes for this shape.
 * If there is only one stroke color for this effect, only innerStroke needs to be set.
 * @param {DvtStroke} innerStroke The inner stroke color for this effect
 * @param {DvtStroke} outerStroke The outer stroke color for this effect
 * @return {DvtShape} To be used for chaining
 */
DvtShape.prototype.setSelectedStroke = function (innerStroke, outerStroke) {
  this.SelectedInnerStroke = innerStroke;
  this.SelectedOuterStroke = outerStroke;
  return this;
}

/**
 * Sets the selected hover inner and outer stroke for this shape.
 * If there is only one stroke color for this effect, only innerStroke needs to be set.
 * If none are given, default will be to use the selected outer and hover inner strokes.
 * @param {DvtStroke} innerStroke The inner stroke color for this effect
 * @param {DvtStroke} outerStroke The outer stroke color for this effect
 * @return {DvtShape} To be used for chaining
 */
DvtShape.prototype.setSelectedHoverStroke = function (innerStroke, outerStroke) {
  this.SelectedHoverInnerStroke = innerStroke;
  this.SelectedHoverOuterStroke = outerStroke;
  return this;
}

/**
 * Implemented for DvtSelectable
 */
DvtShape.prototype.setSelectable = function (bSelectable) {
  this._bSelectable = bSelectable;
  if (this._bSelectable)
    this.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
  else
    this.setCursor(null);
}


/**
 * Implemented for DvtSelectable
 */
DvtShape.prototype.isSelectable = function () {
  return this._bSelectable;
}

/**
 * Implemented for DvtSelectable
 */
DvtShape.prototype.isSelected = function () {
  return this.IsSelected;
}

/**
 * Implemented for DvtSelectable
 * @override
 */
DvtShape.prototype.setSelected = function (selected) {
  if (this.IsSelected == selected || this instanceof DvtPolyline)
    return;
  
  if (!this._isOriginalStrokeSet) {
    this._isOriginalStrokeSet = true;
    this.OriginalStroke = this.getStroke();
  }
    
  this.IsSelected = selected;
  
  if (this.isSelected()) {
    // on selection, set the outer border treatment on the DvtShape and the inner border treatment
    // on its copy which is placed in front of it with the same fill
    this.UpdateSelectionEffect();
  
    if (this.isHoverEffectShown()) {
      this.CreateSelectedHoverStrokes();
      this.InnerShape.setStroke(this.SelectedHoverInnerStroke);
      this.setStroke(this.SelectedHoverOuterStroke);
    } else {
      this.InnerShape.setStroke(this.SelectedInnerStroke);
      this.setStroke(this.SelectedOuterStroke);
    }
  }
  else {
    this.removeChild(this.InnerShape);
    this.setStroke(this.OriginalStroke);
  }
}

/**
 * Implemented for DvtSelectable
 * @override
 */
DvtShape.prototype.showHoverEffect = function () {
  this.IsShowingHoverEffect = true;
  
  if (!this._isOriginalStrokeSet) {
    this._isOriginalStrokeSet = true;
    this.OriginalStroke = this.getStroke();
  }

  // Don't want to set the hover strokes if the shape is a DvtPolyline or if the strokes are not defined
  if (! (this instanceof DvtPolyline && ! this.HoverInnerStroke && ! this.HoverOuterStroke)){ 
    this.UpdateSelectionEffect();
  
    if (this.isSelected()) {
      this.CreateSelectedHoverStrokes();
      this.InnerShape.setStroke(this.SelectedHoverInnerStroke);
      this.setStroke(this.SelectedHoverOuterStroke);
    } else {
      this.InnerShape.setStroke(this.HoverInnerStroke);
      this.setStroke(this.HoverOuterStroke);
    }
  }
}

/**
 * Initializes the selected hover strokes
 * @protected
 */
DvtShape.prototype.CreateSelectedHoverStrokes = function () {
  if (!this.SelectedHoverInnerStroke && this.HoverInnerStroke)
    this.SelectedHoverInnerStroke = this.HoverInnerStroke.clone();
  if (!this.SelectedHoverOuterStroke && this.SelectedOuterStroke) {
    this.SelectedHoverOuterStroke = this.SelectedOuterStroke.clone();
    this.SelectedHoverOuterStroke.setWidth(this.SelectedOuterStroke.getWidth() + 
                                          (this.HoverInnerStroke.getWidth()-this.SelectedInnerStroke.getWidth()));
  }
}

/**
 * Implemented for DvtSelectable
 */
DvtShape.prototype.hideHoverEffect = function () {
  this.IsShowingHoverEffect = false;
  if (this.isSelected()) {
    this.InnerShape.setStroke(this.SelectedInnerStroke);
    this.setStroke(this.SelectedOuterStroke);
  } else {
    this.removeChild(this.InnerShape);
    this.setStroke(this.OriginalStroke);
  }
}
/**
 * Determine if the selection hover effect is shown.
 * @type boolean
 */
DvtShape.prototype.isHoverEffectShown = function() {
  return this.IsShowingHoverEffect;
}

/**
 * Override DvtContainer's addChildAt to account for the selection shape
 * @override
 */
DvtShape.prototype.addChildAt = function(obj, idx) {
  DvtShape.superclass.addChildAt.call(this, obj, (this.isSelected() || this.isHoverEffectShown()) ? idx + 1 : idx);
}

/**
 * Override DvtContainer's getChildAt to account for the selection shape
 * @override
 */
DvtShape.prototype.getChildAt = function(obj, idx) {
  return DvtShape.superclass.getChildAt.call(this, obj, (this.isSelected() || this.isHoverEffectShown()) ? idx + 1 : idx);
}

/**
 * Override DvtContainer's getChildIndex to account for the selection shape
 * @override
 */
DvtShape.prototype.getChildIndex = function(obj) {
  var idx = DvtShape.superclass.getChildIndex.call(this, obj);
  return (this.isSelected() || this.isHoverEffectShown()) ? idx + 1 : idx;
}

/**
 * Override DvtContainer's removeChildAt to account for the selection shape
 * @override
 */
DvtShape.prototype.removeChildAt = function(idx) {
  DvtShape.superclass.removeChildAt.call(this, (this.isSelected() || this.isHoverEffectShown()) ? idx + 1 : idx);
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  *  Creates an arc (a portion of an ellipse or circle).
  *  @class DvtArc  Creates an arc (a portion of an ellipse or circle).
  *  <p>
  *  Example:<br><br><code>
  *  //  Create an arc from an ellipse centered at (50,50) with radii 100 and<br>
  *  // 200, and starting at angle 40, for an extent of 75.<br><br>
  *  var  arc = new DvtArc(context, 50, 50, 100, 200, 40, 75);<br><br>
  *  //  Create an arc from a circle centered at (50,50) with radii 100<br>
  *  //  starting at angle 40, for an extent of 75.<br><br>
  *  var  arc = new Dvtarc(context, 50, 50, 100, 40, 75);<br>
  *  </code>
  *  @constructor  
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {number} cx  The center x position.
  *  @param {number} cy  The center y position.
  *  @param {number} rx  The horizontal radius of the ellipse/circle.
  *  @param {number} ry  The vertical radius of the ellipse/circle.
  *  @param {number} sa  The starting angle in degrees (following the normal anti-clockwise is positive convention).
  *  @param {number} ae  The angle extent in degrees (following the normal anti-clockwise is positive convention).
  *  @param {String} clos  An optional closure type for the arc. Closure types are {@link DvtArc#OPEN} (the default),
  *                        {@link DvtArc#CHORD} and {@link DvtArc#PIE}.
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var  DvtArc = function(context, cx, cy, rx, ry, sa, ae, clos, id)
{
   this.Init(context, cx, cy, rx, ry, sa, ae, clos, id) ;
};

DvtObj.createSubclass(DvtArc, DvtShape, "DvtArc") ;

// Closure types
/** 
  * Closure type - arc is not closed.
  * @type String
  */
DvtArc.OPEN  = "OPEN" ;
/**
  * Closure type - arc is closed to create a segment.
  * @type String
  */
DvtArc.CHORD = "CHORD" ;
/** 
  * Closure type - arc is closed to create a sector.
  * @type String
  */
DvtArc.PIE   = "PIE" ;

/**
  *  Returns the start angle of the arc.
  *  @returns {number}  The starting angle (following the normal anti-clockwise
  *  is positive convention).
  */
DvtArc.prototype.getAngleStart = function()
{
   return this._impl.getAngleStart() ;
} ;
/**
  *  Sets the start angle of the arc.
  *  @param {number} sa  The starting angle (following the normal anti-clockwise
  *  is positive convention).
  */
DvtArc.prototype.setAngleStart = function(sa)
{
   this._impl.setAngleStart(sa) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Returns the angle subtended by the arc.
  *  @returns {number}  The angle subtended by the arc (following the normal anti-clockwise
  *  is positive convention).
  */
DvtArc.prototype.getAngleExtent = function()
{
   return this._impl.getAngleExtent() ;
} ;
/**
  *  Sets the angle subtended by the arc.
  *  @param {number} ae  The angle subtended by the arc (following the normal anti-clockwise
  *  is positive convention).
  */
DvtArc.prototype.setAngleExtent = function(ae)
{
   this._impl.setAngleExtent(ae) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Returns the closure type of the arc.
  *  @type String
  *  @returns The closure type,  such as {@link DvtArc#OPEN}.
  */
DvtArc.prototype.getClosure = function()
{
   return this._impl.getClosure() ;
} ;

/**
  *  Sets the closure type of the arc.
  *  @param {String} ct   The closure type, such as {@link DvtArc#OPEN}.
  */
DvtArc.prototype.setClosure = function(ct)
{
   this._impl.setClosure(ct) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Get the x coordinate of the center of the ellipse.
  *  @type number
  */
DvtArc.prototype.getCx = function()
{
   return this._impl.getCx() ;
} ;
/**
  *  Set the x coordinate of the center of the ellipse.
  *  @param {number} cx  The x coordinate of the center of the ellipse.
  */
DvtArc.prototype.setCx = function(cx)
{
   this._impl.setCx(cx) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Get the y coordinate of the center of the ellipse.
  *  @type number
  */
DvtArc.prototype.getCy = function()
{
   return this._impl.getCy() ;
} ;
/**
  *  Set the y coordinate of the center of the ellipse.
  *  @param {number} cy  The y coordinate of the center of the ellipse.
  */
DvtArc.prototype.setCy = function(cy)
{
   this._impl.setCy(cy) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Gets the horizontal radius of the ellipse.
  *  @type number
  */
DvtArc.prototype.getRx = function()
{
   return this._impl.getRx() ;
} ;
/**
  *  Sets the horizontal radius of the ellipse.
  *  @param {number} rx  The horizontal radius of the ellipse.
  */
DvtArc.prototype.setRx = function(rx)
{
   this._impl.setRx(rx) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Gets the vertical radius of the ellipse.
  *  @type number
  */
DvtArc.prototype.getRy = function()
{
   return this._impl.getRy() ;
} ;
/**
  *  Sets the vertical radii of the ellipse.
  *  @param {number} ry  The vertical radius of the ellipse.
  */
DvtArc.prototype.setRy = function(ry)
{
   this._impl.setRy(ry) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Object initializer.
  */
DvtArc.prototype.Init = function(context, cx, cy, rx, ry, sa, ae, clos, id)
{
  if (! this.getImpl()) {
    this.setImpl(context.getImplFactory().newArc(cx, cy, rx, ry, sa, ae, clos, id)) ;
  }
  DvtArc.superclass.Init.call(this, context) ;

};

/**
  *  Defines the position and extent of the arc.
  *  @param {number} sa  The starting angle in degrees (following the normal anti-clockwise is positive convention).
  *  @param {number} ae  The angle extent in degrees (following the normal anti-clockwise is positive convention).
  */
DvtArc.prototype.setArc = function(sa, ae)
{
   this._impl.setArc(sa, ae) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Sets both the horizontal and vertical radii of the ellipse/circle.
  *  @param {number} rx  The horizontal radius of the ellipse.
  *  @param {number} ry  The vertical radius of the ellipse.
  */
DvtArc.prototype.setRadius = function(rx, ry)
{
   this.setRx(rx);
   this.setRy(ry);
} ;

/**
 * @override
 */
DvtArc.prototype.copyShape = function()
{
  return new DvtArc(this.getCtx(), this.getCx(), this.getCy(), this.getRx(), this.getRy(), this.getAngleStart(), this.getAngleExtent(), this.getClosure());
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  *  Creates a circular displayable shape.
  *  @extends DvtShape
  *  @class DvtCircle is a platform independent class representing a displayable
  *  circle.
  *  <p>
  *  <b>Example:</b><br><br> <code>
  *  var circle = new DvtCircle(context, 10, 10, 100, 'myCircle') ;<br>
  *  circle.setStroke(new DvtStroke("red", 5)) ;<br>
  *</code>
  *<br>
  *  @constructor
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {number} cx  The center x position.
  *  @param {number} cy  The center y position.
  *  @param {number} r   The radius of the circle.
  *  @param {String} id  Optional ID for the object (see also {@link DvtDisplayable#setId}) 
  */
var  DvtCircle = function(context, cx, cy, r, id)
{
   this.Init(context, cx, cy, r, id) ;
};

DvtObj.createSubclass(DvtCircle, DvtShape, "DvtCircle") ;

/**
  *    Returns the center x-coordinate.
  *    @type number
  *    @returns The center x-coordinate.
  */
DvtCircle.prototype.getCx = function()
{
   return this._impl.getCx() ;
} ;
/**
  *  Sets the x coordinate of the center.
  *  @param {number} cx  The center x position.
  */

DvtCircle.prototype.setCx = function(cx)
{
   this._impl.setCx(cx) ;
   this.UpdateSelectionEffect();
} ;

/**
  *    Returns the center y-coordinate.
  *    @type number
  *    @returns The center y-coordinate.
  */
DvtCircle.prototype.getCy = function()
{
   return this._impl.getCy() ;
} ;
/**
  *  Sets the y coordinate of the center.
  *  @param {number} cy  The center y position.
  *
  */
DvtCircle.prototype.setCy = function(cy)
{
   this._impl.setCy(cy) ;
   this.UpdateSelectionEffect();
} ;

/**
  *    Returns the radius of the circle.
  *    @type number
  *    @returns The radius of the circle.
  */
DvtCircle.prototype.getRadius = function()
{
   return this._impl.getRadius() ;
} ;

/**
  *  Sets the radius  of the circle.
  *  @param {number} r   The radius of the circle.
  */
DvtCircle.prototype.setRadius = function(r)
{
   this._impl.setRadius(r) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Object initializer.
  */
DvtCircle.prototype.Init = function(context, cx, cy, r, id)
{
  if (! this.getImpl()) {
    this.setImpl(context.getImplFactory().newCircle(cx, cy, r, id)) ;
  }

  DvtCircle.superclass.Init.call(this, context) ;
};

/**
  *  Sets the center position of the circle.
  *  @param {number} cx The center x position
  *  @param {number} cy The center y position
  */
DvtCircle.prototype.setCenter= function(cx,cy)
{
   this.setCx(cx) ;
   this.setCy(cy) ;
} ;

/**
 * @override
 */
DvtCircle.prototype.copyShape = function()
{
  return new DvtCircle(this.getCtx(), this.getCx(), this.getCy(), this.getRadius());
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Creates an image.
  * @extends DvtShape
  * @class DvtImage is a platform independent class representing a displayable
  * image.
  * <p>
  * <b>Example:</b><br><br> <code>
  * var img = new DvtImage(context, "pic.png", 10, 10, 100, 150, 'myImage') ;<br>
  *</code>
  * @constructor  
  * @param {DvtContext} context   The platform dependent context object (such
  *                               as {@link DvtSvgContext}).
 *  @param {String} imgSrc  The image URL.
 *  @param {number} x  The x position of the top left corner of the image.
 *  @param {number} y  The y position of the top left corner of the image.
 *  @param {number} w  The width of the image (optional).
 *  @param {number} h  The height of the image (optional).
 *  @param {String} id Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var DvtImage = function(context, imgSrc, x, y, w, h, id)
{
  this.Init(context, imgSrc, x, y, w, h, id) ;
};

DvtObj.createSubclass(DvtImage, DvtShape, "DvtImage") ;

/**
  *  Gets the height of the image.
  *  @returns {number}  The height of the image.
  */
DvtImage.prototype.getHeight = function()
{
  return this._impl.getHeight() ;
} ;

/**
  *  Sets the height of the image.
  *  @param {number} h  The height of the image.
  */
DvtImage.prototype.setHeight = function(h)
{
  this._impl.setHeight(h) ;
} ;

DvtImage.prototype.getPos = function()
{
  return this._impl.getPos() ;
};

DvtImage.prototype.setPos= function(x,y)
{
  this.setX(x) ;
  this.setY(y) ;
};

/**
  *  Returns the src of the image.
  *  @returns (String) the src of the image.
  */
DvtImage.prototype.getSrc = function()
{
  return this._impl.getSrc();
} ;

/**
  *  Sets the src of the image.
  *  @param {String} src  The src of the image.
  *  @returns nothing
  */
DvtImage.prototype.setSrc = function(src)
{
  this._impl.setSrc(src);
} ;

/**
  *  Returns the <code>x</code> coordinate of the image.
  *  @returns {number} the <code>x</code> coordinate of the image.
  */
DvtImage.prototype.getX = function()
{
   return this._impl.getX() ;
} ;

/**
  *  Sets the <code>x</code> coordinate of the top left position of the image.
  *  @param {number} x  The <code>x</code> coordinate of the top left coordinate of the image.
  */
DvtImage.prototype.setX = function(x)
{
  this._impl.setX(x) ;
} ;

/**
  *  Returns the <code>y</code> coordinate of the image.
  *  @returns {number} the <code>y</code> coordinate of the image.
  */
DvtImage.prototype.getY = function(y)
{
  return this._impl.getY() ;
} ;

/**
  *  Sets the <code>y</code> coordinate of the top left position of the image.
  *  @param {number} x  The <code>y</code> coordinate of the top left coordinate of the image.
  */
DvtImage.prototype.setY = function(y)
{
  this._impl.setY(y) ;
} ;

/**
  *  Returns the width of the image.
  *  @returns {number} the width of the image.
  */
DvtImage.prototype.getWidth = function()
{
  return this._impl.getWidth() ;
} ;

/**
  *  Sets the width of the image.
  *  @param {number} w  The width of the image.
  */
DvtImage.prototype.setWidth = function(w)
{
  this._impl.setWidth(w) ;
} ;

/**
 * Convenience method for setting the width and height of the image.
 * @param {object} dims An object with width and height properties.
 */
DvtImage.prototype.__setDimensions = function(dims) {
  this.setWidth(dims.width);
  this.setHeight(dims.height);
}

/**
  *  Object initializer.
  *  @protected
  */
DvtImage.prototype.Init = function(context, imgSrc, x, y, w, h, id)
{
  if (! this.getImpl()) {
    this.setImpl(context.getImplFactory().newImage(imgSrc, x, y, w, h, id)) ;
  }
  DvtImage.superclass.Init.call(this, context) ;

};


/**
  *  Loads the image and sets the image size if image was loaded
  *  Returns the dimension of the image if it was loaded
  */
DvtImage.prototype.loadImage = function() {
  return this._impl.loadImage() ;
}

/**
 * @override
 */
DvtImage.prototype.copyShape = function()
{
  return new DvtImage(this.getCtx(), this.getSrc(), this.getX(), this.getY(), this.getWidth(), this.getHeight());
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  *  Creates a line drawn from (x1, y1) to (x2, y2).
  *  @extends DvtShape
  *  @class DvtLine Creates a line drawn from (x1, y1) to (x2, y2).
  *   <p>
  *  Example:<br><br><code>
  *  var line = new DvtLine(context, 10, 10, 100, 150);<br>
  *  </code>
  *  @constructor  
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {number} x1  An x end position of the line.
  *  @param {number} y1  The associated y end point (for x1).
  *  @param {number} x2  The other x end point.
  *  @param {number} y2  The associated y end point (for x2).
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *
  */
var  DvtLine = function(context, x1, y1, x2, y2, id)
{
   this.Init(context, x1, y1, x2, y2, id ) ;
};

DvtObj.createSubclass(DvtLine, DvtShape, "DvtLine") ;

/**
  *  Returns the <code>x1</code> coordinate of the line.
  *  @returns {number} the <code>x1</code> coordinate of the line.
  */
DvtLine.prototype.getX1 = function()
{
   return this._impl.getX1() ;
} ;

/**
  *  Sets the <code>x1</code> coordinate of the line.
  *  @param {number} x1  The <code>x</code> coordinate of the line.
  */
DvtLine.prototype.setX1 = function(x1)
{
   this._impl.setX1(x1) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Returns the <code>y1</code> coordinate of the line.
  *  @returns {number} the <code>y1</code> coordinate of the line.
  */
DvtLine.prototype.getY1 = function()
{
   return this._impl.getY1() ;
} ;

/**
  *  Sets the <code>y1</code> coordinate of the line.
  *  @param {number} y1  The <code>y</code> coordinate of the line.
  */
DvtLine.prototype.setY1 = function(y1)
{
   this._impl.setY1(y1) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Returns the <code>x2</code> coordinate of the line.
  *  @returns {number} the <code>x2</code> coordinate of the line.
  */
DvtLine.prototype.getX2 = function()
{
   return this._impl.getX2() ;
} ;

/**
  *  Sets the <code>x2</code> coordinate of the line.
  *  @param {number} x2  The <code>x</code> coordinate of the line.
  */
DvtLine.prototype.setX2 = function(x2)
{
   this._impl.setX2(x2) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Returns the <code>y2</code> coordinate of the line.
  *  @returns {number} the <code>y2</code> coordinate of the line.
  */
DvtLine.prototype.getY2 = function()
{
   return this._impl.getY2() ;
} ;

/**
  *  Sets the <code>y2</code> coordinate of the line.
  *  @param {number} y2  The <code>y</code> coordinate of the line.
  */
DvtLine.prototype.setY2 = function(y2)
{
   this._impl.setY2(y2) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Object initializer.
  *  @protected
  */
DvtLine.prototype.Init = function(context, x1, y1, x2, y2, id)
{
  if (! this.getImpl()) {
    this.setImpl(context.getImplFactory().newLine(x1, y1, x2, y2, id)) ;
  }
  DvtLine.superclass.Init.call(this, context) ;

};

/**
 * @override
 */
DvtLine.prototype.copyShape = function()
{
  return new DvtLine(this.getCtx(), this.getX1(), this.getY1(), this.getX2(), this.getY2());
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/** 
  *  A marker object for lines, scatter and bubble charts and legend areas.
  *  @class DvtMarker A marker object for lines, scatter and bubble charts and legend areas.
  *  @extends DvtShape
  *  @constructor
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {Object} type An string representing the type of the marker (see {@link DvtMarker}), 
  *                       a string URI for the shape path for a custom svg marker, 
  *                       or an array of image URIs for each of the 4 marker states (active, hover, selected, hoverSelected).
  *  @param {number} x  The x position of the top left corner of the marker.
  *  @param {number} y  The y position of the top left corner of the marker.
  *  @param {number} w  The width of the marker.
  *  @param {number} h  The height of the marker.
  *  @param {number} sx  Optional The horizontal scale factor of the marker.
  *  @param {number} sy  Optional The vertical scale factor of the marker.
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *  @param {boolean} maintainAspect Optional Indicates whether aspect ratio should be maintained (false is not specified)
  *
  */
var  DvtMarker = function(context, type, skin, x, y, w, h, id, sx, sy, maintainAspect)
{
  this.Init(context, type, skin, x, y, w, h, id, sx, sy, maintainAspect) ;
};

DvtObj.createSubclass(DvtMarker, DvtShape, "DvtMarker") ;

/**
 * No marker shape defined.
 */
DvtMarker.NONE = "none";
/**  
 * A circular marker.
 */
DvtMarker.CIRCLE = "circle";
/**  
 * A square marker.
 */
DvtMarker.SQUARE = "square";
/**  
 * A diamond shaped marker.
 */
DvtMarker.DIAMOND = "diamond";
/**  
  * A triangular shaped marker with a vertex at the top.
  */
DvtMarker.TRIANGLE_UP = "triangleUp";
/**  
 * A triangular shaped marker with a vertex at the bottom.
 */
DvtMarker.TRIANGLE_DOWN = "triangleDown";
/**  
 * A plus-shaped marker.
 */
DvtMarker.PLUS = "plus";
/**  
 * A human figure shaped marker.
 */
DvtMarker.HUMAN = "human";
/**  
 * A rectangular marker with rounded corners.
 */
DvtMarker.ROUNDED_RECTANGLE = "roundedRect";
/**  
 * A custom svg shaped marker.
 */
DvtMarker.CUSTOM = "custom";
/**  
 * An image marker.
 */
DvtMarker.IMAGE = "image";

// Array indicies if an array of image URIs are passed in type parameter
DvtMarker.IMAGE_SOURCE = 0;
DvtMarker.IMAGE_SOURCE_SELECTED = 1;
DvtMarker.IMAGE_SOURCE_HOVER = 2;
DvtMarker.IMAGE_SOURCE_HOVER_SELECTED = 3;

DvtMarker.DEFAULT_UNDERLAY_TYPE = DvtMarker.SQUARE;
DvtMarker.DEFAULT_UNDERLAY_COLOR = "#8f808080";

/**
 * @protected
 */
DvtMarker.SELECTION_STROKE_COLOR = "#000000";
/**
 * @protected
 */
DvtMarker.SELECTION_STROKE_ALPHA = 1;

/**
  *  Returns the greater of the width and height measurement.
  *  @returns {number} the size of the marker.
  */
DvtMarker.prototype.getSize = function()
{
   return this._size;
} ;

/**
  *  Returns the type of the marker (such as {@link DvtMarker#CIRCLE}).
  *  @returns {String} the type of the marker (such as {@link DvtMarker#CIRCLE}).
  */
DvtMarker.prototype.getType = function()
{
   return  this._impl.getType() ;
} ;


/**
  *  Returns the type of the marker that was passed into the constructor.
  *  The type can be a string indicating a built-in marker type, an array of image paths, 
  *  or a single path for custom svg marker.
  *  @returns {Object} the type of the marker 
  */
DvtMarker.prototype.getInitType = function()
{
   return this._initType;
} ;


/**
  *  Object initializer.
  *  @protected
  */
DvtMarker.prototype.Init = function(context, type, skin, x, y, w, h, id, sx, sy, maintainAspect)
{
  //BUG FIX #13376761: save initial parameters in case we want to create
  //a copy of this marker
  this._skin = skin;
  this._xx = x;
  this._yy = y;
  this._ww = w;
  this._hh = h;
  this._sx = sx ? sx : 1;
  this._sy = sy ? sy : 1;
  this._maintainAspect = maintainAspect;
  this._initType = type;
  this._bAnimatedOnDisplay = false;
  
  var etype;
  var markerDef;

  if (type instanceof Array) {
    this._setMarkerImageStates(type);
    type = DvtMarker.IMAGE;
  } else {
    if (!DvtMarker.isBuiltInShape(type)) {
      markerDef = DvtMarkerUtils.getCustomMarkerInfo(context, type);
      if (markerDef) {
        // Custom marker is always DvtPath or DvtContainer containing a collection of DvtPaths
        etype = (markerDef instanceof DvtPath ? 'path' : 'g');
        if (! w || ! h) {
          var dim = DvtDisplayableUtils._getDimForced(context, markerDef);
          w = w ? w : dim.w;
          h = h ? h : dim.h;
          this._ww = w;
          this._hh = h;
        }  
        type = DvtMarker.CUSTOM;
      } else {
        // no markerDef found, default to rect
        type = DvtMarker.SQUARE;
      }
    } else {
      type = DvtMarker.convertShapeString(type);
    }
  }
  // Create the impl shape
  if (!this.getImpl())
    this.setImpl(context.getImplFactory().newMarker(type, id, etype)) ;
  
  // Superclass should only be set after the impl is created
  DvtMarker.superclass.Init.call(this, context);
  
  if (type === DvtMarker.HUMAN) {
    markerDef = DvtMarkerUtils.getBuiltinMarkerInfo(context, "human", this.getSkin());
  }

  // Update the width and height with the scale factors
  var ww = this.getScaledWidth();
  var hh = this.getScaledHeight();
  this._size = Math.max(ww, hh);
  
  // Position and size the marker
  this.getImpl().setBounds(x, y, ww, hh, markerDef);

  // Store other params
  this._bSelecting       = false;
  this._selectionEffects = null;
  this._dataColor        = "#000000";
  this._underlayColor    = "#000000";
  
  //properties related to selection
  this._savedStroke      = null;
  this._savedFill        = null;
  this._bSavedStroke     = false;
  this._bSavedFill       = false;
  this._selectionType    = DvtSelectionEffectUtils.DEFAULT_SEL_TYPE;
  this._selStrokeWidth   = null;
  this._selStrokeColor   = null;
  this._selStrokeAlpha   = null;
};

/**
 *  Returns the skin of the marker.
 *  @returns {String} the skin name
 */
DvtMarker.prototype.getSkin = function()
{
   return this._skin ;
}

/**
 *  Returns the x-coord of the marker.
 *  @returns {number} the x-coord of the marker
 */
DvtMarker.prototype.getX = function()
{
   return  this._xx ;
} ;

/**
 *  Returns the y-coord of the marker.
 *  @returns {number} the y-coord of the marker
 */
DvtMarker.prototype.getY = function()
{
   return  this._yy ;
} ;

/**
 *  Returns the width of the marker, before any scale is applied.
 *  @returns {number} the width of the marker
 */
DvtMarker.prototype.getWidth = function()
{
   return  this._ww ;
} ;

/**
 *  Returns the height of the marker, before any scale is applied.
 *  @returns {number} the height of the marker
 */
DvtMarker.prototype.getHeight = function()
{
   return  this._hh ;
} ;

/**
 *  Returns the horizontal scale of the marker.
 *  @returns {number} the horizontal scale of the marker
 */
DvtMarker.prototype.getSx = function()
{
   return  this._sx ;
} ;

/**
 *  Returns the vertical scale of the marker.
 *  @returns {number} the vertical scale of the marker
 */
DvtMarker.prototype.getSy = function()
{
   return  this._sy ;
} ;

/**
 *  Returns whether aspect ratio should be maintained.
 *  @returns {boolean} whether aspect ratio should be maintained
 */
DvtMarker.prototype.getMaintainAspect = function() {
  return this._maintainAspect == true;
};

/**
 *  Returns the width of the marker, after any scale is applied.
 *  @returns {number} the scaled width of the marker
 */
DvtMarker.prototype.getScaledWidth = function()
{
    return this._sx ? this._ww*this._sx : this._ww;
} ;

/**
 *  Returns the height of the marker, after any scale is applied.
 *  @returns {number} the scaled height of the marker
 */
DvtMarker.prototype.getScaledHeight = function()
{
    return this._sy ? this._hh*this._sy : this._hh;
} ;

/**
 * Converts the specified shape string to its constant value.
 * @param {string} shape The shape string.
 * @return {number} The corresponding constant value.
 */
DvtMarker.convertShapeString = function(shape) {
  if(shape == DvtMarker.CIRCLE || shape == "c")
    return DvtMarker.CIRCLE;
  else if(shape == DvtMarker.SQUARE || shape == "s")
    return DvtMarker.SQUARE;
  else if(shape == DvtMarker.DIAMOND || shape == "d")
    return DvtMarker.DIAMOND;
  else if(shape == DvtMarker.TRIANGLE_UP || shape == "tu" || shape == "t")
    return DvtMarker.TRIANGLE_UP;
  else if(shape == DvtMarker.TRIANGLE_DOWN || shape == "td")
    return DvtMarker.TRIANGLE_DOWN;
  else if(shape == DvtMarker.PLUS || shape == "p")
    return DvtMarker.PLUS;
  else if(shape == DvtMarker.HUMAN || shape == "h")
    return DvtMarker.HUMAN;
  else if(shape == DvtMarker.ROUNDED_RECTANGLE || shape == "rr")
    return DvtMarker.ROUNDED_RECTANGLE;
  else
    return DvtMarker.NONE;
}

/**
 * @protected
 * Set the type of selection feedback to show.
 */
DvtMarker.prototype.setSelectionType = function(type)
{
  this._selectionType = type;
};

/**
 * @protected
 * Get the type of selection feedback to show.
 */
DvtMarker.prototype.getSelectionType = function(type)
{
  return this._selectionType;
};

/**
 * @protected
 * Apply any selection effects to this object.
 */
DvtMarker.prototype.ApplySelectionState = function()
{
  switch (this._selectionType) {
    case DvtSelectionEffectUtils.SEL_TYPE_STROKE_NO_FILTERS:
      this.ApplySelectingSelectedStroke(true, true, true);
      break;
    case DvtSelectionEffectUtils.SEL_TYPE_STROKE_FILTERS:
    default:
      this.ApplySelectionFilters();
      break;
  }
};

/**
 * @protected
 * Apply any selection filters to this object.
 */
DvtMarker.prototype.ApplySelectionFilters = function()
{
  if (!this._selectionEffects)
  {
    this.UpdateSelectionEffects();
  }
  if (this.isSelected())
  {
    if (this._selectionEffects)
    {
      for (var i = 0; i < this._selectionEffects.length; i++)
      {
        var drawEffect = this._selectionEffects[i];
        if (!(drawEffect instanceof DvtShadow))
        {
          DvtSelectionEffectUtils.applyDrawEffect(this, drawEffect);
        }
      }
    }
  }
  else
  {
    this.ClearSelectionEffects();
  }
};


/**
 * @protected
 * Clear any selection effects.
 */
DvtMarker.prototype.ClearSelectionEffects = function()
{
  if (this._selectionEffects)
  {
    for (var i = 0; i < this._selectionEffects.length; i++)
    {
      this.removeDrawEffect(this._selectionEffects[i]);
    }
  }
};

/**
 * @protected
 * Update any selection effects.
 */
DvtMarker.prototype.UpdateSelectionEffects = function()
{
  var selColor = "#000000";
  if (this._impl.getUnderlay()) {
    selColor = this._underlayColor;
  } else {
    selColor = this._dataColor; 
  }
  this._selectionEffects = DvtSelectionEffectUtils.createMarkerSelectionEffects(this.getCtx(), selColor, this._innerColor, this._outerColor);
};


/**
 * @protected
 * Apply any mouse hover selection effects to this object.
 */
DvtMarker.prototype.ApplySelectingState = function()
{
  switch (this._selectionType) {
    case DvtSelectionEffectUtils.SEL_TYPE_STROKE_NO_FILTERS:
      this.ApplySelectingSelectedStroke(true, true, true);
      break;
    case DvtSelectionEffectUtils.SEL_TYPE_STROKE_FILTERS:
    default:
      this.ApplySelectingStroke();
      break;
  }
};

/**
 * @protected
 */
DvtMarker.prototype.ApplySelectingStroke = function(bAnimated)
{
  this.ApplySelectingSelectedStroke(bAnimated, true);
};

/**
 * @protected
 */
DvtMarker.prototype.ApplySelectingSelectedStroke = function(bAnimated, bApplySelecting, bApplySelected)
{
  var stroke;
  var bSelecting = bApplySelecting && this._bSelecting;
  var bSelected = bApplySelected && this.isSelected();
  
  var bStrokeFill = (this._selectionType === DvtSelectionEffectUtils.SEL_TYPE_STROKE_NO_FILTERS);
  if (bStrokeFill)
  {
    var fill;
    if (bSelected)
    {
      if (!this._bSavedFill)
      {
        fill = new DvtSolidFill("#ffffff", 1);
        this._savedFill = this.getFill();
        this._bSavedFill = true;
        this.setFill(fill);
      }
    }
    else
    {
      if (this._bSavedFill)
      {
        fill = this._savedFill;
        this._savedFill = null;
        this._bSavedFill = false;
        
        this.setFill(fill);
      }
    }
  }
  
  if (bSelecting || bSelected)
  {
    if (!this._bSavedStroke)
    {
      stroke = DvtSelectionEffectUtils.createSelectingStroke(this._dataColor);
      
      //for selection, save the color and alpha before applying
      //the new values
      if (stroke instanceof DvtSolidStroke)
      {
        this._selStrokeColor = stroke.getColor();
        this._selStrokeAlpha = stroke.getAlpha();
        if (bSelected)
        {
          if (bStrokeFill)
          {
            stroke.setColor(this._dataColor);
          }
          else
          {
            stroke.setColor(DvtMarker.SELECTION_STROKE_COLOR);
            stroke.setAlpha(DvtMarker.SELECTION_STROKE_ALPHA);
          }
        }
      }
      
      
      var strokeWidth = stroke.getWidth();
      //save the stroke width so that we can reset it if needed
      this._selStrokeWidth = strokeWidth;
      stroke.setWidth(strokeWidth);
      
      this._savedStroke = this.getStroke();
      this._bSavedStroke = true;
      
      this.setStroke(stroke);
    }
    
    //for selection, set the selected color and alpha values on the stroke;
    //for selecting, restore the original color and alpha values on the stroke
    if (bApplySelecting && bApplySelected)
    {
      if (this.isSelected() && this.getStroke() instanceof DvtSolidStroke)
      {
        stroke = this.getStroke().clone();
        if (bStrokeFill)
        {
        if (stroke.getColor() !== this._dataColor)
        {
          stroke.setColor(this._dataColor);
          this.setStroke(stroke);
        }
      }
        else
        {
          if (stroke.getColor() !== DvtMarker.SELECTION_STROKE_COLOR)
          {
            stroke.setColor(DvtMarker.SELECTION_STROKE_COLOR);
            stroke.setAlpha(DvtMarker.SELECTION_STROKE_ALPHA);
            this.setStroke(stroke);
          }
        }
      }
      else if (this._bSelecting && this.getStroke() instanceof DvtSolidStroke)
      {
        stroke = this.getStroke().clone();
        if (stroke.getColor() !== this._selStrokeColor)
        {
          stroke.setColor(this._selStrokeColor);
          stroke.setAlpha(this._selStrokeAlpha);
          this.setStroke(stroke);
        }
      }
    }
    
    if (this._impl.getUnderlay()) {
      var underlayStroke = DvtSelectionEffectUtils.createSelectingStroke(this._underlayColor);
      var underlayStrokeWidth = underlayStroke.getWidth();
      if (this.getType() === DvtMarker.HUMAN)
        underlayStrokeWidth *= this.getImpl().getDefaultScale();
      underlayStroke.setWidth(underlayStrokeWidth);
      this._impl.getUnderlay().setStroke(underlayStroke);
    }
    
    //if animating and applying selecting effects, start or stop
    //the animation
    if (bAnimated && bApplySelecting)
    {
      if (this._bSelecting)
      {
        if (!this._selectingAnimator)
        {
          //start the animation if we're currently selecting and 
          //we haven't already started an animation
          this.StartAnimatingStroke();
        }
      }
      else
      {
        //stop the animation if we're not currently selecting
        this.StopSelectingAnimator();
        //need to reset the stroke width because we've stopped
        //the animation in the middle
        this.SetStrokeWidth(this._selStrokeWidth);
      }
    }
  }
  else
  {
    //need to stop animation before restoring old stroke
    if (bAnimated)
    {
      this.StopSelectingAnimator();
    }
    
    stroke = this._savedStroke;
    this._savedStroke = null;
    this._bSavedStroke = false;
    
    this.setStroke(stroke);
    // reset underlay stroke to null
    // if underlay ever comes with original stroke, this need to be modified
    if (this._impl.getUnderlay()) 
    this._impl.getUnderlay().setStroke(null);
  }
};

/**
 * @protected
 */
/*DvtMarker.prototype.ApplySelectingAnimatedScale = function()
{
  if (this._bSelecting)
  {
    this._savedMatrix = this.getMatrix().clone();
    
    this.StartAnimatingScale();
  }
  else
  {
    this.StopSelectingAnimator();
    
    this.setMatrix(this._savedMatrix);
  }
};*/

/**
 * @protected
 */
DvtMarker.prototype.StartAnimatingStroke = function()
{
  if (!this._bSelecting || !this.getStroke())
  {
    return;
  }
  
  var playable1 = new DvtCustomAnimation(this.getCtx(), null, 1);
  var anim1 = playable1.getAnimator();
  anim1.addProp(DvtAnimator.TYPE_NUMBER, this, this.GetStrokeWidth, this.SetStrokeWidth, this.getStroke().getWidth() * 3);
  
  var playable2 = new DvtCustomAnimation(this.getCtx(), null, 1);
  var anim2 = playable2.getAnimator();
  anim2.addProp(DvtAnimator.TYPE_NUMBER, this, this.GetStrokeWidth, this.SetStrokeWidth, this.getStroke().getWidth());
  
  var seqPlayable = new DvtSequentialPlayable(this.getCtx(), playable1, playable2);
  seqPlayable.setOnEnd(this.StartAnimatingStroke, this);
  
  this._selectingAnimator = seqPlayable;
  seqPlayable.play();
};

/**
 * @protected
 */
/*DvtMarker.prototype.StartAnimatingScale = function()
{
  if (!this._bSelecting)
  {
    return;
  }
  
  var newMat = this.getMatrix().clone();
  var dims = this.getDimensions();
  //dims.w *= this.getScaleX();
  //dims.h *= this.getScaleY();
  newMat.translate(-dims.w / 2, -dims.h / 2);
  newMat.scale(2);
  newMat.translate(dims.w / 2, dims.h / 2);
  
  //this.setMatrix(newMat);
  //return;
  
  var playable1 = new DvtCustomAnimation(this.getCtx(), null, 1);
  var anim1 = playable1.getAnimator();
  anim1.addProp(DvtAnimator.TYPE_MATRIX, this, this.getMatrix, this.setMatrix, newMat);
  
  var playable2 = new DvtCustomAnimation(this.getCtx(), null, 1);
  var anim2 = playable2.getAnimator();
  anim2.addProp(DvtAnimator.TYPE_MATRIX, this, this.getMatrix, this.setMatrix, this.getMatrix().clone());
  
  var seqPlayable = new DvtSequentialPlayable(this.getCtx(), playable1, playable2);
  seqPlayable.setOnEnd(this.StartAnimatingScale, this);
  
  this._selectingAnimator = seqPlayable;
  seqPlayable.play();
};*/

/**
 * @protected
 */
DvtMarker.prototype.StopSelectingAnimator = function()
{
  if (this._selectingAnimator)
  {
    this._selectingAnimator.stop();
    this._selectingAnimator = null;
  }
};

/**
 * @protected
 */
DvtMarker.prototype.SetStrokeWidth = function(sw)
{
  var stroke = this.getStroke();
  if (stroke)
  {
    stroke = stroke.clone();
    stroke.setWidth(sw);
    this.setStroke(stroke);
  }
};

/**
 * @protected
 */
DvtMarker.prototype.GetStrokeWidth = function()
{
  var stroke = this.getStroke();
  if (stroke)
  {
    return stroke.getWidth();
  }
  return 0;
};

/**
 * @override
 */
DvtMarker.prototype.showHoverEffect = function()
{
  if (this.getType() === DvtMarker.IMAGE) {
    this.IsShowingHoverEffect = true;
    if (this.isSelected())
      this.getImpl().setSource(this.GetMarkerImage(DvtMarker.IMAGE_SOURCE_HOVER_SELECTED));
    else
      this.getImpl().setSource(this.GetMarkerImage(DvtMarker.IMAGE_SOURCE_HOVER));
  }
  else if (this.getSkin() == DvtCSSStyle.SKIN_ALTA)
    DvtMarker.superclass.showHoverEffect.call(this);
  else
    this.SetSelectionMouseOver(true);
};


/**
 * @override
 */
DvtMarker.prototype.hideHoverEffect = function()
{
  if (this.getType() === DvtMarker.IMAGE) {
    this.IsShowingHoverEffect = false;
    if (this.isSelected())
      this.getImpl().setSource(this.GetMarkerImage(DvtMarker.IMAGE_SOURCE_SELECTED));
    else
      this.getImpl().setSource(this.GetMarkerImage(DvtMarker.IMAGE_SOURCE));
  }
  else if (this.getSkin() == DvtCSSStyle.SKIN_ALTA)
    DvtMarker.superclass.hideHoverEffect.call(this);
  else
    this.SetSelectionMouseOver(false);
};


/**
 * @override
 */
DvtMarker.prototype.isHoverEffectShown = function()
{
  if (this.getSkin() == DvtCSSStyle.SKIN_ALTA)
    return DvtMarker.superclass.isHoverEffectShown.call(this);
  else
    return this._bSelecting;
};


/**
 * @protected
 * Set whether the selection mouse hover effect is shown.
 * 
 * @param {boolean}  bOver  true if the mouse is over the object,
 *        false otherwise
 */
DvtMarker.prototype.SetSelectionMouseOver = function(bOver)
{
  if (this._bSelecting != bOver)
  {
    this._bSelecting = bOver;
    
    this.ApplySelectingState();
  }
};

/**
 * @override
 */
DvtMarker.prototype.setSelected = function(selected)
{  
  if (this.IsSelected == selected)
    return;
  
  if (this.getType() === DvtMarker.IMAGE) { 
    this.IsSelected = selected;
    if (selected) {
      if (this.isHoverEffectShown())
        this.getImpl().setSource(this.GetMarkerImage(DvtMarker.IMAGE_SOURCE_HOVER_SELECTED));
      else
        this.getImpl().setSource(this.GetMarkerImage(DvtMarker.IMAGE_SOURCE_SELECTED));
    } else {
      this.getImpl().setSource(this.GetMarkerImage(DvtMarker.IMAGE_SOURCE));
    }
  }
  else if (this.getSkin() == DvtCSSStyle.SKIN_ALTA) {
    DvtMarker.superclass.setSelected.call(this, selected);
  } else {
    this.IsSelected = selected;
    this.ApplySelectionState();
  }
};

/**
 * Specifies the color of the data item and its selection feedback, if different from the default.
 * @param {string} dataColor The CSS color string of the primary color of the data item.
 * @param {string} innerColor The CSS color string of the inner color of the selection effect.
 * @param {string} outerColor The CSS color string of the outer color of the selection effect.
 */
DvtMarker.prototype.setDataColor = function(dataColor, innerColor, outerColor)
{ 
  this._dataColor = dataColor;
  if (this.getSkin() == DvtCSSStyle.SKIN_ALTA) {
    var hoverColor = DvtColorUtils.adjustHSL(dataColor, 0, 0, 0.15);
    var sis = new DvtSolidStroke('#FFFFFF', 1, 1.5);
    this.setHoverStroke(new DvtSolidStroke(hoverColor, 1, 2));
    this.setSelectedStroke(sis, new DvtSolidStroke('#5A5A5A', 1, 4.5));
    this.setSelectedHoverStroke(sis, new DvtSolidStroke(hoverColor, 1, 4.5));
    this.HoverInnerStroke.setFixedWidth(true);
    this.SelectedInnerStroke.setFixedWidth(true);
    this.SelectedOuterStroke.setFixedWidth(true);
    this.SelectedHoverInnerStroke.setFixedWidth(true);
    this.SelectedHoverOuterStroke.setFixedWidth(true);
  } else {
    this._innerColor = innerColor;
    this._outerColor = outerColor;
  }
}

/**
 * Get the data color used as a base for selection colors.
 * 
 * @type string
 */
DvtMarker.prototype.getDataColor = function()
{
  return this._dataColor;
};

/**  Sets the underlay for markers
 *  @param {string} ut underlay type
 *  @param {string} uc fill color of the underlay
 *  @param {number} ua fill alpha of the underlay
 */
DvtMarker.prototype.setUnderlay = function(type, color, alpha)
{
   this._impl.setUnderlay(type, color, alpha);
   this._underlayColor = color;
};

/** Gets the underlay for this marker
 *  Used in DvtGraphCore to get the color or shape of the underlay for hiding a single one.
 */
DvtMarker.prototype.getUnderlay = function()
{
   return this._impl.getUnderlay();
};

/**
  *  Enables/disables the visibility of a marker underlay.
  *  @param {Boolean}  bVis  True if the object is to be visible, else false if
  *  it is to be hidden.
  */
DvtMarker.prototype.setUnderlayVisible = function (bVis) {
  var underlay = this._impl.getUnderlay();
  if (underlay)
    underlay.setVisible(bVis);
}

/**  Changes the shape to an outline shape format.  Used for legend
  *  markers that represent a hidden state for the associated series risers.
  *  @param {String} color Border color for hollow shape in format of #aarrggbb
  *  @override
  */
DvtMarker.prototype.setHollow = function(color)
{
    //scale the stroke width inversely proportional to the marker scale
    //so that the stroke width appears to be the same for all markers
    var scaleX = this.getScaleX();
    var scaleY = this.getScaleY();
    var scale = Math.min(scaleX, scaleY);
    var strokeWidth = this.GetStrokeWidth();
    strokeWidth = (strokeWidth ? strokeWidth : 1) / scale;
    //save the stroke width so that we can reset it if needed
    this._impl.setHollow(color, strokeWidth)
};

/**  Adds reference for legend text to marker
  *  @param {DvtText} text Legend text
  */
DvtMarker.prototype.setText = function(text)
{
    this._markerText = text;
}

/**  Adds reference for legend text to marker
  *  @param {number} alpha Opacity of object
  *  @override
  */
DvtMarker.prototype.setAlpha = function(alpha) {
    DvtMarker.superclass.setAlpha.call(this, alpha);
    if (this._markerText)
        this._markerText.setAlpha(alpha);
    this.UpdateSelectionEffect();
}

/**
  *  Enables/disables the visibility of marker and text if reference exists.
  *  @param {Boolean}  bVis  True if the object is to be visible, else false if
  *  it is to be hidden.
  *  @override
  */
DvtMarker.prototype.setVisible = function(bVis)
{
  DvtMarker.superclass.setVisible.call(this, bVis);
  if (this._markerText) 
      this._markerText.setVisible(bVis);
}

DvtMarker.prototype._setMarkerImageStates = function(imageURIs)
{
  // at a minimum an image URI will be provided for the active marker state
  var sourceImage = imageURIs[0];
  this._imageStates = [sourceImage];
  this._imageStates.push(imageURIs[1] ? imageURIs[1] : sourceImage); // sourceSelected
  this._imageStates.push(imageURIs[2] ? imageURIs[2] : sourceImage); // sourceHover
  this._imageStates.push(imageURIs[3] ? imageURIs[3] :this._imageStates[1]); // sourceHoverSelected
}

DvtMarker.prototype.GetMarkerImage = function(state)
{
  if (this._imageStates)
    return this._imageStates[state];
  return null;
}

/**
 * Determines if the specified marker shape is a built-in shape.
 * @param {string} shape The shape. For custom markers this would be the shape path.
 * @return {boolean} True if shape is built-in.
 */
DvtMarker.isBuiltInShape = function(shape) {
  var shp = DvtMarker.convertShapeString(shape);
  if(shp == DvtMarker.CIRCLE || shp == DvtMarker.SQUARE || shp == DvtMarker.DIAMOND || 
     shp == DvtMarker.TRIANGLE_UP || shp == DvtMarker.TRIANGLE_DOWN || shp == DvtMarker.PLUS ||
     shp == DvtMarker.HUMAN || shp == DvtMarker.ROUNDED_RECTANGLE) {
    return true;
  } else {
    return false;
  }
}

/**
 * @override
 */
DvtMarker.prototype.copyShape = function()
{
  return new DvtMarker(this.getCtx(), this.getInitType(), this.getSkin(), this.getX(), this.getY(), this.getWidth(), this.getHeight(), 
                        null, this.getSx(), this.getSy(), this.getMaintainAspect());
}

/**
 * @override
 */
DvtMarker.prototype.UpdateSelectionEffect = function() {
  if (this.getSkin() == DvtCSSStyle.SKIN_ALTA)
    DvtMarker.superclass.UpdateSelectionEffect.call(this);
}

/**
 * @override
 */
DvtMarker.prototype.getDimensions = function(targetCoordinateSpace) {
  // Transforms on markers are not transferred to the outer group element so we must return dimensions of child 
  // element which has the matrix
  if (this.InnerShape && this.InnerShape.getParent())
    return this.InnerShape.getDimensions(targetCoordinateSpace);
  else
    return DvtMarker.superclass.getDimensions.call(this, targetCoordinateSpace);
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  *  Creates an elliptical shape.
  *  @extends DvtCircle
  *  @class DvtOval  Creates an elliptical shape.
  *  <p>
  *  Example:<br><br><code>
  *  var  oval = new DvtOval(context, 50, 50, 100, 200);<br>
  *  </code>
  *  @constructor  
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {number} cx  The center x position.
  *  @param {number} cy  The center y position.
  *  @param {number} rx  The horizontal radius of the ellipse.
  *  @param {number} ry  The vertical radius of the ellipse.
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var  DvtOval = function(context, cx, cy, rx, ry, id)
{
   this.Init(context, cx, cy, rx, ry, id) ;
};

DvtObj.createSubclass(DvtOval, DvtCircle, "DvtOval") ;

/**
  *  Gets the horizontal radius of the ellipse.
  *  @type number
  */
DvtOval.prototype.getRx = function()
{
   return this._impl.getRx() ;
} ;
/**
  *  Sets the horizontal radius of the ellipse.
  *  @param {number} rx  The horizontal radius of the ellipse.
  */
DvtOval.prototype.setRx = function(rx)
{
   this._impl.setRx(rx) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Gets the vertical radius of the ellipse.
  *  @type number
  */
DvtOval.prototype.getRy = function()
{
   return this._impl.getRy() ;
} ;
/**
  *  Sets the vertical radii of the ellipse.
  *  @param {number} ry  The vertical radius of the ellipse.
  */
DvtOval.prototype.setRy = function(ry)
{
   this._impl.setRy(ry) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Object initializer.
  *  @protected
  */
DvtOval.prototype.Init = function(context, cx, cy, rx, ry, id)
{
  if (! this.getImpl()) {
    this.setImpl(context.getImplFactory().newOval(cx, cy, rx, ry, id)) ;
  }
  DvtOval.superclass.Init.call(this, context) ;
};

/**
  *  Sets both the horizontal and vertical radii of the ellipse.
  *  @param {number} rx  The horizontal radius of the ellipse.
  *  @param {number} ry  The vertical radius of the ellipse.
  */
DvtOval.prototype.setRadius = function(rx, ry)
{
   this.setRx(rx);
   this.setRy(ry);
} ;

/**
 * @override
 */
DvtOval.prototype.copyShape = function()
{
  return new DvtOval(this.getCtx(), this.getCx(), this.getCy(), this.getRx(), this.getRy());
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  *  Creates a shape using SVG path commands.
  *  @extends DvtShape
  *  @class DvtPath  Creates an arbitrary shape using SVG path commands.
  *  @constructor
  *  @param {DvtContext} context  The platform dependent context object (such
  *                               as {@link DvtSvgContext}).
  *  @param {Object} cmds  Optional string of SVG path commands (see comment for
  *                        {@link DvtPath#setCmds}), or an array containing
  *                        consecutive command and coordinate entries (see comment
  *                        for {@link DvtPath#setCommands}).
  * @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var  DvtPath = function(context, cmds, id)
{
   this.Init(context, cmds, id) ;
};

DvtObj.createSubclass(DvtPath, DvtShape, "DvtPath") ;

/**
 *  Gets the path from a string of SVG commands sequences. See also {@link DvtPath#setCommands}.
 *  @type {String} cmds A string containing the SVG commands sequences.
 */
DvtPath.prototype.getCmds = function () {
  if(!this._sCmds)
    this._sCmds = this._impl.getCmds();

  return this._sCmds;
}

/**
 *  Sets the path from a string of SVG command sequences. See also {@link DvtPath#setCommands}.
 *  @param {String} cmds A string containing SVG command sequences.
 */
DvtPath.prototype.setCmds = function (cmds) {
  this._impl.setCmds(cmds);

  // Update the cached string commands
  this._sCmds = cmds;
  this._aCmds = null; // this will be populated on the next getCommands call
  this.UpdateSelectionEffect();
}

/**
 *  Gets the array of consecutive SVG path command sequences supplied by
 *  the last setCommands().
 *  @type Array  An array of commands and coordinates.
 */
DvtPath.prototype.getCommands = function () {
  if(this._aCmds) // If cached copy exists, use it.
    return this._aCmds;

  // Otherwise, convert, cache and return.
  this._sCmds = this.getCmds();
  this._aCmds = this._sCmds ? DvtPathUtils.createPathArray(this._sCmds) : null;
  return this._aCmds;
}

/**
 *  Sets the path from an array of consecutive SVG path
 *  command sequences. See also {@link DvtPath#setCmds}.
 *  @param {Array} ar  An array of commands and coordinates.
 */
DvtPath.prototype.setCommands = function (ar) {
  var sCmds = ar ? DvtPathUtils.getPathString(ar) : null;
  this.setCmds(sCmds);
  
  // Cache the string and array
  this._aCmds = ar;
  this._sCmds = sCmds;
  this.UpdateSelectionEffect();
}

/**
  *  Object initializer.
  *  @protected
  */
DvtPath.prototype.Init = function(context, cmds, id)
{
  if (! this.getImpl()) {
    this.setImpl(context.getImplFactory().newPath(cmds, id)) ;
  }
  DvtPath.superclass.Init.call(this, context) ;

};

/**
 * @override
 */
DvtPath.prototype.copyShape = function()
{
  return new DvtPath(this.getCtx(), this.getCmds());
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Creates a polygon from an array of consecutive (x,y) coordinates.
  * @extends DvtShape
  * @constructor  
  * @param {DvtContext} context   The platform dependent context object (such
  *                               as {@link DvtSvgContext}).
  * @param {Array} ar  An array of consecutive x,y coordinate pairs.
  * @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var  DvtPolygon = function(context, arPoints, id)
{
   this.Init(context, arPoints, id) ;
};

DvtObj.createSubclass(DvtPolygon, DvtShape, "DvtPolygon") ;

/**
  *  Returns the coordinates of the polygon vertices.
  *  @type Array
  */
DvtPolygon.prototype.getPoints = function()
{
   return this._impl.getPoints() ;
} ;

/**
  *   Sets the polygon points from an array of x,y coordinate pairs.
  *   @param {Array} arPoints  An array of x, y pairs of coordinates.
  */
DvtPolygon.prototype.setPoints = function(arPoints)
{
   this._impl.setPoints(arPoints) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Object initializer.
  *  @protected
  */
DvtPolygon.prototype.Init = function(context, arPoints, id)
{
  if (! this.getImpl()) {
    this.setImpl(context.getImplFactory().newPolygon(arPoints, id)) ;
  }
  DvtPolygon.superclass.Init.call(this, context) ;
};

/**
 * @override
 */
DvtPolygon.prototype.copyShape = function()
{
  return new DvtPolygon(this.getCtx(), this.getPoints());
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  *  Creates a shape of joined lines whose vertices are specified by
  *  an array of point coordinates.
  *  @extends DvtShape
  *  @constructor  
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {Array} arPoints   An array of consecutive x,y coordinate pairs.
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *  @returns {DvtPolyline} A new DvtPolyline object.
  */
var  DvtPolyline = function(context, arPoints, id)
{
   this.Init(context, arPoints, id) ;
};

DvtObj.createSubclass(DvtPolyline, DvtShape, "DvtPolyline") ;

DvtPolyline.prototype.getPoints = function()
{
   return this._impl.getPoints() ;
} ;

/**
 *  Sets the points for the line.
 *  e.g. for single line    myLine.setPoints(x1,y1,x2,y2)
 *
 *       for polyline       setPoints( [x1,y1,x2,y2,x3,y3,x4,y4, . . .])
 */
DvtPolyline.prototype.setPoints = function(array)
{
  // Slightly moving the last point in firefox. Fix for bug 18686189 
   if (DvtAgent.isPlatformGecko())
     array[array.length-1] += 0.1;
   this._impl.setPoints(array) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Object initializer.
  *  @protected
  */
DvtPolyline.prototype.Init = function(context, arPoints, id)
{
  if (! this.getImpl()) {
    this.setImpl(context.getImplFactory().newPolyline(arPoints, id)) ;
  }
  DvtPolyline.superclass.Init.call(this, context) ;
};

/**
 * @override
 */
DvtPolyline.prototype.copyShape = function()
{
  return new DvtPolyline(this.getCtx(), this.getPoints());
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  *  Creates a rectangular displayable shape.
  *  @extends DvtShape
  *  @class DvtRect is a platform independent class representing a displayable
  *  rectangle.
  *  <p>
  *  <b>Example:</b><br><br> <code>
  *  var rect = new DvtRect(context, 10, 10, 100, 150, 'myRect') ;<br>
  *  rect.setStroke(new DvtStroke("red", 5)) ;<br>
  *<br>
  *  // Rounded rectangle<br><br>
  *  var rect = new DvtRect(context, 10, 10, 100, 150) ;<br>
  *  rect.setCornerRadius(20) ;<br>
  *</code>
  *  @constructor  
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {number} x  The x position of the top left corner of the rectangle.
  *  @param {number} y  The y position of the top left corner of the rectangle.
  *  @param {number} w  The width of the rectangle.
  *  @param {number} h  The height of the rectangle.
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var  DvtRect = function(context, x, y, w, h,id)
{
   this.Init(context, x, y, w, h, id) ;
};

DvtObj.createSubclass(DvtRect, DvtShape, "DvtRect") ;

/**
  *  Returns the <code>x</code> coordinate of the rectangle.
  *  @returns {number} the <code>x</code> coordinate of the rectangle.
  */
DvtRect.prototype.getX = function()
{
   return this._impl.getX() ;
} ;

/**
  *  Sets the <code>x</code> coordinate of the top left position of the rectangle.
  *  @param {number} x  The <code>x</code> coordinate of the top left coordinate of the rectangle.
  */
DvtRect.prototype.setX = function(x)
{
   this._impl.setX(x) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Returns the <code>y</code> coordinate of the rectangle.
  *  @returns {number} the <code>y</code> coordinate of the rectangle.
  */
DvtRect.prototype.getY = function(y)
{
   return this._impl.getY() ;
} ;

/**
  *  Sets the <code>y</code> coordinate of the top left position of the rectangle.
  *  @param {number} y  The <code>y</code> coordinate of the top left coordinate of the rectangle.
  */
DvtRect.prototype.setY = function(y)
{
   this._impl.setY(y) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Returns the width of the rectangle.
  *  @returns {number} the width of the rectangle.
  */
DvtRect.prototype.getWidth = function()
{
   return this._impl.getWidth() ;
} ;

/**
  *  Sets the width of the rectangle.
  *  @param {number} w  The width of the rectangle.
  */
DvtRect.prototype.setWidth = function(w)
{
   this._impl.setWidth(w) ;
   this.UpdateSelectionEffect();
} ;


/**
  *  Gets the height of the rectangle.
  *  @returns {number}  The height of the rectangle.
  */
DvtRect.prototype.getHeight = function()
{
   return this._impl.getHeight() ;
} ;

/**
  *  Sets the height of the rectangle.
  *  @param {number} h  The height of the rectangle.
  */
DvtRect.prototype.setHeight = function(h)
{
   this._impl.setHeight(h) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Gets the horizontal radius of the rounded corners, if any.
  *  @type number
  */
DvtRect.prototype.getRx = function()
{
   return this._impl.getRx() ;
} ;

/**
  *  Sets the horizontal radius of the rounded corners.
  *  @param {number} rx  The horizontal radius of the rounded corners.
  */
DvtRect.prototype.setRx = function(rx)
{
   this._impl.setRx(rx) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Gets the vertical radius of the rounded corners, if any.  If not specified, 
  *  the vertical radius defaults to be the same as the horizontal radius.
  *  @type number
  */
DvtRect.prototype.getRy = function()
{
   return this._impl.getRy() ;
} ;
/**
  *  Sets the vertical radius of the rounded corners.  If not specified, the 
  *  vertical radius defaults to be the same as the horizontal radius.
  *  @param {number} ry  The vertical radius of the rounded corners.
  */
DvtRect.prototype.setRy = function(ry)
{
   this._impl.setRy(ry) ;
   this.UpdateSelectionEffect();
} ;

/**
  *  Object initializer.
  *  @protected
  */
DvtRect.prototype.Init = function(context, x, y, w, h, id)
{
  if (! this.getImpl()) {
    this.setImpl(context.getImplFactory().newRect(x, y, w, h, id)) ;
  }
  DvtRect.superclass.Init.call(this, context) ;

};

/**
  *  Sets the top left position of the rectangle.
  *  @param {number} x  The <code>x</code> coordinate of the top left coordinate of the rectangle.
  *  @param {number} y  The <code>y</code> coordinate of the top left coordinate of the rectangle.
  */
DvtRect.prototype.setPos = function(x,y)
{
   this.setX(x) ;
   this.setY(y) ;
};

/**
  *  Sets the corner radius value(s) to create rounded corners.
  *  @param {number} rx  The horizontal (x) radius.
  *  @param {number} ry  Optional: The vertical (y) radius. if omitted the rx
  *                      value is used.
  */
DvtRect.prototype.setCornerRadius = function(rx, ry)
{
   this.setRx(rx);
   this.setRy(ry);
} ;

/**
  *  Sets the position and size of the rectangle.
  *  May be specified as individual values or using a DvtRectangle object.
  *  <p>
  *  Example<br><br><code>
  *  rect.setRect(10, 10, 50, 100) ; &nbsp;  or<br>
  *  <br>
  *  rect.setRect(myRect) ; &nbsp; &nbsp;  where myRect = new DvtRectangle(10, 10, 50, 100);<br>
  *
  *  @param {number} x  The x position of the top left corner of the rectangle.
  *  @param {number} y  The y position of the top left corner of the rectangle.
  *  @param {number} w  The width of the rectangle.
  *  @param {number} h  The height of the rectangle.
  */
DvtRect.prototype.setRect = function(x,y,w,h)
{
   if (x instanceof DvtRectangle) {
     this.setPos(x.x, x.y) ;
     this.setWidth(x.w) ;
     this.setHeight(x.h) ;
   }
   else {
     this.setPos(x,y) ;
     this.setWidth(w) ;
     this.setHeight(h) ;
   }
   this.UpdateSelectionEffect();
} ;

/**
 * @override
 */
DvtRect.prototype.copyShape = function()
{
  var copy = new DvtRect(this.getCtx(), this.getX(), this.getY(), this.getWidth(), this.getHeight());
  copy.setRx(this.getRx());
  copy.setRy(this.getRy());
  return copy;
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/**
 * Creates an instance of DvtText.
 * @extends  DvtShape
 * @class DvtText
 * @constructor
 * @param {DvtContext} context   The platform dependent context object (such
 *                               as {@link DvtSvgContext}).
 * @param {number} x  
 * @param {number} y  
 * @param {String} text  Text to be displayed.
 * @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
 */
var  DvtText = function(context, text, x, y, id)
{
  this.Init(context,  text, x, y, id) ;
};

DvtObj.createSubclass(DvtText, DvtShape, "DvtText") ;

DvtText.ELLIPSIS = "...";

DvtText._BULK_TRUNCATION = true;

//bulk truncation internal state
DvtText._TRUNCATION_DONE = 0;
DvtText._TRUNCATION_GUESS_TOO_SHORT = 1;
DvtText._TRUNCATION_GUESS_TOO_LONG = 2;

// horizontal alignments
DvtText.H_ALIGN_START    = 'start';
DvtText.H_ALIGN_CENTER   = 'center';
DvtText.H_ALIGN_END      = 'end';
DvtText.H_ALIGN_DEFAULT  = DvtText.H_ALIGN_START;

// vertical alignments
DvtText.V_ALIGN_TOP      = 'top';
DvtText.V_ALIGN_MIDDLE   = 'middle';
DvtText.V_ALIGN_BOTTOM   = 'bottom';
DvtText.V_ALIGN_BASELINE = 'baseline';
DvtText.V_ALIGN_DEFAULT  = DvtText.V_ALIGN_TOP;

/**
  *  Object initializer.
  *  @protected
  */
DvtText.prototype.Init = function(context, text, x, y, id)
{
  if (! this.getImpl()) {
    this.setImpl(context.getImplFactory().newText(text, x, y, id)) ;
  }
  DvtText.superclass.Init.call(this, context) ;

  this._Ctx = context ;

  this._maxw = -1;
  this._minChars = null; // minimum number of characters before ellipses
  
  this._bHtmlText = false;

  //flag indicating whether text is truncated
  this._bTruncated = false;
  this._inProcess = false;

  this._horizAlign = DvtText.H_ALIGN_DEFAULT;
  this._vertAlign = DvtText.V_ALIGN_DEFAULT;
  
  // set the text alignment to the default (start, top)
  if (DvtText.H_ALIGN_DEFAULT != DvtText.H_ALIGN_START)
    this.setHorizAlignment(DvtText.H_ALIGN_DEFAULT);
  if (DvtText.V_ALIGN_DEFAULT != DvtText.V_ALIGN_BASELINE)
    this.setVertAlignment(DvtText.V_ALIGN_DEFAULT);

  // for defer truncation
  this._defTrunc = false;
  this._setCachedDims(undefined);
  
  // for legend text to keep track of whether associated legend marker is hollow
  this._bHollow = false;
  
  // Clone
  this._cloneCSSStyle = null;

};


/**
 * Returns the text string for this text object.
 * @return {string} the text string
 */ 
DvtText.prototype.getTextString = function() {

  return String(this._impl.getTextString());
} ;


/**
 * Sets the text angle. Only graph should use this. All other components should use setRotation on DvtDisplayable.
 * @param {number} a the angle in degrees. Positive angle rotate counter-clockwise.
 */
DvtText.prototype.doNotUseSetAngle = function(a)
{
  this._impl.doNotUseSetAngle(a) ;
};

/**
  *  Returns the edit mode of this text object.
  *  @return {boolean}  true if edift mode is on, else false.
  */
DvtText.prototype.getEditMode = function()
{
   return this._impl.getEditMode() ;
};

/**
  *  Sets the edit mode of this text object.
  *  @param {boolean} bEditMode  true if edit mode is to be set on, else false.
  *  @param {function} editStartCallback  optional function to call when editing starts that takes the string as
  *         an argument and can optionally return a replacement string
  *  @param {function} editEndCallback  optional function to call when editing ends that takes the string as
  *         an argument and can optionally return a replacement string
  */
DvtText.prototype.setEditMode = function(bEditMode, editStartCallback, editEndCallback)
{
   this._impl.setEditMode(bEditMode, editStartCallback, editEndCallback) ;
};

/**
  *  Sets the pixel width of the edititable area.
  *  @param {number} pxWidth  the pixel width of the edit area.
  */
DvtText.prototype.setEditWidth = function(pxWidth)
{
   this._impl.setEditWidth(pxWidth) ;
};


/**
  *  Sets the tooltip for this text object when it is in edit mode.
  *  @param {string} sTip  the tooltip to be shown when the cursor is
  *                        over the editable area and edit mode is set.
  */
DvtText.prototype.setEditTooltip = function(sTip)
{
   this._impl.setEditTooltip(sTip) ;
};


DvtText.prototype.getEditBorder = function(bShow)
{
   return this._impl.getEditBorder() ;
};

DvtText.prototype.setEditBorder = function(bShow)
{
   this._impl.setEditBorder(bShow) ;
};

DvtText.prototype.selectEditText = function() {
  if (this._impl.selectEditText) {
    this._impl.selectEditText();
  }
};

DvtText.prototype.focusEditText = function() {
  if (this._impl.focusEditText) {
    this._impl.focusEditText();
  }
};

/**
 * Sets the rotation point for angled text (see {@link DvtText#setAngle}).
 * @param {number} cx the rotation point x-coordinate.
 * @param {number} cy the rotation point y-coordinate.
 */
DvtText.prototype.doNotUseSetRotatePoint = function(cx, cy)
{
  this._impl.doNotUseSetRotatePoint(cx, cy) ;
}; 


/**
 * Sets the text string for this text object.
 * @param {string} textStr the text string
 */
DvtText.prototype.setTextString = function(txt)
{
  this._text = txt;
  this._impl.setTextString(txt) ;
  this._setCachedDims(undefined);
} ;

/**
 * Gets the x position for this text object.
 * @returns {number}  the x position.
 */
DvtText.prototype.getX = function()
{
  return this._impl.getX() ;
}; 
/**
 * Sets the x position for this text object.
 * @param {number} x the x position
 */
DvtText.prototype.setX = function(x)
{
  this._impl.setX(x) ;
}; 


/**
 * Gets the y position for this text object.
 * @returns {number}  the y position.
 */
DvtText.prototype.getY = function()
{
  return this._impl.getY() ;
}; 
/**
 * Sets the y position for this text object.
 * @param {number} y the y position
 */
DvtText.prototype.setY = function(y)
{
  this._impl.setY(y) ;
};


/**
 * Sets the font size.
 * @param {number} fontSize the font size
 */
DvtText.prototype.setFontSize = function(fontSize)
{
  this._impl.setFontSize(fontSize);
  this._setCachedDims(undefined);
  var style = this.getCSSStyle();
  if (style) {
    this._cloneCSSStyle = style.clone();
    this._cloneCSSStyle.setFontSize(DvtCSSStyle.FONT_SIZE, fontSize.toString());
  }
  else {
    this.setCSSStyle(new DvtCSSStyle());
    (this.getCSSStyle()).setFontSize(DvtCSSStyle.FONT_SIZE, fontSize.toString());
  }
    
};

// TODO JSDOC Not sure how to expose these, so this is temporary
DvtText.prototype.alignStart = function() {
  this._impl.alignStart();
  this._horizAlign = DvtText.H_ALIGN_START;
}

// TODO JSDOC Not sure how to expose these, so this is temporary
DvtText.prototype.alignCenter = function() {
  this._impl.alignCenter();
  this._horizAlign = DvtText.H_ALIGN_CENTER;
}

// TODO JSDOC Not sure how to expose these, so this is temporary
DvtText.prototype.alignEnd = function() {
  this._impl.alignEnd();
  this._horizAlign = DvtText.H_ALIGN_END;
}

// TODO JSDOC Not sure how to expose these, so this is temporary
DvtText.prototype.alignTop = function() {
  //save the member first so that the impl can depend on it
  this._vertAlign = DvtText.V_ALIGN_TOP;
  this._impl.alignTop();
};

// TODO JSDOC Not sure how to expose these, so this is temporary
DvtText.prototype.alignMiddle = function() {
  //save the member first so that the impl can depend on it
  this._vertAlign = DvtText.V_ALIGN_MIDDLE;
  this._impl.alignMiddle();
};

// TODO JSDOC Not sure how to expose these, so this is temporary
DvtText.prototype.alignBottom = function() {
  //save the member first so that the impl can depend on it
  this._vertAlign = DvtText.V_ALIGN_BOTTOM;
  this._impl.alignBottom();
};

// TODO JSDOC Not sure how to expose these, so this is temporary
DvtText.prototype.alignBaseline = function() {
  //save the member first so that the impl can depend on it
  this._vertAlign = DvtText.V_ALIGN_BASELINE;
  this._impl.alignBaseline();
};

/**
 * @return {Number} The horizontal alignment
 */
DvtText.prototype.getHorizAlignment = function() {
  return this._horizAlign;
}

/**
 * @return {Number} The vertical alignment
 */
DvtText.prototype.getVertAlignment = function() {
  return this._vertAlign;
}

/**
 * @param align {String} The horizontal alignment
 */
DvtText.prototype.setHorizAlignment = function(align) {
  if (align == DvtText.H_ALIGN_START)
    this.alignStart();
  else if (align == DvtText.H_ALIGN_CENTER)
    this.alignCenter();
  else if (align == DvtText.H_ALIGN_END)
    this.alignEnd();
}

/**
 * @param align {String} The vertical alignment
 */
DvtText.prototype.setVertAlignment = function(align) {
  if (align == DvtText.V_ALIGN_TOP)
    this.alignTop();
  else if (align == DvtText.V_ALIGN_MIDDLE)
    this.alignMiddle();
  else if (align == DvtText.V_ALIGN_BOTTOM)
    this.alignBottom();
  else if (align == DvtText.V_ALIGN_BASELINE)
    this.alignBaseline();
}


/**
 * Truncates the given text element to fit within the available space. Adds the text to the container, and removes it
 * if the text can't fit at all.
 * @param {DvtContainer} container Element used temporarily for calculating the bounds of the text, must be connected.
 * @param {function} callback The callback function that takes a bbox and returns true if the text fits
 * @param {object} cbArgs The arguments to pass the callback, after the bbox
 * @param {number} minChars (optional) Minimum number of characters before the ellipsis. Default is 1.
 * @return {DvtText} The text element, truncated to fit in the given space, or null if the text can't fit at all.
 */
DvtText.prototype.truncateToCallback = function(container, callback, cbArgs, minChars) {
  // If the text fits, return immediately
  container.addChild(this);
  var cssFontStyles = this.GetFontStyles();
  var text = this.getTextString();
  var dim = this._measureText(text, cssFontStyles);
  if (callback(dim, cbArgs))
    return this;
  
  // If the text doesn't fit, attempt to truncate
  if (isNaN(minChars) || minChars == null)
    minChars = 1;
  var maxNumChars = Math.max(text.length-1, minChars);
  
  //Determine whether we can use context to measure text.
  //If so, don't need to call getDimensions() at all, and only need to setTextString() at the very end.
  var tmpTextString = text.substring(0, maxNumChars) + DvtText.ELLIPSIS;
  dim = this._measureText(tmpTextString, cssFontStyles);

  // current guess is too long
  while (!callback(dim, cbArgs)) {
    if (maxNumChars <= minChars) {
      tmpTextString = "";
      dim.w = 0;
      break;
    }
    
    // Truncate 1 char at a time
    maxNumChars -= 1;
    tmpTextString = text.substring(0, maxNumChars) + DvtText.ELLIPSIS;
    dim = this._measureText(tmpTextString, cssFontStyles);
  }
  
  this.setTextString(tmpTextString);
  
  // check if the truncated text can fit
  if (tmpTextString == "") {
    container.removeChild(this);
    return null;
  } else {
    return this;
  }
}


/**
 * Set the maximum width allowed for this text field.  
 * If this text field is longer than the maxWidth,
 * the text will be truncated.
 * 
 * @param mw maximum width
 * @param minChars minimum number of characters before ellipses
 */
DvtText.prototype.setMaxWidth = function(mw, minChars) {
  this._maxw = mw;
  this._minChars = minChars;

  if (mw > 0)
    this._ProcessText();
}

/**
 * Gets the text string for this object.
 * @return {string} the text string
 */
DvtText.prototype.getText = function() {
  if (this.getEditMode() && this._impl.getText) {
    return this._impl.getText();
  }
  return this._text;
}

/**
 * Sets the text string for this text object.
 * @param {array} single line text or multiline text
 * @param {array} dimList Optional dimensions of multilines
 */
DvtText.prototype._setTextWithSize = function(text, dimList) {
  this._impl.setText(text, dimList);
}

/**
 * Sets the TruncateAt for this text object.
 * @param {number} truncateAt
 */
DvtText.prototype.setTruncateAt = function(truncateAt) {
  this._truncateAt = truncateAt;

  // reset the text
  this.DoSetText(this.getText());
}

/**
 * Gets the TruncateAt for this text object.
 * @return {number} truncateAt
 */
DvtText.prototype.getTruncateAt = function() {
  return this._truncateAt;
}

/**
 * Sets the text string for this object.
 * @param {string} text the text string
 */
DvtText.prototype.DoSetText = function(text) {
  //handle "truncate at" if specified
  text = this.DoTruncateAt(text, this.getTruncateAt());

  this._setCachedDims(undefined);
  this._setText(text);
  if (text)
    this._ProcessText();
};

/**
 * Truncate text at number of character specified
 * @param text text being truncated
 * @param {number} truncateAt
 * @return (String) truncated text
 */
DvtText.prototype.DoTruncateAt = function(text, truncateAt) {
  this._bTruncated = false;

  if (truncateAt && text && text.length > truncateAt) {
    text = text.substr(0, Math.max(truncateAt - 3, 0)) + "...";
    this._bTruncated = true;
  }

  return text;
}
    
/**
 * Determine if the text has been truncated.
 * @return true if text has been truncated, false if not
 */
DvtText.prototype.isTruncated = function() {
  return this._bTruncated;
}
    
/**
 * Is truncation enabled?
 * @return true if truncation is enabled, false if not
 */
DvtText.prototype.isTruncationEnabled = function() {
  //Note: no truncation on html text, because that's harder
  return (this._maxw > 0) && (! this._bHtmlText);
}
    
/**
 * Sets the text string for this text object.
 * It also performs word wrapping or truncation if enabled
 * @param {string} text the text string
 */
DvtText.prototype.setText = function(text) {
  // save the original text
  this._text = text;
  this.DoSetText(text);
}

/**
 * Sets the text string for this text object.
 * @param {array} single line text or multiline text
 */
DvtText.prototype._setText = function(text) {
  this._impl.setText(text);
}

/**
 * Gets the text string for this text object.
 * @return {string} the text string
 */
DvtText.prototype._getText = function() {
  return this._impl.getText();
}

/**
 * Sets the CSSStyle for this text object.
 * perform truncation if enabled
 * @param value text being set
 */
DvtText.prototype.setCSSStyle = function(style) {
  if(this._cloneCSSStyle)
    this._cloneCSSStyle = null;
  this._setCachedDims(undefined);
  DvtText.superclass.setCSSStyle.call(this, style);
  if (this._text !== null && typeof this._text !== "undefined") {
    this.setText(this.getText());  // Force the untruncated text into the SVGTextElement
  }
  else {
    this._ProcessText();
  }
}

/**
 * Calculate the optimal text size based on the bounds provided.
 * @param {DvtRectangle} bounds Bounds of label
 */
DvtText.prototype.getOptimalFontSize = function(bounds) {
  var height = bounds.h;
  var width = bounds.w;
  var text = this.getTextString();
  for(var i = Math.max(Math.min(width/text.length, height/2), 9); i < 50; i += 1) {
    this.setFontSize(i);
    var textDim = this.measureDimensions();
    if(textDim.w > width || textDim.h > height) {
      return i - 1;
    }
  }
  return 50;
}

/**
 * @protected
 * Get the font styles.
 * @return {DvtCSSStyle} the font styles
 */
DvtText.prototype.GetFontStyles = function() {
  return this.getImpl().getFontStyles();
};

/**
 * Performs truncation.
 */
DvtText.prototype._ProcessText = function() {
  if (this.isTruncationEnabled() && ! this._inProcess) {
    this._inProcess = true;
    this._TruncateText();
    this._inProcess = false;
  }
}

/**
 * Check whether the text being shown needs to be truncated,
 * and truncate it if so.  
 */
DvtText.prototype._TruncateText = function() {
  if (this.isTruncationEnabled()) {
    var maxLineWidth = this._maxw;
    
    var minChars = this._minChars;
    var cacheEnabled = false;
    if (isNaN(minChars) || minChars == null) {
      minChars = 0;
      cacheEnabled = true;
    }
    
    //indicating whether text is truncated
    this._bTruncated = false;
    
    var dim;
    if (cacheEnabled)
      dim = this.getCachedDims();
    else
      dim = this.measureDimensions();
    
    if (dim.w > maxLineWidth) {
      this._bTruncated = true;

      // defer text truncation
      if (! this._defTrunc) {
        dim = this._truncateLine(maxLineWidth, minChars, dim);
        if (cacheEnabled)
          this._setCachedDims(dim);
      }
    }
  }
}

DvtText.prototype._GetTempTextField = function() {
  if (this._ttf === undefined) {
    var tmp = new DvtText(this._Ctx, null, 0, 0, this.getId() + "__tmp");

    //Bug 13878815 - TEXT IN NODE WRAPS UNNECESSARILY AT DIFFERENT ZOOM LEVELS
    //don't add the tempTextField to this parent, add it to the stage instead
    //so we always get the same dimensions
    // Temporarily add to the DOM so that metrics may be obtained
//    if (this.getParent()) {
//      this.getParent().addChild(tmp);
//    }
    var stage = this.getCtx().getStage();
    stage.addChild(tmp);

    this._ttf = tmp;
  }
  return this._ttf;
}

// remove the temporary text field
DvtText.prototype._RemoveTempTextField = function() {
  if (this._ttf) {
    //Bug 13878815 - TEXT IN NODE WRAPS UNNECESSARILY AT DIFFERENT ZOOM LEVELS
//    if (this.getParent()) {
//      this.getParent().removeChild(this._ttf);
//    }
    var stage = this.getCtx().getStage();
    stage.removeChild(this._ttf);

    this._ttf = undefined;
  }
}


DvtText.prototype.getCachedDims = function() {
  var dim = this._dim;
  if (! dim) {
    dim = this.GetDimsForCaching();
    this._setCachedDims(dim);
  }
  return dim;
}

/**
 * Get dimensions for caching by getCachedDims().
 */
DvtText.prototype.GetDimsForCaching = function() {
  //DvtText always returned the superclass getDimensions call, but I'm not
  //sure why.  That call only calls the superclass function, not any 
  //overrides in subclasses, like the one here in DvtText itself.  
  //It seems like it should return this.getDimensions(), so that
  //any subclass overrides get called.
  return DvtText.superclass.getDimensions.call(this);
};

DvtText.prototype._setCachedDims = function(val) {
  DvtDisplayableUtils._setDimForced(this, val);
}


/**
 * set defer text truncation flag
 * if it's not defer, truncate text
 */
DvtText.prototype.truncateText = function(deferFlag) {
  this.setTruncationDeferred(deferFlag);
  if (! deferFlag) {
    this._ProcessText();
  }
}

DvtText.prototype.setTruncationDeferred = function(deferFlag) {
  this._defTrunc = deferFlag;
}

DvtText.prototype.isTruncationDeferred = function() {
  return this._defTrunc;
}

/**
 * Truncate texts for an array of DvtText.
 * @param {array} textFields 
 */
DvtText.truncateTexts = function(textFields) {

  if (DvtText._BULK_TRUNCATION) {
    DvtText._bulkTruncation(textFields);
  }
  else {
    var len = textFields.length;
    for (var i = 0; i < len; i++) {
      var deferFlag = textFields[i].isTruncationDeferred();
      textFields[i].setTruncationDeferred(false);
      try {
        textFields[i]._ProcessText();
      }
      finally {
        textFields[i].setTruncationDeferred(deferFlag);
      }
    }
  }
}

/**
  *  @override
  */
DvtText.prototype.getDimensions = function(targetCoordinateSpace) {
  if (this.isTruncated() && this._defTrunc && !this._dim) {
    //Don't call superclass getDimensions(), because that will convert the coord space rect and
    //then we wouldn't be able to simply assign the maxWidth value here without converting that 
    //to the target coord space first.  Instead, get the unconverted dims from the impl,
    //assign maxWidth, and then convert the rect.  
    var dim = this.getImpl().getDimensions();
    dim.w = this._maxw;
    return (!targetCoordinateSpace || targetCoordinateSpace === this) ? dim :
      this.ConvertCoordSpaceRect(dim, targetCoordinateSpace);
  }

  return DvtText.superclass.getDimensions.call(this);
}

/**
 *  @override
 */
DvtText.prototype.getCSSStyle = function() {
  if(this._cloneCSSStyle) 
    return this._cloneCSSStyle;  
  else
    return DvtText.superclass.getCSSStyle.call(this);
};

/**
 * Optimized version of getDimensions. Uses measureText (i.e. a dedicated DvtText object) to get the dimensions for
 * SVG. If measureText is used, the text will not be added to the container in order to enhance performance.
 * @param {DvtContainer} container (optional) If provided, the text will be added to the container before calling
 *     getDimensions and then removed from it, if measureText is not supported.
 * @param {DvtDisplayable} targetCoordinateSpace (optional) The displayable defining the target coordinate space
 * @return {DvtRectangle} The bounds of the text
 */
DvtText.prototype.measureDimensions = function(container, targetCoordinateSpace) {
  var dim;
  var context = this.getCtx();
  
  if (context.supportsMeasureText()) {
    var tmpDim = context.measureText(this.getTextString(), this.GetFontStyles(), this._horizAlign, this._vertAlign);
    dim = new DvtRectangle(tmpDim.x + this.getX(), tmpDim.y + this.getY(), tmpDim.w, tmpDim.h);
    if (this.isTruncated() && this._defTrunc && !this._dim)
      dim.w = this._maxw;
    return (!targetCoordinateSpace || targetCoordinateSpace === this) ? dim :
      this.ConvertCoordSpaceRect(dim, targetCoordinateSpace);
  }
  else {
    if (!container)
      return this.getDimensions(targetCoordinateSpace);
    
    container.addChild(this);
    dim = this.getDimensions(targetCoordinateSpace);
    container.removeChild(this);
    return dim;
  }
}


/**
 * Measures the dimensions of a text string. If measureText is not supported, then the textString will be set before
 * calling getDimensions. Used for truncation methods.
 * @param {string} textString The text string to measure
 * @param {DvtCSSStyle} cssStyle CSS object specifying font styles
 * @returns {DvtRectangle} The text dimensions
 */
DvtText.prototype._measureText = function (textString, cssStyle) {
  var context = this.getCtx();
  if (context.supportsMeasureText()) {
    var tmpDim = context.measureText(textString, cssStyle, this._horizAlign, this._vertAlign);
    return new DvtRectangle(tmpDim.x + this.getX(), tmpDim.y + this.getY(), tmpDim.w, tmpDim.h);
  } else {
    this.setTextString(textString);
    return this.getDimensions();
  } 
}


/**
 * Truncates the line to a given width. Assumes that the full text can't fit in the container.
 * @param {number} maxLineWidth Maximum line width
 * @param {number} minChars Minimum number of characters before the ellipsis
 * @param {DvtRectangle} dim The dimensions of the text before truncation
 * @return {DvtRectangle} The dimensions of the text after truncation
 */
DvtText.prototype._truncateLine = function(maxLineWidth, minChars, dim) {
  // Determine avg pixels per char
  var text = this.getTextString();
  var avgPixel = dim.w / text.length;

  //substract 3 for the ellipsis
  var maxNumChars = Math.max(Math.floor(maxLineWidth/avgPixel) - 3, minChars);

  //Determine whether we can use context to measure text.
  //If so, don't need to call getDimensions() at all, and only need to setTextString() at the very end.
  var cssFontStyles = this.GetFontStyles();
  var tmpTextString = text.substring(0, maxNumChars) + DvtText.ELLIPSIS;
  dim = this._measureText(tmpTextString, cssFontStyles);

  // initial guess is too short
  while (dim.w < maxLineWidth) {
    if (maxNumChars >= text.length)
      break;

    // Add 1 char at a time
    maxNumChars += 1;
    tmpTextString = text.substring(0, maxNumChars) + DvtText.ELLIPSIS;
    dim = this._measureText(tmpTextString, cssFontStyles);
  }

  // current guess is too long
  while (dim.w > maxLineWidth) {
    if (maxNumChars <= minChars) {
      tmpTextString = "";
      dim.w = 0;
      break;
    }
    
    // Truncate 1 char at a time
    maxNumChars -= 1;
    tmpTextString = text.substring(0, maxNumChars) + DvtText.ELLIPSIS;
    dim = this._measureText(tmpTextString, cssFontStyles);
  }

  this.setTextString(tmpTextString);
  return dim;
}


DvtText._bulkSetDimensions = function(textFields) {
  var tf;

  for (var i = 0; i < textFields.length; i++) {
    tf = textFields[i];
    tf._setCachedDims(tf.GetDimsForCaching());

  }
}


//1) init state
DvtText.prototype._initTruncation = function() {
  var dim = this.getCachedDims();
  var minChars = this._minChars;
  if (isNaN(minChars) || minChars == null) {
    minChars = 0;
  }
  var maxLineWidth = this._maxw;

  // Determine avg pixels per char
  var text = this.getTextString();
  var avgPixel = dim.w / text.length;

  //substract 3 for the ellipsis
  var maxNumChars = Math.max(Math.floor(maxLineWidth/avgPixel) - 3, minChars);

  // save state
  this.setTextString(text.substring(0, maxNumChars) + DvtText.ELLIPSIS);
  this._maxNumChars = maxNumChars;
  this._setCachedDims(undefined);

  this._originalText = text;
  this._minChars2 = minChars;

  this._state = DvtText._TRUNCATION_GUESS_TOO_SHORT;
}


// This method returns true if truncation is done, return false otherwise.a
DvtText.prototype._doTruncation = function() {
  var state = this._state;

  var text = this._originalText;
  var dim = this.getCachedDims();
  var maxNumChars = this._maxNumChars;
  var maxLineWidth = this._maxw;

  // initial guess is too short
  if (state == DvtText._TRUNCATION_GUESS_TOO_SHORT) {
    if (dim.w < maxLineWidth) {
      if (maxNumChars >= text.length) {
        state = DvtText._TRUNCATION_GUESS_TOO_LONG;
      }
      else {
        // Add 1 char at a time
        maxNumChars += 1;

        // save state
        this.setTextString(text.substring(0, maxNumChars) + DvtText.ELLIPSIS);
        this._maxNumChars = maxNumChars;
        this._setCachedDims(undefined);
      }
    }
    // done with this state, go to next state
    else {
      state = DvtText._TRUNCATION_GUESS_TOO_LONG;
    }
  }
  if (state == DvtText._TRUNCATION_GUESS_TOO_LONG) {
    if (dim.w > maxLineWidth) {
      if (maxNumChars <= this._minChars2) {
        this.setTextString("");
        dim.w = 0;
        state = DvtText._TRUNCATION_DONE;
      }

      else {
        // Truncate 1 char at a time
        maxNumChars -= 1;
        this.setTextString(text.substring(0, maxNumChars) + DvtText.ELLIPSIS);

        // done with truncation
        if (maxNumChars == 0) {
          dim.w = 0;
          state = DvtText._TRUNCATION_DONE;
        }
        else {
          // save state
          this._maxNumChars = maxNumChars;
          this._setCachedDims(undefined);
        }
      }
    }
    else {
      state = DvtText._TRUNCATION_DONE;
    }
  }

  // if done, clear state and return true
  if (state == DvtText._TRUNCATION_DONE) {
    this._maxNumChars = undefined;
    this._originalText = undefined;
    this._minChars2 = undefined;
    this._state = undefined;
    return true;
  }
  else {
    this._state = state;
    return false;
  }
}


/**
 * Bulk truncation
 * @param {array} textFields list of texts need to be truncated
 */
DvtText._bulkTruncation = function(textFields) {
  var tf;
  var toDoList = [];
  var dim;

  //1) Init state
  for (var i = 0; i < textFields.length; i++) {
    tf = textFields[i];

    tf._bTruncated = false;
    dim = tf.getCachedDims();

    // truncation required
    if (dim.w > tf._maxw) {
      tf._bTruncated = true;

      tf._initTruncation();
      toDoList.push(tf);
    }
  }

  //getDimensions for all texts
  textFields = toDoList;
  DvtText._bulkSetDimensions(textFields);

  var done = false;
  toDoList = [];

  //2) Guess too short or too long
  while (! done) {
    for (var i = 0; i < textFields.length; i++) {
      tf = textFields[i];

      //if not done
      if (! tf._doTruncation()) {
        toDoList.push(tf);
      }
    }

    if (toDoList.length > 0) {
      textFields = toDoList;
      DvtText._bulkSetDimensions(textFields);
      toDoList = [];
    }
    else {
      done = true;
    }
  }
}

/**
 * @override
 */
DvtText.prototype.copyShape = function()
{
  return new DvtText(this.getCtx(), this.getText(), this.getX(), this.getY());
}

/**
 * @override
 */
DvtText.prototype.destroy = function()
{
  DvtText.superclass.destroy.call(this);
  if (this._impl.destroy)
    this._impl.destroy();
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*   DvtTextArea                   TextArea "shape"                    */
/*---------------------------------------------------------------------*/
/**
 * Creates an instance of DvtTextArea.
 * @extends  DvtText
 * @class DvtTextArea
 * @constructor
 * @param {DvtContext} context   The platform dependent context object (such
 *                               as {@link DvtSvgContext}).
 * @param {number} x the x position
 * @param {number} y the y position
 * @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
 */
var DvtTextArea = function(context, x, y, id) {
  this.Init(context, x, y, id) ;
};

DvtObj.createSubclass(DvtTextArea, DvtText, "DvtTextArea") ;

DvtTextArea.NEW_LINE = "\n";


DvtTextArea.TRIM_TRAILING_REGEXP = /\s+$/;


/**
 * Initializes this instance of DvtTextArea.
 */
DvtTextArea.prototype.Init = function(context, x, y, id) {

  if (! this.getImpl()) {
    this.setImpl(context.getImplFactory().newTextArea(x, y, id)) ;
  }
  DvtTextArea.superclass.Init.call(this, context) ;

  this._bWordWrap = true;
  
  // the maximum number of lines to be displayed in the text area
  this._maxLines = -1;
  //BUG FIX 13620233: maximum height allowed for wrapping
  this._maxHeight = -1;
  
  this._numLines = 1;
  
  this._bTruncated = false;

};



/**
 * Returns the number of lines displayed for this textArea object.
 * @return {number} the number of lines displayed
 */
DvtTextArea.prototype.getNumLines = function() {
  if(this.isWrappingEnabled()) 
    return this._numLines;  
  else
    return 1;
};



/**
 * Returns the amount of vertical space, in pixels, between lines
 * @return {number} the amount of vertical space (called leading) between lines
 */
DvtTextArea.prototype.getLeading = function() {
  return this._impl.getLeading();
};

/**
 * Sets the amount of vertical space, in pixels, between lines
 * @param {number} leading amount of vertical space, in pixels, between lines
 */
DvtTextArea.prototype.setLeading = function(leading) {
  this._impl.setLeading(leading);
};



/**
 * Returns the maximum number of lines that can be displayed for this textArea object.
 * @return {number} the maximum number of lines to be displayed
 */
DvtTextArea.prototype.getMaxLines = function() {
  return this._maxLines;
};

/**
 * Sets the maximum number of lines that can be displayed for this textArea object.
 * @param {number} maxLines the maximum number of lines to be displayed
 */
DvtTextArea.prototype.setMaxLines = function(maxLines) {
  this._maxLines = maxLines;
};

/**
 * Set the maximum height allowed for this text area.
 * 
 * @param mh maximum height
 */
DvtTextArea.prototype.setMaxHeight = function(mh) {
  this._maxHeight = mh;
};


/**
 *  @override
 */
DvtTextArea.prototype.DoSetText = function(text) {
  //handle "truncate at" if specified
  text = this.DoTruncateAt(text, this.getTruncateAt());

  var lines;
  text = text ? text : "";
  if (text) {
    this._setText(text);
    lines = text.split(DvtTextArea.NEW_LINE);
  }
  //TODO: Keep the original text??
  this._lines = lines;
  this._ProcessText();
};

/**
 * Set whether this text field should wrap words.
 * 
 * @param b true to wrap words, false otherwise
 */
DvtTextArea.prototype.setWordWrap = function(wrap) {
  this._bWordWrap = wrap;
};

/**
 * Determine whether this text field wraps words.
 * 
 * @return true if words are wrapped, false otherwise
 */
DvtTextArea.prototype.isWordWrap = function() {
  return this._bWordWrap;
};

/**
 * Is wrapping enabled?
 * @return true if truncation is enabled, false if not
 */
DvtTextArea.prototype.isWrappingEnabled = function() {
  //Note: no wrapping on html text, because that's harder
  return this.isWordWrap() && this._maxw > 0 && ! this._bHtmlText && this._lines;
};

/**
 * @override
 * Performs wrapping
 */
DvtTextArea.prototype._ProcessText = function() {
  if (! this._inProcess) {
    this._inProcess = true;
    if (this.isWrappingEnabled()) {
      this._wrapText();
    }
    else if (this.isTruncationEnabled()) {
      this._TruncateText();
    }
    this._inProcess = false;
  }
}


/**
 * Check whether the text being shown needs to be wrapped,
 * and wrap it if so.  
 */
DvtTextArea.prototype._wrapText = function() {
  //the maximum width allowed for a line of text
  var maxLineWidth = this._maxw;

  var lines = DvtArrayUtils.copy(this._lines);
  var numLines = lines.length;
  var tmptf = this._GetTempTextField();
  tmptf.setCSSStyle(this.getCSSStyle());

  var newTexts = [];
  var remainder;
  var dim;
  var max_hh = 0;
  var max_ww = 0;
  var preWrappedString;
  var truncateNonWrappedString = false;
  var maxOut = false;
  var dimList = [];
  var totalHeight = 0;

  //loop over all the lines and wrap each independently
  //end the loop once we've processed all lines, or have reached
  //the maximum number of lines to display
  for (var lineIndex = 0; lineIndex < numLines; lineIndex++) {
    tmptf._setText(lines[lineIndex]);
    dim = tmptf.getDimensions();

    // need word wrap if text width > max width
    if (dim.w > maxLineWidth) {
      preWrappedString = tmptf._getText();
      remainder = DvtTextArea._wrapLine(maxLineWidth, tmptf);

      //include the remainder of the line as a new next line in the text
      if (remainder) {

        //Bug 16949028 - AF:OUTPUTTEXT INSIDE DIGRAMNODES CANNOT DISPLAY CORRECTLY
        // BUGFIX 12724589 and 12780962
        // if we couldn't wrap this string, then truncate it
        if(preWrappedString == remainder) {
          truncateNonWrappedString = true;

          var wordOffsets = DvtTextArea._breakWords(preWrappedString);
          if (wordOffsets.length > 1) {
            remainder = preWrappedString.substring(wordOffsets[1].index);
            lines.splice(lineIndex + 1, 0, remainder);
            //increment line count
            numLines++;
          }
        }
        else {
          lines.splice(lineIndex + 1, 0, remainder);
          numLines++;
        }
      }
    }

    // exit the loop if we've reached the maximum number of lines to display 
    // or if the line is too long to wrap, thus requiring us to truncate it
    //BUG FIX 13620233: also truncate and exit the loop if there are more
    //lines to wrap, but we've hit the max height allowed (we actually 
    //check whether the NEXT line will be greater than the max height, 
    //assuming the next line is the same height as this line, because that 
    //means we need to truncate THIS line)

    //Bug 15868287 - IE9: NODES POSITIONED INCORRECTLY IN DIAGRAM EDITOR
    //IE9 dimensions rounding error
    var bMaxHeight = (this._maxHeight > -1) && 
                     (lineIndex < numLines - 1) && 
                     (totalHeight + 2 * dim.h > this._maxHeight + DvtMath.TOLERANCE);

    if(bMaxHeight || ((this._maxLines != -1) && (lineIndex == this._maxLines-1)) || truncateNonWrappedString)
    {
      if(bMaxHeight || (this._maxLines < numLines) || truncateNonWrappedString) {
        var s = tmptf._getText();
        // trim trailing whitespace and truncate if necessary
        s = s.replace(DvtTextArea.TRIM_TRAILING_REGEXP, "");
        // disable truncation
        tmptf.setMaxWidth(0);
        tmptf.setText(s + DvtText.ELLIPSIS);
        // enable truncation
        tmptf.setMaxWidth(maxLineWidth, this._minChars);
        this._bTruncated = true;
      } 
      //Bug 16949028 - AF:OUTPUTTEXT INSIDE DIGRAMNODES CANNOT DISPLAY CORRECTLY
      if (truncateNonWrappedString)
        truncateNonWrappedString = false;
      else
        maxOut = true;
    }

    // keep dimension for each line
    dim = tmptf.getDimensions();

    // a word is wider then the max width allowed for the line
    if (dim.w > this._maxw) {
      tmptf._TruncateText();
      dim = tmptf.getDimensions();
    }

    newTexts.push(tmptf._getText());
    dimList.push(new DvtRectangle(dim.x, dim.y, dim.w, dim.h));

    max_hh = Math.max(dim.h, max_hh);
    max_ww = Math.max(dim.w, max_ww);

    totalHeight += dim.h;

    if (maxOut)
      break;
  }

  //TODO: call API
  //peformance: need in DvtSvgTextArea
  this._maxh = max_hh;
  this._maxw = max_ww;


  // remove the temporary text field
  this._RemoveTempTextField();

  //replace the original text with the wrapping text
  this._setTextWithSize(newTexts, (dimList.length === 0)? undefined : dimList);
  this._numLines = newTexts.length;
};


/**
 * @private
 * Check whether the text being shown needs to be wrapped,
 * and wrap it if so.  
 * return remainder if wrap
 */
DvtTextArea._wrapLine = function(maxLineWidth, textField) {
  var lineText = textField._getText();
  var newLineText;
  var remainder;
  var ww;

  //test wrapping word by word to find where
  //we need to wrap to make the string fit in the allowed width; 
  var wordOffsets = DvtTextArea._breakWords(lineText);
  if (wordOffsets.length > 1) {
    var i;
    for (i = 0; i < wordOffsets.length - 1; i++) {
      //add one word to the line
      if (i < wordOffsets.length) {
        newLineText = lineText.substring(0, wordOffsets[i + 1].index);
      }
      else {
        newLineText = lineText.substring(0, wordOffsets[i].length);
      }
      //replace the original text with the truncated text
      textField._setText(newLineText);

      //re-apply TextFormat after modifying text 
      //check whether the newly truncated text fits
      ww = textField.getDimensions().w;
      if (ww > maxLineWidth)
        break;
    }
    //after the above loop 'i' will be the index of the
    //first word in the remainder of the line, so the 
    //last word that fits in the line is i-1
    if (i > 0) {
      var s = lineText.substring(0, wordOffsets[i].index);
      // trim off trailing whitespace so that we can properly do left or right alignment
      s = s.replace(DvtTextArea.TRIM_TRAILING_REGEXP, "");
      textField._setText(s);
    }

    remainder = lineText.substring(wordOffsets[i].index); 
  }
  else {
    // BUGFIX 12724589 and 12780962
    // we fall into this case if the original string was one long word.
    // in this case, we couldn't wrap the line, and should return the
    // original line of text as the remainder
    remainder = lineText;
  }

  return remainder;
};

/**
 * @private
 */
DvtTextArea._breakWords = function(text) {
  if (DvtTextArea._word_regexp === undefined) {
    DvtTextArea._word_regexp = new RegExp("\\S+", "g");
  }
  var wordOffsets = [];
  var result;
  while ((result = DvtTextArea._word_regexp.exec(text)) !== null) {
    wordOffsets.push({index: result.index,
                      length: result[0].length
                     });
  }
  return wordOffsets;
};


/**
 * Get the maximum text height
 * @type {number} maximum height
 */
DvtTextArea.prototype._getMaxTextHeight = function() {
  return this._maxh;
}

/**
 * Get the maximum text width
 * @type {number} maximum width
 */
DvtTextArea.prototype._getMaxTextWidth = function() {
  return this._maxw;
}

/**
 * @override
 */
DvtTextArea.prototype.isTruncated = function() {
    // Since truncation can happen in DvtTextArea._wrapText() and DvtText._TruncateText() need to check both
    return this._bTruncated || DvtTextArea.superclass.isTruncated.call(this);
}

/**
 * @override
 */
DvtTextArea.prototype.copyShape = function()
{
  var copy = new DvtTextArea(this.getCtx(), this.getX(), this.getY());
  copy.setText(this.getText());
  return copy;
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*   DvtTextFormatted                   TextFormatted "shape"              */
/*-------------------------------------------------------------------------*/
/**
 * Creates an instance of DvtTextFormatted.
 * @extends  DvtShape
 * @class DvtTextFormatted
 * @constructor
 * @param {DvtContext} context   The platform dependent context object (such
 *                               as {@link DvtSvgContext}).
 * @param x {number} x the x position
 * @param y {number} y the y position
 * @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
 */
var DvtTextFormatted = function(context, x, y, id) {
  this.Init(context, x, y, id) ;
};

DvtObj.createSubclass(DvtTextFormatted, DvtText, "DvtTextFormatted") ;



/**
 * Initializes this instance of DvtTextFormatted.
 */
DvtTextFormatted.prototype.Init = function(context, x, y, id) {

  if (! this.getImpl()) {
    this.setImpl(context.getImplFactory().newTextFormatted(x, y, id)) ;
  }
  DvtTextFormatted.superclass.Init.call(this, context);

  this._bHtmlText = true;

};


/**
 * Returns the textFormatted string for this textFormatted object.
 * @return {string} the textFormatted string
 */
DvtTextFormatted.prototype.getText = function() {
  return this._impl.getText();
};

/**
 * Sets the textFormatted string for this textFormatted object.
 * @param {string} textFormattedStr the textFormatted string
 */
DvtTextFormatted.prototype.setText = function(text) {
  this._impl.setText(text);

};

/**
 * @override
 */
DvtTextFormatted.prototype.GetDimsForCaching = function() {
  var dim = this.getDimensions();
  //make sure that dimensions at least start at specified x so that 
  //horizontal indentation for lists is taken into account
  var xx = this.getX();
  if (dim.x > xx) {
    dim.w = dim.w + (dim.x - xx);
    dim.x = xx;
  }
  return dim;
};

/**
 * @override
 */
DvtTextFormatted.prototype.copyShape = function()
{
  var copy = new DvtTextFormatted(this.getCtx(), this.getX(), this.getY());
  copy.setText(this.getText());
  return copy;
}
/**
 * Style Utilities 
 * @class DvtStyleUtils 
 * @constructor
 */
var DvtStyleUtils = function() {
}

DvtObj.createSubclass(DvtStyleUtils, DvtObj, "DvtStyleUtils");

DvtStyleUtils.DEFAULT_FONT_FAMILY = "Tahoma,Verdana,Helvetica,sans-serif";
DvtStyleUtils.DEFAULT_FONT_SIZE = "11px";
DvtStyleUtils.DEFAULT_FONT_COLOR = "black";

DvtStyleUtils.getStyle = function(styleMap, styleName, defaultStyle) {
  if (styleMap && styleMap[styleName] !== null && typeof styleMap[styleName] !== "undefined")
    return styleMap[styleName];
  else
    return defaultStyle;
}

DvtStyleUtils.setStyle = function(styleMap, styleName, styleValue) {
  if (styleMap && styleValue !== null && typeof styleValue !== "undefined" )
    styleMap[styleName] = styleValue;
}

/**
 * Creates a DvtShadow object from a CSS box-shadow specification
 * 
 * @param {string} shadowStyle the CSS box-shadow specification
 * @return {DvtShadow} the DvtShadow object
 */
DvtStyleUtils.getShadow = function(shadowStyle) {
  var shadow = null;
  if (shadowStyle != null && shadowStyle != "none") {
    var shadowRGBA;
    var shadowH = 0;
    var shadowV = 0;
    var shadowBlur = 0;
    
    var arr = shadowStyle.replace(/, /g,",").split(" ");
  
    if (shadowStyle.match(/rgb/i) == null)
      arr[arr.length] = "rgba(0, 0, 0, 0.2)";
        
    if (arr.length < 3)
        return null;
      
    for (i=0;i<arr.length;i++){
      if (i == 0)
        shadowH = parseInt(arr[0]);
      else if (i == 1)
        shadowV = parseInt(arr[1]);
      else if (i == 2 && arr[i].match(/rgb/i) == null)
        shadowBlur = parseInt(arr[i]);
      else if (arr[i].match(/rgb/i))
        shadowRGBA = arr[i];
    }

    var shadowDistance = Math.sqrt(shadowH*shadowH + shadowV*shadowV);
    var angle = Math.atan2(shadowV, shadowH) * 180/Math.PI;
      
    shadow = new DvtShadow(shadowRGBA, 
                          shadowDistance, // distance
                          shadowBlur, // blurX
                          shadowBlur, // blurY
                          angle, // angle of the shadow
                          1, // strength or the imprint/spread
                          3, // quality
                          false, // inner shadow
                          false, // knockout effect
                          false // hide object
                          );
  }
  
  return shadow;  
}
/**
 * @constructor
 * DvtCSSGradient
 */
var DvtCSSGradient = function(type) {
  this.Init(type);
};

DvtObj.createSubclass(DvtCSSGradient, DvtObj, "DvtCSSGradient");


DvtCSSGradient.LINEAR = 0;
DvtCSSGradient.RADIAL = 1;


DvtCSSGradient.prototype.Init = function(type) {
  this._type = type;
}

/**
  *   Returns DvtCSSGradient.LINEAR or DvtCSSGradient.RADIAL
  */
DvtCSSGradient.prototype.getGradientType = function()
{
  return this._type;
};

/**
  *   Returns an array of alpha's.
  *   @type Array
  */
DvtCSSGradient.prototype.getAlphas = function()
{
  return this._arAlphas;
};

/**
  *  Sets the alphas of the gradient.
  *  @param {Array} arAlphas  The array of alphas
  */
DvtCSSGradient.prototype.setAlphas = function(arAlphas)
{
   this._arAlphas = arAlphas;
};

/**
  *   Returns an array of colors.
  *   @type Array
  */
DvtCSSGradient.prototype.getColors = function()
{
  return this._arColors;
};

/**
  *  Sets the colors of the gradient.
  *  @param {Array} arColors  The array of colors
  */
DvtCSSGradient.prototype.setColors = function(arColors)
{
   this._arColors = arColors;
};


/**
  *   Returns an array of stop ratios.
  *   @type Array
  */
DvtCSSGradient.prototype.getRatios = function()
{
  return this._arRatios;
};

/**
  *  Sets the ratios of the gradient.
  *  @param {Array} arRatios  The array of ratios
  */
DvtCSSGradient.prototype.setRatios = function(arRatios)
{
   this._arRatios = arRatios;
};


/**
 * @constructor
 * DvtLinearGradient
 */
var DvtLinearGradient = function() {
  this.Init();
};

/*
 * make DvtCSSGradient a subclass of DvtLinearGradient
 */
DvtObj.createSubclass(DvtLinearGradient, DvtCSSGradient, "DvtLinearGradient");

//convert sides into an angle (in radian)
//Note: in toolkit, angles are in degree (not radian)
DvtLinearGradient.LEFT = 0;   //0;
DvtLinearGradient.BOTTOM_LEFT= 45;
DvtLinearGradient.BOTTOM = 90; //Math.PI / 2;
DvtLinearGradient.BOTTOM_RIGHT = 135;
DvtLinearGradient.RIGHT = 180;    //Math.PI;
DvtLinearGradient.TOP_RIGHT = 225;
DvtLinearGradient.TOP = 270;     //3 * Math.PI / 2;
DvtLinearGradient.TOP_LEFT = 315;

DvtLinearGradient.prototype.Init = function() {
  DvtLinearGradient.superclass.Init.call(this, DvtCSSGradient.LINEAR);
}

/*---------------------------------------------------------------------*/
/*   get/setAngle()                                                    */
/*---------------------------------------------------------------------*/
/**
  *  Returns the angle of the gradient.
  *  @returns {number} the angle of the gradient.
  */
DvtLinearGradient.prototype.getAngle = function()
{
  // return angle
  // usage:
  // angle = Math.atan2(hh, ww)
  // var fill_matrix:Matrix = new Matrix();
  // fill_matrix.createGradientBox(ww, hh, angle, xx, yy);

  return this._angle;
};

/**
  *  Sets the angle of the gradient.
  *  @param {number} angle  The angle of the gradient.
  */
DvtLinearGradient.prototype.setAngle = function(angle)
{
  this._angle = angle;
};

/**
 * parse gradient string into gradient object
 * @class DvtGradientParser
 * @constructor
 */
var DvtGradientParser = function() {
};

DvtObj.createSubclass(DvtGradientParser, DvtObj, "DvtGradientParser");


/*ADF Faces FusionFx-v3-desktop linear gradient syntax */
/*using CSS3 gradients for button background-image */

/*********************************************************************
@agent webkit {
  .AFButtonBackground:alias {
    background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#FFFFFF), to(#CCD6E8));
  }
}

@agent webkit and (min-version: 534.16) {
  .AFButtonBackground:alias {
    background-image: -webkit-linear-gradient(top, #FFFFFF, #CCD6E8);
  }
}

@agent gecko {
  .AFButtonBackground:alias {
    background-image: -moz-linear-gradient(top, #FFFFFF, #CCD6E8);
  }
}

@agent ie and (max-version: 7.0) {
  .AFButtonBackground:alias {
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFF', endColorstr='#CCD6E8');
  }
}

@agent ie and (min-version: 8.0) {
  .AFButtonBackground:alias {
    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFF', endColorstr='#CCD6E8')";
  }
}
*********************************************************************/

/* End of CSS3 gradients for buttom background-image*/


DvtGradientParser.WEBKIT_GD_LINEAR = "-webkit-linear-gradient";
DvtGradientParser.FIREFOX_GD_LINEAR = "-moz-linear-gradient";
DvtGradientParser.IE_GD_LINEAR = "progid:DXImageTransform.Microsoft.gradient";
DvtGradientParser.IE_GD_LINEAR2 = "progid:DXImageTransform.Microsoft.Gradient";
DvtGradientParser.IE7_GD_FILTER = "filter";
DvtGradientParser.IE8_GD_FILTER = "-ms-filter";
DvtGradientParser.IE10_GD_LINEAR = "-ms-linear-gradient";

DvtGradientParser.IE_GD_START_COLOR_STR = "startColorstr";
DvtGradientParser.IE_GD_END_COLOR_STR = "endColorstr";
DvtGradientParser.IE_GD_GRADIENT_TYPE = "GradientType";


//TODO: should we support old webkit gradient syntax???
DvtGradientParser.OLD_WEBKIT_GD_LINEAR = "-webkit-gradient"; 

//W3C CSS3 gradient
DvtGradientParser.GD_LINEAR = "linear-gradient";
DvtGradientParser.GD_RADIAL = "radial-gradient";


DvtGradientParser.GD_TOP = "top";
DvtGradientParser.GD_BOTTOM = "bottom";
DvtGradientParser.GD_LEFT = "left";
DvtGradientParser.GD_RIGHT = "right";
DvtGradientParser.GD_CENTER = "center";
DvtGradientParser.GD_TO = "to";


/**
 * Parse a gradient style String and turn it into a DvtCSSStyle.
 * @param {String} gradient string
 * @return a CSSGradient object if it is a gradient value,
 *         a null otherwise. 
 */
DvtGradientParser.parseCSSGradient = function (gradient) {

  if (gradient != null) {
    gradient = DvtStringUtils.trim(gradient);

    if (DvtGradientParser._startsWith(gradient, DvtGradientParser.WEBKIT_GD_LINEAR)) {
      gradient = DvtGradientParser._removeParenthesis(gradient, DvtGradientParser.WEBKIT_GD_LINEAR);
      return DvtGradientParser._parseLinearGradient(gradient);
    }
    else if (DvtGradientParser._startsWith(gradient, DvtGradientParser.FIREFOX_GD_LINEAR)) {
      gradient = DvtGradientParser._removeParenthesis(gradient, DvtGradientParser.FIREFOX_GD_LINEAR);
      return DvtGradientParser._parseLinearGradient(gradient);
    }
    else if (DvtGradientParser._startsWith(gradient, DvtGradientParser.IE_GD_LINEAR)) {
      gradient = DvtGradientParser._removeParenthesis(gradient, DvtGradientParser.IE_GD_LINEAR);
      return DvtGradientParser._parseIELinearGradient(gradient);
    }
    else if (DvtGradientParser._startsWith(gradient, DvtGradientParser.IE_GD_LINEAR2)) {
      gradient = DvtGradientParser._removeParenthesis(gradient, DvtGradientParser.IE_GD_LINEAR2);
      return DvtGradientParser._parseIELinearGradient(gradient);
    }
    else if (DvtGradientParser._startsWith(gradient, DvtGradientParser.IE10_GD_LINEAR)) {
      gradient = DvtGradientParser._removeParenthesis(gradient, DvtGradientParser.IE10_GD_LINEAR);
      return DvtGradientParser._parseLinearGradient(gradient);
    }
    //W3C CSS3 gradient (Draft 13 Dec 2011)
    else if (DvtGradientParser._startsWith(gradient, DvtGradientParser.GD_LINEAR)) {
      gradient = DvtGradientParser._removeParenthesis(gradient, DvtGradientParser.GD_LINEAR);
      return DvtGradientParser._parseLinearGradient(gradient);
    }
  }
  return null;
}


  /****************************************************************
   CSS3 - http://dev.w3.org/csswg/css3-images/#gradients

   <linear-gradient> = linear-gradient(
      [ [ <angle> | to <side-or-corner> ] ,]? 
      <color-stop>[, <color-stop>]+
   )

   <side-or-corner> = [left | right] || [top | bottom]

/(([0-9]+)(deg|rad|grad)|\s*(to\s+)?((left|right)\s+)?(top|bottom)?\s*,\s*)?/i

/(#[0-9a-f]+|rgb\(\s*(\d+),\s*(\d+),\s*(\d+)\s*\)|rgba\(\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d?)(\.\d+)?\s*\)|[a-z]+)(\s+\d+%)?/gi


  ******************************************************************/

DvtGradientParser._parseLinearGradient = function (gradient) {
  var number = new RegExp("(-?(?:\\d*\\.)?\\d+)");
  var angleUnit = new RegExp("(deg|rad|grad|turn)")
  var horiz = new RegExp("(left|right)");
  var vert = new RegExp("(top|bottom)");
  var to = new RegExp("(?:(to)\\s+)");
  var angle = new RegExp("(?:" + number.source + angleUnit.source + ")");
  var horizThenVert = new RegExp("(?:" + horiz.source + "(?:\\s+" + vert.source + ")?)");
  var vertThenHoriz = new RegExp("(?:" + vert.source + "(?:\\s+" + horiz.source + ")?)");
  var sideOrCorner = new RegExp("(?:" + horizThenVert.source + "|" + vertThenHoriz.source + ")");
  var direction = new RegExp("(?:" + angle.source + "|(?:" + to.source + "?" + sideOrCorner.source + "))");
  var everythingBeforeColorStops = new RegExp("(?:\\s*" + direction.source + "\\s*,\\s*)", "i");
  var sides = gradient.match(everythingBeforeColorStops);

  if (sides == null || sides.length == 0)
    return null;

  // sides
  // 0: match string
  // 1: angle
  // 2: angle unit
  // 3: to (if present)
  // 4: horiz (if horizThenVert)
  // 5: vert (if horizThenVert)
  // 6: vert (if vertThenHoriz)
  // 7: horiz (if vertThenHoriz)

  var size = sides.length;
  var gradObj = new DvtLinearGradient();

  var bTop = sides[5] == DvtGradientParser.GD_TOP || sides[6] == DvtGradientParser.GD_TOP;
  var bBottom = sides[5] == DvtGradientParser.GD_BOTTOM || sides[6] == DvtGradientParser.GD_BOTTOM;
  var bLeft = sides[4] == DvtGradientParser.GD_LEFT || sides[7] == DvtGradientParser.GD_LEFT;
  var bRight = sides[4] == DvtGradientParser.GD_RIGHT || sides[7] == DvtGradientParser.GD_RIGHT;
  var bTo = sides[3] == DvtGradientParser.GD_TO;
  if (bTo) {
    // specifying end rather than start, swap top/bottom, left/right if specified
    if (bTop || bBottom) {
      bTop = !bTop;
      bBottom = !bBottom;
    }
    
    if (bLeft || bRight) {
      bLeft = !bLeft;
      bRight = !bRight;
    }
  }

  // top left | top right | top
  if (bTop) {
    if (bLeft) {
      gradObj.setAngle(DvtLinearGradient.TOP_LEFT);      
    }
    else if (bRight) {
      gradObj.setAngle(DvtLinearGradient.TOP_RIGHT);      
    }
    else {
      gradObj.setAngle(DvtLinearGradient.TOP);
    }
  }
  // bottom left | bottom right | bottom
  else if (bBottom) {
    if (bLeft) {
      gradObj.setAngle(DvtLinearGradient.BOTTOM_LEFT);
    }
    else if (bRight) {
      gradObj.setAngle(DvtLinearGradient.BOTTOM_RIGHT);
    }
    else {
      gradObj.setAngle(DvtLinearGradient.BOTTOM);
    }
  }
  else if (bLeft) {
    gradObj.setAngle(DvtLinearGradient.LEFT);
  }
  else if (bRight) {
    gradObj.setAngle(DvtLinearGradient.RIGHT);
  }
  // parse angle if specified
  else if (sides[2]) {
    var angle = parseFloat(sides[1]);

    if (! isNaN(angle)) {
      if (sides[2] == "grad") {
        angle = (angle/200) * 180;
      }
      else if (sides[2] == "rad") {
        angle = (angle/Math.PI) * 180;
      }
      else if (sides[2] == "turn") {
        angle = (angle/.5) * 180;
      }
      //TODO: dont need to convert negative angle to positive
      //because beginGradientFill take negative angle
      //      angle = angle % (2 * Math.PI);
      //      if (angle < 0) {
      //        angle += 2 * Math.PI;
      //      }

      gradObj.setAngle(angle);
    }
  }

  // parse colorStops
  gradient = gradient.substring(sides[0].length);    
  DvtGradientParser._parseCSSColorStops(gradient, gradObj);

  return gradObj;
}


DvtGradientParser._parseIELinearGradient = function (gradient) {
  var props = gradient.split(",");
  var size = props.length;
  var prop;
  var colorStops = [];

  //In IE GradientType = 0 (default, vertical)
  //      GradientType = 1 (horizontal)
  var gradObj = new DvtLinearGradient();
  gradObj.setAngle(DvtLinearGradient.TOP);

  // parse gradient properties
  for (var i = 0; i < size; i++) {
    prop = (DvtStringUtils.trim(props[i])).split("=");
    if (prop.length == 2) {
      prop[0] = DvtStringUtils.trim(prop[0]);
      if (prop[0] == DvtGradientParser.IE_GD_START_COLOR_STR) {
        colorStops[0] = DvtGradientParser.removeQuotes(prop[1], DvtCSSStyle.SINGLE_QUOTE);
      }
      else if (prop[0] == DvtGradientParser.IE_GD_END_COLOR_STR) {
        colorStops[1] = DvtGradientParser.removeQuotes(prop[1], DvtCSSStyle.SINGLE_QUOTE);
      }
      else if (prop[0] == DvtGradientParser.IE_GD_GRADIENT_TYPE) {
        if (prop[1] == "1") {
          gradObj.setAngle(DvtLinearGradient.LEFT);
        }
      }
    }
  }

  // parse color stops
  DvtGradientParser._parseColorStops(colorStops, gradObj);

  return gradObj;
}


DvtGradientParser._parseCSSColorStops = function (s, gradObj) {

  var colorStops = s.match(/(#[0-9a-f]+|rgb\(\s*(\d+),\s*(\d+),\s*(\d+)\s*\)|rgba\(\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d?)(\.\d+)?\s*\)|[a-z]+)(\s+\d+%)?/gi);

  if (colorStops != null && colorStops.length > 0) {
    DvtGradientParser._parseColorStops(colorStops, gradObj);
  }
}


DvtGradientParser._parseColorStops = function (colorStops, gradObj) {

  var size = colorStops.length;
  var alphas = [];
  var colors = [];
  var ratios = [];
  var alphaColor;
  var position;
  var noRatio = true;
  var sPosition;
  var colorArray = [];

  for (var i = 0; i < colorStops.length; i++) {
    sPosition = DvtCSSStyle.parseBorderColor(colorStops[i], colorArray);
    alphaColor = colorArray.pop();

    colors[i] = alphaColor;
    alphas[i] = DvtColorUtils.getAlpha(alphaColor);;

    sPosition = DvtStringUtils.trim(sPosition);
    if (sPosition.length > 0) {
      position = parseFloat(sPosition);
      if (! isNaN(position)){
        //NOTE: support % but not length or ratio.
        if (DvtStringUtils.endsWith(sPosition, "%")) {
          ratios[i] = position / 100;
          noRatio = false;
        }
      }
    }
  }

  // no location is specified, even space is assumed
  if (size > 1 && noRatio) {
    for (var r=0; r < size; r++) {
      ratios[r] = r / (size - 1);
    }
  }
  else if (noRatio) {
    ratios[0] = 0;
  }

  gradObj.setColors(colors);
  gradObj.setAlphas(alphas);
  gradObj.setRatios(ratios);
}

DvtGradientParser._removeParenthesis = function (gradient, keyWord) {
  if (gradient.charAt(gradient.length - 1) != ")")
    return null;

  // remove keyWord and parethesis around it
  gradient = gradient.substring(keyWord.length);

  return DvtGradientParser.removeQuotes(gradient, "(", ")");
}

DvtGradientParser.removeQuotes = function (colorStr, openQ, closeQ) {
  // remove quote around the color string
  colorStr = DvtStringUtils.trim(colorStr);
  if (colorStr.charAt(0) != openQ)
    return colorStr;

  if (closeQ == null)
    closeQ = openQ;

  var iStart = 1;
  var iEnd = colorStr.lastIndexOf(closeQ);
  if (iEnd < 0)
    return colorStr.substring(iStart);

  return colorStr.substring(iStart, iEnd);
}



DvtGradientParser._startsWith = function (str, value) {
  return str.indexOf(value) == 0;
}

/**
 * @constructor
 * Represents a set of CSS styles.
 * @param {String} style inlineStyle
 */
var DvtCSSStyle = function(style) {
  this.Init(style);
};

DvtObj.createSubclass(DvtCSSStyle, DvtObj, "DvtCSSStyle");


/**
 * @private
 * Only contain css style we support
 */
DvtCSSStyle._attrs = ["background", 
                      "background-color", 
                      "background-image", 
                      "background-repeat", 
                      "background-position", 

                      "border",
                      "border-top",
                      "border-bottom",
                      "border-left",
                      "border-right",

                      "border-color",
                      "border-top-color",
                      "border-bottom-color",
                      "border-left-color",
                      "border-right-color",
                      "border-width",
                      "border-top-width",
                      "border-bottom-width",
                      "border-left-width",
                      "border-right-width",
                      "border-style",
                      "border-top-style",
                      "border-bottom-style",
                      "border-left-style",
                      "border-right-style",

                      "border-radius",
                      "border-top-left-radius",
                      "border-top-right-radius",
                      "border-bottom-right-radius",
                      "border-bottom-left-radius",

                      "box-sizing",

                      "padding",
                      "padding-top",
                      "padding-bottom",
                      "padding-left",
                      "padding-right",

                      "margin",
                      "margin-top",
                      "margin-bottom",
                      "margin-left",
                      "margin-right",

                      "color",
                      "font-family",
                      "font-size",
                      "font-style",
                      "font-weight",

                      "text-align",
                      "text-decoration",

                      "height",
                      "width",
                      
                      "-tr-outer-color",
                      "-tr-inner-color",
                      
                      //HV recognizes these for "gradient" or "solid"
                      "fill-type",
                      "border-type",

                      "content"
                     ];



/*
 * A list of css styles apply to this text object.
 * note: svg does not recognize the "color" attribute, use "fill" instead

DvtCSSStyle._textAttrs = 
[
  "color",
  "font-family",
  "font-size",
  "font-style",
  "font-weight",
  "text-align",
  "text-decoration",
];
 */

// Skin names. Not proper CSS and should move when possible
DvtCSSStyle.SKIN_SKYROS = 'skyros';
DvtCSSStyle.SKIN_ALTA = 'alta';

// CSS attributes supported

// Background Properties
DvtCSSStyle.BACKGROUND = "background";
DvtCSSStyle.BACKGROUND_COLOR = "background-color";
DvtCSSStyle.BACKGROUND_IMAGE = "background-image";
DvtCSSStyle.BACKGROUND_REPEAT = "background-repeat";
DvtCSSStyle.BACKGROUND_POSITION = "background-position";

// Border Properties
DvtCSSStyle.BORDER = "border";
DvtCSSStyle.BORDER_TOP = "border-top";
DvtCSSStyle.BORDER_BOTTOM = "border-bottom";
DvtCSSStyle.BORDER_LEFT = "border-left";
DvtCSSStyle.BORDER_RIGHT = "border-right";

DvtCSSStyle.BORDER_WIDTH = "border-width";
DvtCSSStyle.BORDER_TOP_WIDTH = "border-top-width";
DvtCSSStyle.BORDER_BOTTOM_WIDTH = "border-bottom-width";
DvtCSSStyle.BORDER_LEFT_WIDTH = "border-left-width";
DvtCSSStyle.BORDER_RIGHT_WIDTH = "border-right-width";

DvtCSSStyle.BORDER_COLOR = "border-color";
DvtCSSStyle.BORDER_TOP_COLOR = "border-top-color";
DvtCSSStyle.BORDER_BOTTOM_COLOR = "border-bottom-color";
DvtCSSStyle.BORDER_LEFT_COLOR = "border-left-color";
DvtCSSStyle.BORDER_RIGHT_COLOR = "border-right-color";

//bug 13826956 - border-radius css property not supported when used inside <dvt:node>
DvtCSSStyle.BORDER_RADIUS = "border-radius";
DvtCSSStyle.BORDER_TOP_LEFT_RADIUS = "border-top-left-radius";
DvtCSSStyle.BORDER_TOP_RIGHT_RADIUS = "border-top-right-radius";
DvtCSSStyle.BORDER_BOTTOM_RIGHT_RADIUS = "border-bottom-right-radius";
DvtCSSStyle.BORDER_BOTTOM_LEFT_RADIUS = "border-bottom-left-radius";

// Margin Properties
DvtCSSStyle.MARGIN = "margin";
DvtCSSStyle.MARGIN_TOP = "margin-top";
DvtCSSStyle.MARGIN_BOTTOM = "margin-bottom";
DvtCSSStyle.MARGIN_LEFT = "margin-left";
DvtCSSStyle.MARGIN_RIGHT = "margin-right";

// Padding Properties
DvtCSSStyle.PADDING = "padding";
DvtCSSStyle.PADDING_TOP = "padding-top";
DvtCSSStyle.PADDING_BOTTOM = "padding-bottom";
DvtCSSStyle.PADDING_LEFT = "padding-left";
DvtCSSStyle.PADDING_RIGHT = "padding-right";

// Font Properties
DvtCSSStyle.COLOR = "color";
DvtCSSStyle.FONT_FAMILY = "font-family";
DvtCSSStyle.FONT_SIZE = "font-size";
DvtCSSStyle.FONT_STYLE = "font-style";
DvtCSSStyle.FONT_WEIGHT = "font-weight";
DvtCSSStyle.TEXT_DECORATION = "text-decoration";
DvtCSSStyle.TEXT_ALIGN = "text-align";


//Bug 13842185 - DIFFICULT TO SIZE PGLS WHEN USING PADDING AND BORDERS
//value for box-sizing
DvtCSSStyle.BORDER_BOX = "border-box";
DvtCSSStyle.CONTENT_BOX = "content-box";


// Size Properties
DvtCSSStyle.HEIGHT = "height";
DvtCSSStyle.WIDTH = "width";


//value for background-image
DvtCSSStyle.NONE = "none";

//values for background-repeat
DvtCSSStyle.NO_REPEAT = "no-repeat";
DvtCSSStyle.REPEAT = "repeat";
DvtCSSStyle.REPEAT_X = "repeat-x";
DvtCSSStyle.REPEAT_Y = "repeat-y";
        
//value for margin
DvtCSSStyle.AUTO = "auto";
DvtCSSStyle.AUTO_MARGIN = "8";
        
//HV recognizes these for "gradient" or "solid"
DvtCSSStyle.BORDER_TYPE = "border-type";
DvtCSSStyle.FILL_TYPE = "fill-type";

//used for icon url
DvtCSSStyle.CONTENT = "content";

//For Alta selection effects. Not proper CSS and should move when possible
DvtCSSStyle.INNER_COLOR = "-tr-inner-color";
DvtCSSStyle.OUTER_COLOR = "-tr-outer-color";

// Special values for font size
DvtCSSStyle.SpFontSizeMap = {
  "xx-small" : "9",
  "x-small" : "10",
  "small" : "13",
  "medium" : "16",
  "large" : "18",
  "x-large" : "24",
  "xx-large" : "32"
};


// Special values for width
DvtCSSStyle.SpWidthMap = {
  thin : "2",
  medium : "4",
  thick : "6"
};


// Basic color keywords
DvtCSSStyle.SpColorMap = {
  aqua : "#00FFFF",
  black : "#000000",
  blue : "#0000FF",
  white : "#FFFFFF",
  fuchsia : "#FF00FF",
  gray : "#848284",
  lime : "#00FF00",
  maroon : "#840000",
  green : "#008200",
  navy : "#000084",
  olive : "#848200",
  red : "#FF0000",
  silver : "#C6C3C6",
  teal : "#008284",
  yellow : "#FFFF00",
  purple : "#800080",

  // for testing used
  cyan : "#D2B48C",
  goldenrod : "#DAA520",
  lightblue : "#ADD8E6",
  lightyellow : "#FFFFE0",
  orange : "#FFA500",
  paleGoldenRod : "#EEE8AA",
  paleturquoise : "#AFEEEE",
  peachpuff : "#FFDAB9",
  pink : "#FFC0CB",
  tan : "#D2B48C",
  thistle : "#D8BFD8",

  transparent : "transparent"
};


// numeric attributes List
DvtCSSStyle._numericAttrsList = [
  DvtCSSStyle.WIDTH,
  DvtCSSStyle.HEIGHT,
  DvtCSSStyle.BORDER_WIDTH,
  DvtCSSStyle.BORDER_TOP_WIDTH,
  DvtCSSStyle.BORDER_BOTTOM_WIDTH,
  DvtCSSStyle.BORDER_LEFT_WIDTH,
  DvtCSSStyle.BORDER_RIGHT_WIDTH,

  DvtCSSStyle.MARGIN,
  DvtCSSStyle.MARGIN_TOP,
  DvtCSSStyle.MARGIN_BOTTOM,
  DvtCSSStyle.MARGIN_LEFT,
  DvtCSSStyle.MARGIN_RIGHT,

  DvtCSSStyle.PADDING,
  DvtCSSStyle.PADDING_TOP,
  DvtCSSStyle.PADDING_BOTTOM,
  DvtCSSStyle.PADDING_LEFT,
  DvtCSSStyle.PADDING_RIGHT
];


DvtCSSStyle.DOUBLE_QUOTE = "\"";
DvtCSSStyle.SINGLE_QUOTE = "'";

// Default color ramps
DvtCSSStyle.COLORS_SKYROS = ["#003366", "#CC3300", "#666699", "#006666", "#FF9900", "#993366", 
                             "#99CC33", "#624390", "#669933", "#FFCC33", "#006699", "#EBEA79"];
DvtCSSStyle.COLORS_ALTA = ["#267db3", "#68c182", "#fad55c", "#ed6647", "#8561c8", "#6ddbdb", 
                           "#ffb54d", "#e371b2", "#47bdef", "#a2bf39", "#a75dba", "#f7f37b"];


/**
 * Parse an inline style String and turn it into a DvtCSSStyle.
 * @param {String} style inlineStyle
 */
DvtCSSStyle.prototype.Init = function(style) {
  this.parseInlineStyle(style);
};

/**
 * Parse an inlineStyle string into a set of CSS styles and merge the results to this style object.
 * @param {String} style inlineStyle
 */
DvtCSSStyle.prototype.parseInlineStyle = function(style) {
  if (style && style.length > 0) {
    var a = style.split(";");
    var s;
    var colonIndex;
    var attrName;
    var attrVal;
    for (var i = 0; i < a.length; i++) {
      s = a[i];
      if (s && s.length > 0) {
        //find the first colon instead of using String.split because
        //there may be other colons in the string, for instance in
        //a fully qualified background-image url (http://some.server.com/...)
        colonIndex = s.indexOf(":");
        if (colonIndex > -1) {
          attrName = DvtStringUtils.trim(s.substring(0, colonIndex));
          attrVal = DvtStringUtils.trim(s.substring(colonIndex + 1));
              
          if (attrName && attrName.length > 0 &&
              attrVal && attrVal.length > 0) {

            //inline images with data url
            if (attrName == DvtCSSStyle.BACKGROUND_IMAGE &&
                attrVal.indexOf("data:image/") >= 0) {
              attrVal = attrVal + ";" + a[i + 1];
              i++;
            }

            this.setCSSStyle(attrName, attrVal);
          }
        }
      }
    }
  }
};

/**
 * Set a CSS style. For example: key="font-size" val="11px"
 * @param {String} key css style name
 * @param {String} val css style value
 */
DvtCSSStyle.prototype.setCSSStyle = function(key, val) {

  switch (key) {
  case "background-repeat" : 
    this.setBackgroundRepeat(key, val);
    break;
  case "color" :
  case "background-color" :
  case "border-top-color" :
  case "border-bottom-color" :
  case "border-left-color" :
  case "border-right-color" : 
    this.setColorString(key, val);
    break;

  case "border-top" :
  case "border-bottom" :
  case "border-left" :
  case "border-right" :
    this.setBorderSide(key, val);
    break;

  case "border" : 
    this.setBorderShorthand(key, val);
    break;
  case "border-color" :
    this.setBorderColorShorthand(key, val);
    break;
  case "border-width" : 
    this.setBorderWidthShorthand(key, val);
    break;

  //bug 13826956 - border-radius css property not supported when used inside <dvt:node>
  case "border-radius" : 
    this.setBorderRadius(key, val);
    break;

  case "padding" :
    this.setPaddingShorthand(key, val);
    break;
  case "margin" :
    this.setMarginShorthand(key, val);
    break;
  case "font-family" :
    this._setFontFamily(key, val);
    break;
  case "font-size" :
    this.setFontSize(key, val);
    break;

  //Bug 13365200 - hv command button loses gradient
  //TODO: allow gradient syntax on BACKGROUND_COLOR???
  case "background-image" :
    var gradObj = DvtGradientParser.parseCSSGradient(val);
    if (gradObj) {
      val = gradObj;
    }
    this.setStyle(key, val);
    break;

  //IE gradient syntax
  case DvtGradientParser.IE7_GD_FILTER:
  case DvtGradientParser.IE8_GD_FILTER:
    //remove quotes around the value
    var val1 = DvtGradientParser.removeQuotes(val, DvtCSSStyle.DOUBLE_QUOTE);

    var gradObj = DvtGradientParser.parseCSSGradient(val1);
    if (gradObj) {
      key = DvtCSSStyle.BACKGROUND_IMAGE;
      val = gradObj;
    }

    this.setStyle(key, val);
    break;

  default:
    this.setStyle(key, val);
    break;
  }

};


DvtCSSStyle.prototype._setFontFamily = function(key, val) {
    this[key] = val;
};

/**
 * Sets the font-size on a DvtCSSStyle object
 * @param {String} key The css key to set the font-size under
 * @param {String} val The font-size which can be in the format '9', '9px', or 'xx-small'
 */
DvtCSSStyle.prototype.setFontSize = function(key, val) {
  var fsize = DvtStringUtils.trim(val);
  var specSize = DvtCSSStyle.SpFontSizeMap[fsize];
  if (specSize) {
    this[key] = (String(specSize) + "px");
  }
  else {
    this[key] = isNaN(parseFloat(fsize)) ? DvtStyleUtils.DEFAULT_FONT_SIZE : fsize;
  }
};


/**
 * @param key is a shorthand for the four border properties: top, right, bottom and left border, respectively. 
 * @param val may contain up to 4 border-width values. 
 */
DvtCSSStyle.prototype.setBorderWidthShorthand = function(key, val) {
  var bwArray = val.split(" ");
  var bWidth = null;

  switch (bwArray.length) {
  case 1:
    this[key] = DvtCSSStyle._getBorderWidth(bwArray[0]);
    // remove other border widths
    delete this[DvtCSSStyle.BORDER_TOP_WIDTH];
    delete this[DvtCSSStyle.BORDER_BOTTOM_WIDTH];
    delete this[DvtCSSStyle.BORDER_RIGHT_WIDTH];
    delete this[DvtCSSStyle.BORDER_LEFT_WIDTH];
    break;

  case 2:
    bWidth = DvtCSSStyle._getBorderWidth(bwArray[0]);
    this.setStyle(DvtCSSStyle.BORDER_TOP_WIDTH, bWidth);
    this.setStyle(DvtCSSStyle.BORDER_BOTTOM_WIDTH, bWidth);

    bWidth = DvtCSSStyle._getBorderWidth(bwArray[1]);
    this.setStyle(DvtCSSStyle.BORDER_LEFT_WIDTH, bWidth);
    this.setStyle(DvtCSSStyle.BORDER_RIGHT_WIDTH, bWidth);
    break;

  case 3:
    this.setStyle(DvtCSSStyle.BORDER_TOP_WIDTH, DvtCSSStyle._getBorderWidth(bwArray[0]));
    bWidth = DvtCSSStyle._getBorderWidth(bwArray[1]);
    this.setStyle(DvtCSSStyle.BORDER_LEFT_WIDTH, bWidth);
    this.setStyle(DvtCSSStyle.BORDER_RIGHT_WIDTH, bWidth);
    this.setStyle(DvtCSSStyle.BORDER_BOTTOM_WIDTH, DvtCSSStyle._getBorderWidth(bwArray[2]));
    break;

  case 4:
    this.setStyle(DvtCSSStyle.BORDER_TOP_WIDTH, DvtCSSStyle._getBorderWidth(bwArray[0]));
    this.setStyle(DvtCSSStyle.BORDER_RIGHT_WIDTH, DvtCSSStyle._getBorderWidth(bwArray[1]));
    this.setStyle(DvtCSSStyle.BORDER_BOTTOM_WIDTH, DvtCSSStyle._getBorderWidth(bwArray[2]));
    this.setStyle(DvtCSSStyle.BORDER_LEFT_WIDTH, DvtCSSStyle._getBorderWidth(bwArray[3]));
    break;

  case 0:
  default:
    break;
  }
};


/**
 * @private
 * #param val contain only one borderWidth value
 */
DvtCSSStyle._getBorderWidth = function(val) {

  var bwidth = DvtStringUtils.trim(val);
  var specSize = DvtCSSStyle.SpWidthMap[bwidth];
  if (! specSize) {
    specSize = isNaN(parseFloat(bwidth)) ? "0px" : bwidth;
  }

  return specSize;
};


// border-top, border-right, border-bottom and border-left shorthand property 
// can specify width, style, and color in any order and separated by spaces
// ex: border-bottom:#797975 1px solid
//ex: border-top:1px solid rgb(185, 185, 180)

DvtCSSStyle.prototype.setBorderSide = function(key, val) {
  var borderArray = val.split(" ");
  var borderVal = null;
  var noBorder = false;
  var key1;

  for (var i = 0; i < borderArray.length; i++) {
    borderVal = borderArray[i];
    key1 = key;

    //rgb border color
    if (borderVal.indexOf("rgb") == 0) {
      for (var k = i + 1; k < borderArray.length; k++) {
        borderVal += borderArray[k];
        i++;
        if (borderArray[k].indexOf(")") != -1)
          break;
      }
      if (! DvtStringUtils.endsWith(key, "-" + DvtCSSStyle.COLOR))
        key1 = key + "-" + DvtCSSStyle.COLOR;
      this.setStyle(key1, borderVal);
    }
    //border color
    else if (DvtCSSStyle.isColorVal(borderVal)) {
      if (! DvtStringUtils.endsWith(key, "-" + DvtCSSStyle.COLOR))
        key1 = key + "-" + DvtCSSStyle.COLOR;
      this.setStyle(key1, DvtCSSStyle.parseHexColor(borderVal));
    }
    //border width
    else if (DvtCSSStyle.isBorderWidthVal(borderVal)) {
      if (! DvtStringUtils.endsWith(key, "-" + DvtCSSStyle.WIDTH))
        key1 = key + "-" + DvtCSSStyle.WIDTH;
      this.setStyle(key1, borderVal);
    }
    //we don't currently handle border style, but if it's none, set width=0
    else if (val == "none")
      noBorder = true;
  }

  if (noBorder) {
    this.setStyle(key + "-" + DvtCSSStyle.WIDTH, "0");
  }
};


// get margin-top, margin-right, margin-bottom and margin-left
DvtCSSStyle.prototype.getMarginTop = function() {
  return this._getSideWidth(DvtCSSStyle.MARGIN_TOP, DvtCSSStyle.MARGIN);
};

DvtCSSStyle.prototype.getMarginRight = function() {
  return this._getSideWidth(DvtCSSStyle.MARGIN_RIGHT, DvtCSSStyle.MARGIN);
};

DvtCSSStyle.prototype.getMarginBottom = function() {
  return this._getSideWidth(DvtCSSStyle.MARGIN_BOTTOM, DvtCSSStyle.MARGIN);
};

DvtCSSStyle.prototype.getMarginLeft = function() {
  return this._getSideWidth(DvtCSSStyle.MARGIN_LEFT, DvtCSSStyle.MARGIN);
};

// get border-top-width, border-right-width, border-bottom-width and border-left-width
DvtCSSStyle.prototype.getBorderTopWidth = function() {
  return this._getSideWidth(DvtCSSStyle.BORDER_TOP_WIDTH, DvtCSSStyle.BORDER_WIDTH);
};

DvtCSSStyle.prototype.getBorderRightWidth = function() {
  return this._getSideWidth(DvtCSSStyle.BORDER_RIGHT_WIDTH, DvtCSSStyle.BORDER_WIDTH);
};

DvtCSSStyle.prototype.getBorderBottomWidth = function() {
  return this._getSideWidth(DvtCSSStyle.BORDER_BOTTOM_WIDTH, DvtCSSStyle.BORDER_WIDTH);
};

DvtCSSStyle.prototype.getBorderLeftWidth = function() {
  return this._getSideWidth(DvtCSSStyle.BORDER_LEFT_WIDTH, DvtCSSStyle.BORDER_WIDTH);
};

DvtCSSStyle.prototype.getBorderWidth = function() {
  var bwidth = this.getStyle(DvtCSSStyle.BORDER_WIDTH);
  if (bwidth) {
    return DvtCSSStyle.toNumber(bwidth);
  }
  bwidth = this.getStyle(DvtCSSStyle.BORDER_TOP_WIDTH);
  if (bwidth) {
    return DvtCSSStyle.toNumber(bwidth);
  }
  bwidth = this.getStyle(DvtCSSStyle.BORDER_RIGHT_WIDTH);
  if (bwidth) {
    return DvtCSSStyle.toNumber(bwidth);
  }
  bwidth = this.getStyle(DvtCSSStyle.BORDER_BOTTOM_WIDTH);
  if (bwidth) {
    return DvtCSSStyle.toNumber(bwidth);
  }
  bwidth = this.getStyle(DvtCSSStyle.BORDER_LEFT_WIDTH);
  if (bwidth) {
    return DvtCSSStyle.toNumber(bwidth);
  }
  return 0;
};

// get padding-top, padding-right, padding-bottom and padding-left
DvtCSSStyle.prototype.getPaddingTop = function() {
  return this._getSideWidth(DvtCSSStyle.PADDING_TOP, DvtCSSStyle.PADDING);
};

DvtCSSStyle.prototype.getPaddingRight = function() {
  return this._getSideWidth(DvtCSSStyle.PADDING_RIGHT, DvtCSSStyle.PADDING);
};

DvtCSSStyle.prototype.getPaddingBottom = function() {
  return this._getSideWidth(DvtCSSStyle.PADDING_BOTTOM, DvtCSSStyle.PADDING);
};

DvtCSSStyle.prototype.getPaddingLeft = function() {
  return this._getSideWidth(DvtCSSStyle.PADDING_LEFT, DvtCSSStyle.PADDING);
};

// width and height
DvtCSSStyle.prototype.getWidth = function() {
  return this.width;
};

DvtCSSStyle.prototype.getHeight = function() {
  return this.height;
};


/**
 * Convert a string to a number
 * 
 * @return a number or zero if not a number or value is not specified
 */
DvtCSSStyle.toNumber = function(val) {
  if (val) {
    val = parseFloat(val);
    return isNaN(val)? 0 : val;
  }
  return 0;
};

/**
 * Determine if the given style can be inherited.
 * 
 * @return true if the style can be inherited from an ancestor,
 *         false if not
 */
DvtCSSStyle.isInheritable = function(key) {
  switch (key) {
  case DvtCSSStyle.COLOR:
  case DvtCSSStyle.FONT_FAMILY:
  case DvtCSSStyle.FONT_SIZE:
  case DvtCSSStyle.FONT_STYLE:
  case DvtCSSStyle.FONT_WEIGHT:
  case DvtCSSStyle.TEXT_ALIGN:
    return true;

  default:
    return false;
  }
};


// val is a string contains up to 4 rgb colors, ex:
// border-color: rgb(221, 221, 216) rgb(185, 185, 132) rgb(121, 121, 117)
DvtCSSStyle.prototype.setBorderColorShorthand = function(key, val) {
  var bcArray = DvtCSSStyle.parseBorderColorString(key, val);
  var bcolor;

  switch (bcArray.length) {
  case 1:
    this.setColor(key, bcArray[0]);
    delete this[DvtCSSStyle.BORDER_TOP_COLOR];
    delete this[DvtCSSStyle.BORDER_BOTTOM_COLOR];
    delete this[DvtCSSStyle.BORDER_RIGHT_COLOR];
    delete this[DvtCSSStyle.BORDER_LEFT_COLOR];
    break;

  case 2:
    bcolor = bcArray[0];
    this.setColor(DvtCSSStyle.BORDER_TOP_COLOR, bcolor);
    this.setColor(DvtCSSStyle.BORDER_BOTTOM_COLOR, bcolor);

    bcolor = bcArray[1];
    this.setColor(DvtCSSStyle.BORDER_LEFT_COLOR, bcolor);
    this.setColor(DvtCSSStyle.BORDER_RIGHT_COLOR, bcolor);
    break;

  case 3:
    this.setColor(DvtCSSStyle.BORDER_TOP_COLOR, bcArray[0]);
    bcolor = bcArray[1];
    this.setColor(DvtCSSStyle.BORDER_LEFT_COLOR, bcolor);
    this.setColor(DvtCSSStyle.BORDER_RIGHT_COLOR, bcolor);
    this.setColor(DvtCSSStyle.BORDER_BOTTOM_COLOR, bcArray[2]);
    break;

  case 4:
    this.setColor(DvtCSSStyle.BORDER_TOP_COLOR, bcArray[0]);
    this.setColor(DvtCSSStyle.BORDER_RIGHT_COLOR, bcArray[1]);
    this.setColor(DvtCSSStyle.BORDER_BOTTOM_COLOR, bcArray[2]);
    this.setColor(DvtCSSStyle.BORDER_LEFT_COLOR, bcArray[3]);
    break;

  case 0:
  default:
    break;
  }
};


// val is a string contains up to 4 padding sizes, ex:
// padding: 2px 9px 1px
DvtCSSStyle.prototype.setPaddingShorthand = function(key, val) {
  var padArray = val.split(" ");
  var padding = null;

  switch (padArray.length) {
  case 1:
    this.setStyle(key, this._getPercent(padArray[0]));
    delete this[DvtCSSStyle.PADDING_TOP];
    delete this[DvtCSSStyle.PADDING_BOTTOM];
    delete this[DvtCSSStyle.PADDING_RIGHT];
    delete this[DvtCSSStyle.PADDING_LEFT];
    break;

  case 2:
    padding = this._getPercent(padArray[0]);
    this.setStyle(DvtCSSStyle.PADDING_TOP, padding);
    this.setStyle(DvtCSSStyle.PADDING_BOTTOM, padding);

    padding = this._getPercent(padArray[1]);
    this.setStyle(DvtCSSStyle.PADDING_LEFT, padding);
    this.setStyle(DvtCSSStyle.PADDING_RIGHT, padding);
    break;

  case 3:
    this.setStyle(DvtCSSStyle.PADDING_TOP, this._getPercent(padArray[0]));
    padding = this._getPercent(padArray[1]);
    this.setStyle(DvtCSSStyle.PADDING_LEFT, padding);
    this.setStyle(DvtCSSStyle.PADDING_RIGHT, padding);
    this.setStyle(DvtCSSStyle.PADDING_BOTTOM, this._getPercent(padArray[2]));
    break;

  case 4:
    this.setStyle(DvtCSSStyle.PADDING_TOP, this._getPercent(padArray[0]));
    this.setStyle(DvtCSSStyle.PADDING_RIGHT, this._getPercent(padArray[1]));
    this.setStyle(DvtCSSStyle.PADDING_BOTTOM, this._getPercent(padArray[2]));
    this.setStyle(DvtCSSStyle.PADDING_LEFT, this._getPercent(padArray[3]));
    break;

  case 0:
  default:
    break;
  }
};

// val is a string contains up to 4 margin sizes, ex:
// margin: 2px 9px 1px
//TODO: margin can be negative numbers and in CM
DvtCSSStyle.prototype.setMarginShorthand = function(key, val) {
  var padArray = val.split(" ");
  var margin = null;

  switch (padArray.length) {
  case 1:
    this.setStyle(key, this._getMargin(padArray[0]));
    delete this[DvtCSSStyle.MARGIN_TOP];
    delete this[DvtCSSStyle.MARGIN_BOTTOM];
    delete this[DvtCSSStyle.MARGIN_RIGHT];
    delete this[DvtCSSStyle.MARGIN_LEFT];
    break;

  case 2:
    margin = this._getMargin(padArray[0]);
    this.setStyle(DvtCSSStyle.MARGIN_TOP, margin);
    this.setStyle(DvtCSSStyle.MARGIN_BOTTOM, margin);

    margin = this._getMargin(padArray[1]);
    this.setStyle(DvtCSSStyle.MARGIN_LEFT, margin);
    this.setStyle(DvtCSSStyle.MARGIN_RIGHT, margin);
    break;

  case 3:
    this.setStyle(DvtCSSStyle.MARGIN_TOP, this._getMargin(padArray[0]));
    margin = this._getMargin(padArray[1]);
    this.setStyle(DvtCSSStyle.MARGIN_LEFT, margin);
    this.setStyle(DvtCSSStyle.MARGIN_RIGHT, margin);
    this.setStyle(DvtCSSStyle.MARGIN_BOTTOM, this._getMargin(padArray[2]));
    break;

  case 4:
    this.setStyle(DvtCSSStyle.MARGIN_TOP, this._getMargin(padArray[0]));
    this.setStyle(DvtCSSStyle.MARGIN_RIGHT, this._getMargin(padArray[1]));
    this.setStyle(DvtCSSStyle.MARGIN_BOTTOM, this._getMargin(padArray[2]));
    this.setStyle(DvtCSSStyle.MARGIN_LEFT, this._getMargin(padArray[3]));
    break;

  case 0:
  default:
    break;
  }
};

//TODO: auto size defined by browsers
DvtCSSStyle.prototype._getMargin = function(val) {
  var margin = DvtStringUtils.trim(val);
  if (DvtCSSStyle.AUTO == margin)
    return DvtCSSStyle.AUTO_MARGIN;
  else
    return this._getPercent(margin);
};



DvtCSSStyle.prototype._getPercent = function(val) {
  return DvtStringUtils.trim(val);
};


DvtCSSStyle.prototype.setBorderShorthand = function(key, val) {
  /*
   * "border" Shorthand property can specify width, style, and color
   * separated by spaces, which are applied to all 4 border sides
   * ex: "1px solid rgb(185, 185, 180)"
   */
  var borderArray = val.split(" ");
  var borderVal = null;
  var noBorder = false;

  for (var i = 0; i < borderArray.length; i++) {
    borderVal = borderArray[i];

    //rgb border color
    if (borderVal.indexOf("rgb") == 0) {
      for (var k = i + 1; k < borderArray.length; k++) {
        borderVal += borderArray[k];
        i++;
        if (borderArray[k].indexOf(")") != -1)
          break;
      }
      this.setBorderColorShorthand(DvtCSSStyle.BORDER_COLOR, borderVal);
    }

    //border color
    else if (DvtCSSStyle.isColorVal(borderVal))
      this.setBorderColorShorthand(DvtCSSStyle.BORDER_COLOR, borderVal);

    //border width
    else if (DvtCSSStyle.isBorderWidthVal(borderVal))
      this.setBorderWidthShorthand(DvtCSSStyle.BORDER_WIDTH, borderVal);

    //we don't currently handle border style, but if it's none, set width=0
    else if (val == "none")
      noBorder = true;

  }

  if (noBorder) {
    this.setBorderWidthShorthand(DvtCSSStyle.BORDER_WIDTH, "0");
  }
};


DvtCSSStyle._getBorderRadius = function(val) {

  var radii = DvtStringUtils.trim(val);
  return isNaN(parseFloat(radii)) ? "0px" : radii;
};

//bug 13826956 - border-radius css property not supported when used inside <dvt:node>
DvtCSSStyle.prototype.setBorderRadius = function(key, val) {
  /*
   * border-radius: 1-4 length|% / 1-4 length|%;
   *
   * Note: order of 4 values: top-left, top-right, bottom-right, bottom-left.
   * If bottom-left is omitted it is the same as top-right. If bottom-right is omitted 
   * it is the same as top-left. 
   */
  var brArray = val.split("/");
  if (brArray[0] == null) {
    this[key] = "0px";
    return;
  }
  // horizontal radii
  var brHorzArr = DvtStringUtils.trim(brArray[0]).split(" ");
  var bRadius = null;

  switch (brHorzArr.length) {
  case 1:
  default:
    this[key] = DvtCSSStyle._getBorderRadius(brHorzArr[0]);
    // remove other border radius
    delete this[DvtCSSStyle.BORDER_TOP_LEFT_RADIUS];
    delete this[DvtCSSStyle.BORDER_TOP_RIGHT_RADIUS];
    delete this[DvtCSSStyle.BORDER_BOTTOM_RIGHT_RADIUS];
    delete this[DvtCSSStyle.BORDER_BOTTOM_LEFT_RADIUS];
    break;

/*
 * only support 4 corner with the same radius

  case 2:
    bRadius = DvtCSSStyle._getBorderRadius(brHorzArr[0]);
    this.setStyle(DvtCSSStyle.BORDER_TOP_LEFT_RADIUS, bRadius);
    this.setStyle(DvtCSSStyle.BORDER_BOTTOM_RIGHT_RADIUS, bRadius);

    bRadius = DvtCSSStyle._getBorderRadius(brHorzArr[1]);
    this.setStyle(DvtCSSStyle.BORDER_TOP_RIGHT_RADIUS, bRadius);
    this.setStyle(DvtCSSStyle.BORDER_BOTTOM_LEFT_RADIUS, bRadius);
    break;

  case 3:
    this.setStyle(DvtCSSStyle.BORDER_TOP_LEFT_RADIUS, DvtCSSStyle._getBorderRadius(brHorzArr[0]));
    bRadius = DvtCSSStyle._getBorderRadius(brHorzArr[1]);
    this.setStyle(DvtCSSStyle.BORDER_TOP_RIGHT_RADIUS, bRadius);
    this.setStyle(DvtCSSStyle.BORDER_BOTTOM_RIGHT_RADIUS, DvtCSSStyle._getBorderRadius(brHorzArr[2]));
    this.setStyle(DvtCSSStyle.BORDER_BOTTOM_LEFT_RADIUS, bRadius);
    break;

  case 4:
    this.setStyle(DvtCSSStyle.BORDER_TOP_LEFT_RADIUS, DvtCSSStyle._getBorderRadius(brHorzArr[0]));
    this.setStyle(DvtCSSStyle.BORDER_TOP_RIGHT_RADIUS, DvtCSSStyle._getBorderRadius(brHorzArr[1]));
    this.setStyle(DvtCSSStyle.BORDER_BOTTOM_RIGHT_RADIUS, DvtCSSStyle._getBorderRadius(brHorzArr[2]));
    this.setStyle(DvtCSSStyle.BORDER_BOTTOM_LEFT_RADIUS, DvtCSSStyle._getBorderRadius(brHorzArr[3]));
    break;

  case 0:
  default:
    break;

*/
  }

  // vertical radii
  if (brArray.length == 2 && brArray[1] != null) {
    var brVertArr = DvtStringUtils.trim(brArray[1]).split(" ");

    switch (brVertArr.length) {
    case 1:
      if (this[key])
        this[key] += " " + DvtCSSStyle._getBorderRadius(brVertArr[0]);
      else
        this[key] = DvtCSSStyle._getBorderRadius(brVertArr[0]);
      break;

    case 0:
    default:
      break;
    }
  }

};

/**
 * Returns border radius as a number. If the radius were not set for this style, returns 0
 * @return {number} border radius
 */
DvtCSSStyle.prototype.getBorderRadius = function() {
  if (this[DvtCSSStyle.BORDER_RADIUS] !== "undefined" && this[DvtCSSStyle.BORDER_RADIUS] !== null)
    return DvtCSSStyle.toNumber(this[DvtCSSStyle.BORDER_RADIUS]);
  else
    return 0;
};

DvtCSSStyle.prototype.setBackgroundRepeat = function(key, val) {
  var nval = val.toLowerCase();
  var a = val.split(" ");

  // Bug 10080044 - buttons look broken on safari
  // in Safari button -> background-repeat: repeat no-repeat 
  if (a.length == 2) {
    // both repeat or no_repeat
    if (a[0] == a[1])
      val = a[0];
    else if (a[0] == DvtCSSStyle.REPEAT && a[1] == DvtCSSStyle.NO_REPEAT)
      val = DvtCSSStyle.REPEAT_X;
    if (a[0] == DvtCSSStyle.NO_REPEAT && a[1] == DvtCSSStyle.REPEAT)
      val = DvtCSSStyle.REPEAT_Y;
  }
  this[key] = val;
};


/**
 * set a color attribute
 * 
 * @param {string} key color attribute
 * @param {string} val is a string in color keyword or a numeric value in #AARRGGBB or #RRGGBB format
 */
DvtCSSStyle.prototype.setColorString = function(key, val) {
  var color = DvtCSSStyle._toColorVal(val);
  if (color) {
    this[key] = color;
  }
  else if (DvtCSSStyle.isColorVal(val)) {
    this[key] = val;
  }
};


/**
 * If the given string is a color keyword, return its color value
 * 
 * @param s string that may specify a color keyword
 * @return color value if the string is a color keyword.
 */
DvtCSSStyle._toColorVal = function(val) {

  val = DvtStringUtils.trim(val);

  // color name?
  var color = DvtCSSStyle.SpColorMap[val.toLowerCase()];
  if (color) {
    return color;
  }
  return null;
};
    
DvtCSSStyle.prototype.getStyle = function(key) {
  return this[key];
};

DvtCSSStyle.prototype.setStyle = function(key, val) {
  this[key] = val;
};

DvtCSSStyle.prototype.setColor = function(key, val) {
  this[key] = val;
}


/**
 * @private
 * @param value a number with unit
 */
DvtCSSStyle.isValueInEM = function(value) {
  return value.toLowerCase().indexOf("em") >= 0;
}

/**
 * @private
 * @param key its value is a number with unit
 * @param fontSize (string) a number with unit
 *
 */
DvtCSSStyle.prototype._resolveEMx = function(key, fontSize) {
  var value = this.getStyle(key);

  if (value && DvtCSSStyle.isValueInEM(value)) {
    // if value is in EM unit, convert it to px so value no longer in EM
    if (fontSize) {
      this.setStyle(key, 
                    String(Math.round(parseFloat(value) * parseFloat(fontSize))) + "px");
      return true;
    }
    return false;
  }
  return true;
};


/**
 * @return font size if is a font size is specified and not in relative length unit(em)
 * otherwise return null
 */
DvtCSSStyle.prototype.getAbsoluteFontSize = function() {
  // get the current font size
  var fontSize = this.getFontSize();
  if (fontSize && ! DvtCSSStyle.isValueInEM(fontSize)) {
    return fontSize;
  }
  return null;
};


/**
 * @param {string} fontSize to be used in resolving em if no fontSize specified in this css style
 */
DvtCSSStyle.prototype.resolveEM = function(fontSize) {

  // try to resolved em unit with the current font size
  var myFontSize = this.getFontSize();

  if (myFontSize) {
    // FONT_SIZE value is in "em" 
    if (DvtCSSStyle.isValueInEM(myFontSize)) {
      // resolve fontSize and use this new fontSize value
      this._resolveEMx(DvtCSSStyle.FONT_SIZE, fontSize);
    }
    // FONT_SIZE value is NOT in "em", use new fontSize value
    else {
      fontSize = myFontSize;
    }
  }

  var numList = DvtCSSStyle._numericAttrsList;
  for (var i = 0; i < numList.length; i++) {
    if (! this._resolveEMx(numList[i], fontSize)) {
      return false;
    }
  }
  return true;
};


/**
 * return a copy of this object
 */
DvtCSSStyle.prototype.clone = function() {
  var css = new DvtCSSStyle();
  for (var i = 0; i < DvtCSSStyle._attrs.length; i++) {
    var key = DvtCSSStyle._attrs[i];

    if (this[key])
      css[key] = this[key];
  }
  return css;
};

/**
 * Merge the properties of the given CSSStyle into this one.
 * 
 * @param style properties to merge into this CSSStyle
 */
DvtCSSStyle.prototype.merge = function(style) {
  if (style) {
    for (var i = 0; i < DvtCSSStyle._attrs.length; i++) {
      var key = DvtCSSStyle._attrs[i];
      if (style[key])
        this[key] = style[key];
    }
  }
  return this;
};

/**
 * Merge the properties of the given CSSStyle under this one.
 * 
 * @param style properties to merge under this CSSStyle
 */
DvtCSSStyle.prototype.mergeUnder = function(style) {
  if (style) {
    for (var i = 0; i < DvtCSSStyle._attrs.length; i++) {
      var key = DvtCSSStyle._attrs[i];

      if (! this[key] && style[key])
        this[key] = style[key];
    }
  }
  return this;
};



/**
 * Convenience method for getting only styles specified in the list
 * and convert them to a string
 * 
 * @param {array} attrs an array of style properties of interest
 * @return {string} an inlineStyle in string
 */
DvtCSSStyle.prototype.toInlineStyle = function(attrs) {
  var strBuf = "";
  for (var i = 0; i < attrs.length; i++) {
    var key = attrs[i];

    if (this[key] !== undefined)
      strBuf += key + ":" + this[key] + "; ";
  }
  return strBuf;
};

DvtCSSStyle.prototype.toString = function() {
  return this.toInlineStyle(DvtCSSStyle._attrs);
};



DvtCSSStyle.prototype.getBackgroundImage = function() {
  var ret = this[DvtCSSStyle.BACKGROUND_IMAGE];

  var startx;
  var endx;
      
  if (ret && (ret instanceof DvtCSSGradient)) {
    return ret;
  }

  //strip off the 'url(' and ')'
  if (ret) {
    startx = ret.indexOf("url(");
    endx = ret.lastIndexOf(")");
    if ((startx == 0) && (endx == (ret.length - 1))) {
      ret = ret.substring(4, endx);
    }
  }
      
  //strip off single or double quotes
  if (ret) {
    startx = ret.indexOf(DvtCSSStyle.SINGLE_QUOTE);
    endx = ret.lastIndexOf(DvtCSSStyle.SINGLE_QUOTE);
    if ((startx == 0) && (endx == (ret.length - 1))) {
      ret = ret.substring(1, endx);
    }
    else {
      startx = ret.indexOf(DvtCSSStyle.DOUBLE_QUOTE);
      endx = ret.lastIndexOf(DvtCSSStyle.DOUBLE_QUOTE);
      if ((startx == 0) && (endx == (ret.length - 1))) {
        ret = ret.substring(1, endx);
      }
    }
  }
      
  return ret;
};
  	
/**
 * Determine if the given string specifies a color value.
 * 
 * @param s string that may specify a color value
 * 
 * @return true if the string specifies a color value, false otherwise
 */
DvtCSSStyle.isColorVal = function(val) {
  val = DvtStringUtils.trim(val);

  if (val.indexOf("rgb") == 0 ||
      val.indexOf("#") == 0)
    return true;

  // color name?
  var color = DvtCSSStyle.SpColorMap[val.toLowerCase()];
  if (color) {
    return true;
  }
  return false;
};


/**
 * @private
 * #param val contain only one borderWidth value
 */
DvtCSSStyle.isBorderWidthVal = function(val) {

  var bwidth = DvtStringUtils.trim(val);
  if (DvtCSSStyle.SpWidthMap[bwidth])
    return true;

  return ! isNaN(parseFloat(bwidth));
};

// val is a string contains up to 4 rgb colors, ex:
// border-color: rgb(221, 221, 216) rgb(185, 185, 132) rgb(121, 121, 117)
DvtCSSStyle.parseBorderColorString = function(key, val) {

  var val = DvtStringUtils.trim(val);
  var cArray = [];
  while (val != null && val.length > 0) {
    val = DvtCSSStyle.parseBorderColor(val, cArray);
  }
  return cArray;
};


DvtCSSStyle.parseBorderColor = function(val, cArray) {
  val = DvtStringUtils.ltrim(val);
  var strLen = val.length;
  var endIndex = -1;
  var colorObj;

  if (! val) {
    return val;
  }
  // rgb format?
  if (val.indexOf("rgb") == 0) {
    endIndex = val.indexOf(")") + 1;
    colorObj = val.slice(0, endIndex);
  }

  // color name?
  else {
    endIndex = val.indexOf(" ");
    if (endIndex == -1)
      endIndex = strLen;
    var singleColor = val.slice(0, endIndex);

    colorObj = DvtCSSStyle._toColorVal(singleColor);

    // hex color
    if (! colorObj) {
      colorObj = DvtCSSStyle.parseHexColor(singleColor);
    }
  }
      
  if (colorObj) {
    cArray.push(colorObj);
    val = val.slice(endIndex);
  }
  return val;
}

// val is a string in #AARRGGBB or #RRGGBB format
DvtCSSStyle.parseHexColor = function(s) {
  var n ;
  var colorObj;
  if (s && s.length > 0) {
    var bHasAlpha  = (s.length > 7) ;
    n = Number("0x" + s.substring(1)) ;
    if (isNaN(n)) {
      n = 0 ;                   // assume black if we didn't get a valid numeric string
    }
    if (bHasAlpha) {
      var x = "#"+s.substring(3);
      colorObj = x;
    }
    else {
      colorObj = s;
    }
  }
  return colorObj;
}
    


// get padding-top, padding-right, padding-bottom and padding-left
// get border-top-width, border-right-width, border-bottom-width and border-left=width
// get margin-top, margin-right, margin-bottom and margin-left
//TODO: percent
DvtCSSStyle.prototype._getSideWidth = function(key, shortKey) {
  var side = this.getStyle(key);
  if (! side) {
    side = this.getStyle(shortKey);
  }
  return DvtCSSStyle.toNumber(side);
}

DvtCSSStyle.prototype.getFontSize = function() {
  return this.getStyle(DvtCSSStyle.FONT_SIZE);
}

// Returns a CSSStyle object as a merge of the array
DvtCSSStyle.mergeStyles = function(styleArray) {
    var style = new DvtCSSStyle();
    if (styleArray) {
        for (var i=0; i<styleArray.length;i++) {
            style.merge(styleArray[i]);
        }
    }
    return style;    
}

/**
 * Is this style empty?
 * 
 */
DvtCSSStyle.prototype.isEmpty = function() {
  for (var prop in this) {
    if (this.hasOwnProperty(prop)) {
      return false;
    }
  }
  return true;
};


/**
 * Returns true if the text is set to align end.  Takes reading direction into account when dealing with values of
 * "left" and "right".
 * @param {DvtContext} context
 * @return {boolean}
 */
DvtCSSStyle.prototype.isTextAlignEnd = function(context) {
  var align = this.getStyle(DvtCSSStyle.TEXT_ALIGN);
  if (align == "end")
    return true;

  if (DvtAgent.isRightToLeft(context)) {
    return (align == "left");
  }
  else {
    return (align == "right");
  }
}


DvtCSSStyle.prototype.parseBackgroundPosition = function() {
  var s = this.getStyle(DvtCSSStyle.BACKGROUND_POSITION);
  if (! s)
    return null;

  var sPosArray = s.split(" ");
  if (sPosArray.length == 0)
    return ["0%", "0%"];

  var posArray = ["50%", "50%"];
  var position;
  if (sPosArray.length > 0) {
    position = DvtStringUtils.trim(sPosArray[0]);
    if (position == "left") {
      posArray[0] = "0%";
    }
    else if (position == "right") {
      posArray[0] = "100%";
    }
    else if (position == "top") {
      posArray[1] = "0%";
    }
    else if (position == "bottom") {
      posArray[1] = "100%";
    }
    // percent
    else if (position != "center") {
      posArray[0] = position;
    }
  }
  if (sPosArray.length > 1) {
    position = DvtStringUtils.trim(sPosArray[1]);
    if (position == "left") {
      posArray[0] = "0%";
    }
    else if (position == "right") {
      posArray[0] = "100%";
    }
    else if (position == "top") {
      posArray[1] = "0%";
    }
    else if (position == "bottom") {
      posArray[1] = "100%";
    }
    // percent
    else if (position != "center") {
      posArray[1] = position;
    }

  }
  return posArray;
}

DvtCSSStyle.prototype.getIconUrl = function() {
  return this.getStyle(DvtCSSStyle.CONTENT);
}

DvtCSSStyle.prototype.setIconUrl = function(url) {
  this.setStyle(DvtCSSStyle.CONTENT, url);
}

/**
 * Base class for JSON components.
 * @class
 * @constructor
 * @extends {DvtContainer}
 * @export
 */
var DvtBaseComponent = function() {
  // Components should implement newInstance factory methods instead of exposing contructors.
}

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

/**
 * 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.
 * @protected
 */
DvtBaseComponent.prototype.Init = function(context, callback, callbackObj) {
  DvtBaseComponent.superclass.Init.call(this, context);
  this._callback = callback;
  this._callbackObj = callbackObj;
  
  /**
   * Reference to the DvtBaseComponentDefaults instance for this component.
   * @type {DvtBaseComponentDefaults}
   * @protected
   */
  this.Defaults = null;
  
  /**
   * Reference to the options specifications for the component.
   * @protected
   */
  this.Options = null;
}

/**
 * Returns a copy of the default options for the specified skin.
 * @param {string} skin The skin whose defaults are being returned.
 * @return {object} The object containing defaults for this component.
 * @protected
 */
DvtBaseComponent.prototype.GetDefaults = function(skin) 
{  
  // This function will work as long as this.Defaults is properly initialized.
  return this.Defaults ? this.Defaults.getDefaults(skin) : {};
}

/**
 * Specifies the options specifications for this component.
 * @param {object} options The object containing options specifications for this component.
 * @protected
 */
DvtBaseComponent.prototype.SetOptions = function(options) {
  // Subclasses should override
}

/**
 * Applies the specified options on top of the existing properties.  This function is only supported after the 
 * component is initially rendered.
 * @param {object} options The object containing data and specifications for this component.
 * @export
 */
DvtBaseComponent.prototype.applyOptions = function(options) 
{  
  if(!this.Options || !options)
    return;
    
  // Merge the new properties with the existing ones and set
  this.SetOptions(DvtJSONUtils.merge(options, this.Options));
}

/**
 * Renders the component with the specified options object.  If no options are supplied to a component
 * that has already been rendered, the component will be rerendered to the specified size.
 * @param {object} options The object containing data and specifications for this component.
 * @param {number} width The width of the component.
 * @param {number} height The height of the component.
 * @export
 */
DvtBaseComponent.prototype.render = function(options, width, height) 
{  
  // Subclasses should override
}

/**
 * Dispatches the event to the callback function.
 * @param {object} event The event to be dispatched.
 */
DvtBaseComponent.prototype.__dispatchEvent = function(event) {
  this.getCtx().getJsExt().dispatchEvent(this._callback, this._callbackObj, this, event);
}
/**
 * Base class for JSON component defaults.
 * @class
 * @constructor
 * @extends {DvtObj}
 */
var DvtBaseComponentDefaults = function() {
  this.Init();
}

DvtObj.createSubclass(DvtBaseComponentDefaults, DvtObj, "DvtBaseComponentDefaults");

/**
 * @param {object} defaultsMap A map of the skin names to the JSON of defaults.
 */
DvtBaseComponentDefaults.prototype.Init = function(defaultsMap) {
  this._defaults = defaultsMap ? defaultsMap : {};
}

/**
 * Returns the base skin name for this component
 * @return {String} The base skin name for this component
 */
DvtBaseComponentDefaults.prototype.getBaseSkinName = function() 
{  
  return 'alta';
}

/**
 * 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.
 */
DvtBaseComponentDefaults.prototype.calcOptions = function(userOptions) {
  var skin = userOptions ? userOptions['skin'] : null;
  var defaults = this.getDefaults(skin);

  // Use defaults if no overrides specified
  if(!userOptions)
    return defaults;
  else // Merge the options object with the defaults
    return DvtJSONUtils.merge(userOptions, defaults);
}

/**
 * Returns a copy of the default options for the specified skin.
 * @param {string} skin The skin whose defaults are being returned.
 * @return {object} The object containing defaults for this component.
 */
DvtBaseComponentDefaults.prototype.getDefaults = function(skin) {
  if (!skin)
    skin = this.getBaseSkinName();
  // Note: Subsequent default objects are deltas on top of previous objects
  if(skin == 'alta')
    return DvtJSONUtils.merge(this._defaults['alta'], DvtJSONUtils.merge(this._defaults['skyros'], this._defaults['fusion']));
  else if(skin == 'skyros')
    return DvtJSONUtils.merge(this._defaults['skyros'], this._defaults['fusion']);
  else
    return DvtJSONUtils.clone(this._defaults['fusion']);
}
/**
 * Context Menu Item property bag.
 * @param {DvtGraphContextMenuSelectEvent} event  The event to fire in response to selection of this menu item.
 * @param {string} text The text of the context menu item.
 * @param {boolean} disabled True if this context menu item is disabled.
 * @param {boolean} sepBefore True if a separator should appear before this menu item.
 * @class
 * @constructor
 */
var DvtContextMenuItem = function(event, text, disabled, sepBefore) {
  this._event = event;
  this._text = text;
  this._disabled = disabled;
  this._sepBefore = sepBefore;
}

DvtObj.createSubclass(DvtContextMenuItem, DvtObj, "DvtContextMenuItem");

/**
 * Returns the event to fire in response to selection of this menu item.
 * @return {DvtGraphContextMenuSelectEvent} The event to fire in response to selection of this menu item.
 */
DvtContextMenuItem.prototype.getEvent = function() {
  return this._event;
}

/**
 * Returns the text of the context menu item.
 * @return {string} The text of the context menu item.
 */
DvtContextMenuItem.prototype.getText = function() {
  return this._text;
}

/**
 * Returns true if this context menu item is disabled.
 * @return {string} True if this context menu item is disabled.
 */
DvtContextMenuItem.prototype.isDisabled = function() {
  return this._disabled;
}

/**
 * Returns true if a separator should appear before this menu item.
 * @return {string} True if a separator should appear before this menu item.
 */
DvtContextMenuItem.prototype.hasSeparatorBefore = function() {
  return this._sepBefore;
}
/**
 * ShowPopupBehavior property bag.
 * @param {string} popupId The id of the popup that will be fired.
 * @param {string} triggerType The interaction type that triggers the popup.
 * @param {string} alignId The id of the object that the popup will be aligned to.
 * @param {string} align  The alignment position for the popup.
 * @class
 * @constructor
 */
var DvtGraphShowPopupBehavior = function(popupId, triggerType, alignId, align) {
  this._popupId = popupId;
  this._triggerType = triggerType;
  this._alignId = alignId;
  this._align = align;
}

DvtObj.createSubclass(DvtGraphShowPopupBehavior, DvtObj, "DvtGraphShowPopupBehavior");

DvtGraphShowPopupBehavior.TRIGGER_TYPE_ACTION = "action";
DvtGraphShowPopupBehavior.TRIGGER_TYPE_CLICK = "click";
DvtGraphShowPopupBehavior.TRIGGER_TYPE_CONTEXT_MENU = "contextMenu";
DvtGraphShowPopupBehavior.TRIGGER_TYPE_MOUSE_HOVER = "mouseHover";

DvtGraphShowPopupBehavior.ALIGN_AFTER_START = "afterStart";
DvtGraphShowPopupBehavior.ALIGN_AFTER_END = "afterEnd";
DvtGraphShowPopupBehavior.ALIGN_BEFORE_START = "beforeStart";
DvtGraphShowPopupBehavior.ALIGN_BEFORE_END = "beforeEnd";
DvtGraphShowPopupBehavior.ALIGN_END_AFTER = "endAfter";
DvtGraphShowPopupBehavior.ALIGN_END_BEFORE = "endBefore";
DvtGraphShowPopupBehavior.ALIGN_START_AFTER = "startAfter";
DvtGraphShowPopupBehavior.ALIGN_START_BEFORE = "startBefore";
DvtGraphShowPopupBehavior.ALIGN_OVERLAP = "overlap";

/**
 * Creates and returns a new instance of DvtGraphShowPopupBehavior with properties parsed from
 * the specified DvtXmlNode.
 * @param {DvtXmlNode} xmlNode The xml node containing properties for the new instance.
 * @return {DvtGraphShowPopupBehavior}
 */
DvtGraphShowPopupBehavior.newInstance = function(xmlNode) {
  var popupId = xmlNode.getAttr("popupId");
  var triggerType = xmlNode.getAttr("triggerType");
  var alignId = xmlNode.getAttr("alignId");
  var align = xmlNode.getAttr("align");
  return new DvtGraphShowPopupBehavior(popupId, triggerType, alignId, 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.
 */
DvtGraphShowPopupBehavior.prototype.getPopupId = function() {
  return this._popupId;
}

/**
 * Returns the interaction type that triggers the popup.
 * @return {string} The the interaction type that triggers the popup.
 */
DvtGraphShowPopupBehavior.prototype.getTriggerType = function() {
  return this._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.
 */
DvtGraphShowPopupBehavior.prototype.getAlignId = function() {
  return this._alignId;
}

/**
 * Returns the alignment position for the popup.
 * @return {string} The alignment position for the popup.
 */
DvtGraphShowPopupBehavior.prototype.getAlign = function() {
  return this._align;
}
/**
 * DvtClientBehavior property bag.
 * @param {string} type The type of client behavior
 * @param {string} triggerType The interaction type that triggers the client behavior.
 * @class
 * @constructor
 */
var DvtClientBehavior = function(type, triggerType) {
  this._type = type;
  this._triggerType = triggerType;
  this._props = {};
}

DvtObj.createSubclass(DvtClientBehavior, DvtObj, "DvtClientBehavior");

DvtClientBehavior.TRIGGER_TYPE_ACTION = "action";
DvtClientBehavior.TRIGGER_TYPE_CLICK = "click";

DvtClientBehavior.TYPE_ROW_DISCLOSURE = "DvtRowDisclosureBehavior";
DvtClientBehavior.TYPE_DRILL = "DvtDrillBehavior";
DvtClientBehavior.TYPE_ISOLATE_LEVELS = "DvtIsolateLevelsBehavior";
DvtClientBehavior.TYPE_RESTORE = "DvtRestoreBehavior";

/**
 * Returns the type of client behavior
 * @return {string} The type of client behavior
 */
DvtClientBehavior.prototype.getType = function() {
  return this._type;
}

/**
 * Returns the interaction type that triggers the client behavior.
 * @return {string} The interaction type that triggers the client behavior.
 */
DvtClientBehavior.prototype.getTriggerType = function() {
  return this._triggerType;
}

DvtClientBehavior.prototype.setProperty = function(propName, propValue) {
  this._props[propName] = propValue;
}

DvtClientBehavior.prototype.getProperty = function(propName) {
  return this._props[propName];
}

DvtClientBehavior.prototype.getProperties = function() {
  return this._props;
}
/*--------------------------------------------------------------------*/
/*   DvtMagnifyLens              Magnify lens component                 */
/*--------------------------------------------------------------------*/
/**
  *  Magnify lens component.
  *  @extends DvtContainer
  *  @class DvtMagnifyLens  Creates a magnify lens component.
  *  @constructor  
  *  @param {DvtContext} context The context object
  */
var   DvtMagnifyLens = function(context)
{
    this.Init(context);
}  

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

DvtMagnifyLens.DEFAULT_BACKGROUND_COLOR = "#ffffff";

DvtMagnifyLens.prototype.Init = function(context) {

   this._context = context;
   this._lensBorderWidth = 2;
   this._backgroundFill = new DvtSolidFill(DvtMagnifyLens.DEFAULT_BACKGROUND_COLOR, 1);
   this._rimFillColor = "black";
   if (! this.getImpl()) {
     this.setImpl(context.getImplFactory().newContainer("maglens")) ;
   }
   DvtMagnifyLens.superclass.Init.call(this, context) ;

   this._zoomFactor = 3;
   this._zoomSize = 91;
   this._shiftValue = 0.5;
   this.setMouseEnabled(false);
   
   this.setTranslateX(this._lensBorderWidth);
   this.setTranslateY(this._lensBorderWidth);
   this._dirty = true;

}

DvtMagnifyLens.prototype.isDirty = function() 
{    
    return this._dirty;
}

DvtMagnifyLens.prototype.setDirty = function(dirty) 
{
    this._dirty = dirty;
}

DvtMagnifyLens.prototype.setZoomFactor = function(zoomFactor) 
{
    this._zoomFactor = zoomFactor;
}

DvtMagnifyLens.prototype.setSize = function(size) 
{
    this._zoomSize = size;
}

DvtMagnifyLens.prototype.setBackgroundFill = function(fill) 
{
    this._backgroundFill = fill;
}

DvtMagnifyLens.prototype.setRimFillColor = function(color) 
{
    this._rimFillColor = color;
}

DvtMagnifyLens.prototype.getSize = function() 
{
    return this._zoomSize + this._lensBorderWidth*2;
}

DvtMagnifyLens.prototype.initialize = function() 
{
   // TODO: in the future initialize all objects in the constructor, and change properties here
   if (this._initialized) {
        return;
   }
   this._initialized = true;

   var zoomSize = this._zoomSize;
   var context = this._context;
   
   var clipContainer = new DvtContainer(context, "clipContainer");
   var clipPath = new DvtGraphClipPath("maglens");

   var size = zoomSize/2;
   clipPath.addEllipse(size, size, size, size);
   this._magClipPath = clipPath;
   this._magClipContainer = clipContainer;
   
   this._magClipContainer.setClipPath(this._magClipPath);

   var zoomBack = new DvtCircle(context, zoomSize/2, zoomSize/2, zoomSize/2 - this._lensBorderWidth/2);
   zoomBack.setMouseEnabled(false);
   //zoomBack.setSolidStroke(this._borderColor, 1, this._lensBorderWidth);
   clipContainer.addChild(zoomBack);
   //var fakeLine = new DvtRect(context, zoomSize/2 - 2, 0, 4, zoomSize);
   //fakeLine.setFill(this._backgroundFill);
   //fakeLine.setMouseEnabled(false);
   //this._fakeLine = fakeLine;
   //clipContainer.addChild(fakeLine);

   clipContainer.setAlpha(1);
   this._view = clipContainer;
   this.addChild(clipContainer);

   var circleBack = new DvtCircle(this.getCtx(), zoomSize/2, zoomSize/2, zoomSize/2 + this._lensBorderWidth);
   circleBack.setSolidFill(this._rimFillColor);
   //circleBack.setFill(new DvtLinearGradientFill(45, [this._rimFillColor, '#CFCFCF'], [1, 0.75])); 
   circleBack.setMouseEnabled(false);
   this.addChildAt(circleBack, 0);

}

DvtMagnifyLens.prototype.UpdatePosition = function(x, y) 
{
    var zoomSize = this._zoomSize;
    var content = this._content;
    if (content) {
        var scaleFactor = this._zoomFactor;
        content.setTranslateX(-(x)*scaleFactor + zoomSize/2);
        content.setTranslateY(-y*scaleFactor + zoomSize*this._shiftValue);
    }
}

DvtMagnifyLens.prototype.getRootDisplayable = function() 
{
    return this;
}

DvtMagnifyLens.prototype.GetMagnifyContent = function(container) 
{
   return container.createCopy();
}

DvtMagnifyLens.prototype.getDisplayWidth = function() {
   return this._zoomSize + 2*this._lensBorderWidth;
}

DvtMagnifyLens.prototype.getDisplayHeight = function() {
   return this._zoomSize + 2*this._lensBorderWidth;
}

DvtMagnifyLens.prototype.clearContent = function() {
   var clonedContainer = this._content;
   if (clonedContainer) {
        this._view.removeChild(clonedContainer);
   }
}

DvtMagnifyLens.prototype.updateMagnifyContent = function(container) 
{
   this.clearContent();
   var clonedContainer = this.GetMagnifyContent(container);
   clonedContainer.setScaleX( this._zoomFactor);
   clonedContainer.setScaleY( this._zoomFactor);
   this._content = clonedContainer;

   var originalSize = container.getDimensions();
   var fixedBack = new DvtRect(this.getCtx(), originalSize.x, originalSize.y, originalSize.w, originalSize.h);
   fixedBack.setSolidFill('white');
   clonedContainer.addChildAt(fixedBack, 0);
   fixedBack.setMouseEnabled(false);

   this._view.addChildAt(clonedContainer, 0);
   this._view.setMouseEnabled(false);
   clonedContainer.setMouseEnabled(false);

}
/*--------------------------------------------------------------------*/
/*   DvtScrollbar             Scrollbar Component                     */
/*--------------------------------------------------------------------*/
/**
  *  Scrollbar component.
  *  @extends DvtContainer
  *  @class DvtScrollbar component. 
  *  @constructor  
  *  @implements {DvtAutomationContainer}
  */
var   DvtScrollbar = function(context, x, y, w, h, isHoriz, isZoomable, skinName, styleMap, id) {
    this.Init(context, x, y, w, h, isHoriz, isZoomable, skinName, styleMap, id);
}

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

// Constants
//

// ID's
DvtScrollbar.ID_NONE    = 0;
DvtScrollbar.ID_X1RS    = 1;
DvtScrollbar.ID_Y1RS    = 2;
DvtScrollbar.ID_Y2RS    = 3;
DvtScrollbar.ID_O1RS    = 4;

DvtScrollbar.DEFAULT_TEXT_COLOR    = "rgb(69,69,69)";
DvtScrollbar.LABEL_MASK_FILL_COLOR = "rgb(122,143,175)";

DvtScrollbar.SMALL_SCROLL_AMOUNT = 0.2;
DvtScrollbar.LARGE_SCROLL_AMOUNT = 1.0;

DvtScrollbar.MIN_THUMB_SIZE = 30;

DvtScrollbar.SCROLL_TIMER_DELAY = 50;
DvtScrollbar.SCROLL_TIMER_INITIAL_DELAY = 400;

DvtScrollbar.THUMB_TYPE = "thumb";
DvtScrollbar.GRIPPER_START_TYPE = "gripperStart";
DvtScrollbar.GRIPPER_END_TYPE = "gripperEnd";

// constants used for test automation
DvtScrollbar._TEST_ID_THIS                = 0;
DvtScrollbar._TEST_ID_SCROLL_THUMB        = 1;
DvtScrollbar._TEST_ID_SCROLL_BUTTON_DOWN  = 2;
DvtScrollbar._TEST_ID_SCROLL_BUTTON_UP    = 3;

DvtScrollbar.BACKGROUND = "scrollbarBackground";
DvtScrollbar.BORDER_COLOR = "scrollbarBorderColor";
DvtScrollbar.HANDLE_COLOR = "scrollbarHandleColor";
DvtScrollbar.HANDLE_ACTIVE_COLOR = "scrollbarHandleActiveColor";
DvtScrollbar.HANDLE_HOVER_COLOR = "scrollbarHandleHoverColor";

/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    @private
 */
DvtScrollbar.prototype.Init = function(context, x, y, w, h, isHoriz, isZoomable, skinName, styleMap, id) {
    DvtScrollbar.superclass.Init.call(this, context);
    this._isHoriz             = isHoriz;

    this._styleMap = styleMap;
    this._skinName = skinName;
    
    // used to remember initial state while dragging the thumb
    this._clickDiff = new Object();
    this._clickStartPos = new Object();
    this._clickEndPos = new Object();
    this._lastMovePos = -1;
    
    this._container = new DvtRect(context, x, y, w, h, id);
    this._container.setPixelHinting(true);
    this.addChild(this._container);
    this._background = new DvtScrollbarBackground(this);
    this._container.addChild(this._background);
    
    // Create scroll arrows
    if (!this.IsSkinAlta()) {
      this._arrowWidth = DvtScrollbarArrow.WIDTH;
      if (this._isHoriz) {
        this._arrowDown = new DvtScrollbarArrow(this, DvtScrollbarArrow.ORIENT_LEFT);
        this._arrowUp = new DvtScrollbarArrow(this, DvtScrollbarArrow.ORIENT_RIGHT);
      } else {
        this._arrowDown = new DvtScrollbarArrow(this, DvtScrollbarArrow.ORIENT_DOWN);
        this._arrowUp = new DvtScrollbarArrow(this, DvtScrollbarArrow.ORIENT_UP);
      }
      this._container.addChild(this._arrowDown);
      this._container.addChild(this._arrowUp);
      this._container.setSolidStroke("black");
    } else {
      this._arrowWidth = 0;
    }
    
    // Create scroll handle
    this._thumbStartMin = this._arrowWidth;
    this._thumbStartPos = this._thumbStartMin;  
    if (this._isHoriz)
      this._thumbLengthMax = w - this._arrowWidth * 2;
    else
      this._thumbLengthMax = h - this._arrowWidth * 2;
    this._thumbLength = this._thumbLengthMax;
    this._thumb = new DvtScrollbarThumb(this, isZoomable, this.getSkinName());
    this._container.addChild(this._thumb)
    
    this._scrollTimer = new DvtTimer(context, DvtScrollbar.SCROLL_TIMER_DELAY, this.repeatScroll, this);
    this._initScrollTimer = new DvtTimer(context, DvtScrollbar.SCROLL_TIMER_INITIAL_DELAY, this.initRepeatScroll, this);

    this._bScrolled = false;
    this._isLiveScroll = true;
}

DvtScrollbar.prototype.getStyleMap = function() {
  return this._styleMap;
}

DvtScrollbar.prototype.getSkinName = function() {
  return this._skinName;
}

DvtScrollbar.prototype.IsSkinAlta = function() {
  return (this._skinName == "alta");
};

DvtScrollbar.prototype.isHorizontal = function() {
  return this._isHoriz;
}

DvtScrollbar.prototype.getThumbStart = function() {
  return this._thumbStartPos;
}

DvtScrollbar.prototype.getThumbLength = function() {
  return this._thumbLength;
}

DvtScrollbar.prototype.setThumbStartAndLength = function(start, length) {

    var k = 0;

    if (this._isHoriz) {
        k = (this.getWidth() - 2 * this._arrowWidth) / this.getWidth();
        this._thumbStartPos = this._arrowWidth + Math.round(start * k);
        this._thumbLength = Math.round(length * k);
    }
    else {
        k = (this.getHeight() - 2 * this._arrowWidth) / this.getHeight();
        // revert for horizontal axis
        // start = this.getHeight() - start - length;
        this._thumbStartPos = this._arrowWidth + Math.round(start * k);
        this._thumbLength = Math.round(length * k);
    }
    this._thumb.updateSize();
}

DvtScrollbar.prototype.getScrollAreaStart = function() {
  return this._thumbStartMin;
}

DvtScrollbar.prototype.getScrollAreaLength = function() {
  return this._thumbLengthMax;
}

DvtScrollbar.prototype.initRepeatScroll = function() {
    this._initScrollTimer.stop();
    this._scrollTimer.stop();
    this._scrollTimer.start();
}

DvtScrollbar.prototype.repeatScroll = function() {
    if (this._arrow && !this._arrow.isDisabled())
        this.arrowScroll();
}

DvtScrollbar.prototype.arrowScroll = function() {
    
    // BUT #12805200: update scrolling speed to match flash
    var delta = DvtScrollbar.SMALL_SCROLL_AMOUNT;

    this._lastMovePos = this._thumbStartPos;
    
    switch(this._arrow.getOrientation()) {
        case DvtScrollbarArrow.ORIENT_RIGHT:
        case DvtScrollbarArrow.ORIENT_DOWN:
            this.doScroll(delta);
            break;

        case DvtScrollbarArrow.ORIENT_LEFT:
        case DvtScrollbarArrow.ORIENT_UP:
            this.doScroll(-delta);
            break;
    }

    this._bScrolled = true;
    this._fireMoveEvent(!this._isLiveScroll);
}

DvtScrollbar.prototype.doScroll = function(delta) {
    var pos = this._thumbStartPos + Math.round(this._thumbLength * delta);

    this.moveTo(pos);
}

DvtScrollbar.prototype.moveTo = function(pos) {

    if (pos < this._thumbStartMin) {
        pos = this._thumbStartMin;
    }
    if ((pos + this._thumbLength) > (this._thumbStartMin + this._thumbLengthMax)) {
        pos = this._thumbStartMin + this._thumbLengthMax - this._thumbLength;
    }
    this._thumbStartPos = pos;
    this._thumb.updateSize();
    this._thumb.updateTooltipText();
}

DvtScrollbar.prototype.moveStart = function(pos) {

    var endPos = this._thumbStartPos + this._thumbLength;

    if (pos < this._thumbStartMin) {
        pos = this._thumbStartMin;
    }
    if (pos > (endPos - DvtScrollbar.MIN_THUMB_SIZE)) {
        pos = endPos - DvtScrollbar.MIN_THUMB_SIZE;
    }
    this._thumbStartPos = pos;
    this._thumbLength = endPos - pos;
    this._thumb.updateSize();
    this._thumb.updateTooltipText();
}

DvtScrollbar.prototype.moveEnd = function(pos) {

    if (pos < (this._thumbStartPos + DvtScrollbar.MIN_THUMB_SIZE)) {
        pos = this._thumbStartPos + DvtScrollbar.MIN_THUMB_SIZE;
    }
    if (pos > (this._thumbStartMin + this._thumbLengthMax)) {
        pos = this._thumbStartMin + this._thumbLengthMax;
    }
    this._thumbLength = pos - this._thumbStartPos;
    this._thumb.updateSize();
    this._thumb.updateTooltipText();
}

DvtScrollbar.prototype.processBackgroundClick = function(point) {
    var pos;
    var delta = 0;

    if (this._isHoriz) {
        pos = point.x - this.getX();
        if (pos < this._thumbStartPos)
            delta = -DvtScrollbar.LARGE_SCROLL_AMOUNT;
        else if (pos > this._thumbStartPos + this._thumbLength)
            delta = DvtScrollbar.LARGE_SCROLL_AMOUNT;
    }
    else {
        pos = point.y - this.getY();
        if (pos < this._thumbStartPos)
            delta = -DvtScrollbar.LARGE_SCROLL_AMOUNT;
        else if (pos > this._thumbStartPos + this._thumbLength)
            delta = DvtScrollbar.LARGE_SCROLL_AMOUNT;
    }

    if (delta != 0) {
        // remember the current pos for live scroll
        this._lastMovePos = this._thumbStartPos;
        // recalculate thumb position
        this.doScroll(delta);
        // and notify listeners
        // for live scroll, need to fire both MOVE and MOVE_COMPLETE events
        if (this._isLiveScroll)
            this._fireMoveEvent(false);
        this._fireMoveEvent(true);
    }
}

DvtScrollbar.prototype.processArrowDown = function(arrow) {
    this._arrow = arrow;

    this.arrowScroll();
    this._initScrollTimer.stop();
    this._initScrollTimer.start();
}

DvtScrollbar.prototype.handleArrowDown = function(arrow, event) {
    this.processArrowDown(arrow);
}

DvtScrollbar.prototype.processArrowUp = function(arrow) {
    this._clearTimers();
    // fire MOVE_COMPLETE, if scroll occured and live scroll
    if (this._bScrolled && this._isLiveScroll) {
        this._fireMoveEvent(true);
    }
    this._bScrolled = false;
}

DvtScrollbar.prototype._clearTimers = function() {
    this._initScrollTimer.stop();
    this._scrollTimer.stop();
}

DvtScrollbar.prototype.handleArrowUp = function(arrow, event) {
    this.processArrowUp();
}

DvtScrollbar.prototype.processThumbDragStart = function(point) {
    // note the drag start position
    if (this.isHorizontal()) {
        this._clickDiff[DvtScrollbar.THUMB_TYPE] = point.x;
    }
    else {
        this._clickDiff[DvtScrollbar.THUMB_TYPE] = point.y;
    }

    // remember the thumb start position at the time of drag start
    this._clickStartPos[DvtScrollbar.THUMB_TYPE] = this._thumbStartPos;
    // also remember if for tracking move
    this._lastMovePos = this._thumbStartPos;
}

DvtScrollbar.prototype.processThumbDragMove = function(point) {
    var pos;
    if (this.isHorizontal()) {
        pos = this._clickStartPos[DvtScrollbar.THUMB_TYPE] + (point.x - this._clickDiff[DvtScrollbar.THUMB_TYPE]);
    }
    else {
        pos = this._clickStartPos[DvtScrollbar.THUMB_TYPE] + (point.y - this._clickDiff[DvtScrollbar.THUMB_TYPE]);
    }
    this.moveTo(pos);

    this._fireMoveEvent(false);
}

DvtScrollbar.prototype.processThumbDragEnd = function(point) {
    var moveDelta = 0;
    
    if (this.isHorizontal())
        moveDelta = this._clickDiff[DvtScrollbar.THUMB_TYPE] - point.x;
    else
        moveDelta = this._clickDiff[DvtScrollbar.THUMB_TYPE] - point.y;

    if (moveDelta != 0)
      this._fireMoveEvent(true);      // fire MOVE_COMPLETE event
}

/**
 * Handles a mouse event caused by starting to drag the scrollbar grippers.
 * @param {DvtScrollbarGripper} gripper The scrollbar gripper that is dragged
 * @param {DvtMouseEvent} event The mouse event to handle
 */
DvtScrollbar.prototype.handleGripDragStart = function(gripper, event) {
    var point = new DvtPoint(event.pageX, event.pageY);
    this.processGripDragStart(gripper, point);
}

DvtScrollbar.prototype.processGripDragStart = function(gripper, point) {

    var itemType = this.getGripperItemType(gripper);
    
    // note the drag start position
    if (this.isHorizontal()) {
        this._clickDiff[itemType] = point.x;
    }
    else {
        this._clickDiff[itemType] = point.y;
    }

    // remember the thumb start position at the time of drag start
    this._clickStartPos[itemType] = this._thumbStartPos;
    this._clickEndPos[itemType] = this._thumbStartPos + this._thumbLength;
    this._fireResizeEvent(false);
}

DvtScrollbar.prototype.getGripperItemType = function(gripper) {
    var itemType = DvtScrollbar.GRIPPER_START_TYPE;
    var type = gripper.getType();
    if (type == DvtScrollbarGripper.TYPE_RIGHT || type == DvtScrollbarGripper.TYPE_BOTTOM) {
        itemType = DvtScrollbar.GRIPPER_END_TYPE;
    }
    return itemType;
}

/**
 * Handles a mouse event caused by moving and dragging scrollbar grippers.
 * @param {DvtScrollbarGripper} gripper The scrollbar gripper that is dragged
 * @param {DvtMouseEvent} event The mouse event to handle
 */
DvtScrollbar.prototype.handleGripDragMove = function(gripper, event) {
    var point = new DvtPoint(event.pageX, event.pageY);
    this.processGripDragMove(gripper, point);
}

DvtScrollbar.prototype.processGripDragMove = function(gripper, point) {
    var itemType = this.getGripperItemType(gripper);

    var pos;

    switch (gripper.getType()) {
        case DvtScrollbarGripper.TYPE_LEFT:
            pos = this._clickStartPos[itemType] + (point.x - this._clickDiff[itemType]);
            this.moveStart(pos);
            break;

        case DvtScrollbarGripper.TYPE_RIGHT:
            pos = this._clickEndPos[itemType] + (point.x - this._clickDiff[itemType]);
            this.moveEnd(pos);
            break;

        case DvtScrollbarGripper.TYPE_BOTTOM:
            pos = this._clickEndPos[itemType] + (point.y - this._clickDiff[itemType]);
            this.moveEnd(pos);
            break;

        case DvtScrollbarGripper.TYPE_TOP:
            pos = this._clickStartPos[itemType] + (point.y - this._clickDiff[itemType]);
            this.moveStart(pos);
            break;

        default:
            break;
    }
    this._fireResizeEvent(false); 
}

/**
 * Handles a mouse event caused by ending dragging of the scrollbar grippers.
 * @param {DvtScrollbarGripper} gripper The scrollbar gripper that is dragged
 * @param {DvtMouseEvent} event The mouse event to handle
 */
DvtScrollbar.prototype.handleGripDragEnd = function(gripper, event) {
    var point = new DvtPoint(event.pageX, event.pageY);
    var movedDelta = 0;
    var itemType = this.getGripperItemType(gripper);

    switch (gripper.getType()) {
        case DvtScrollbarGripper.TYPE_LEFT:
            movedDelta = this._clickDiff[itemType] - point.x;
            break;

        case DvtScrollbarGripper.TYPE_RIGHT:
            movedDelta = this._clickDiff[itemType] - point.x;
            break;

        case DvtScrollbarGripper.TYPE_BOTTOM:
            movedDelta = this._clickDiff[itemType] - point.y;
            break;

        case DvtScrollbarGripper.TYPE_TOP:
            movedDelta = this._clickDiff[itemType] - point.y;
            break;

        default:
            break;
    }
            
    if (movedDelta !== 0)
        this.processGripDragEnd();
}

DvtScrollbar.prototype.processGripDragEnd = function() {
    this._fireResizeEvent(true);        // fire RESIZE_COMPLETE event
}

DvtScrollbar.prototype._fireMoveEvent = function(complete) {

    var eType;

    if (complete)
        eType = DvtScrollbarEvent.SB_MOVE_COMPLETE;
    else if (this._isLiveScroll)
        eType = DvtScrollbarEvent.SB_MOVE;
    else
        return;     // no live scroll, do not fire event

    var start = this._thumbStartPos - this._thumbStartMin;
    var end   = start + this._thumbLength;

    var movedDelta;
    var scaledDelta;
    var sbEvent = null;

    movedDelta = this._thumbStartPos - this._lastMovePos;
    this._lastMovePos = this._thumbStartPos;
    scaledDelta = movedDelta / this._thumbLengthMax;

    if (movedDelta != 0 || complete) {
        sbEvent = new DvtScrollbarEvent(eType, this, start, end, movedDelta, scaledDelta);
    }
    if (sbEvent)
        this.FireListener(sbEvent, false);
}


DvtScrollbar.prototype._fireResizeEvent = function(complete) {

    var start = this._thumbStartPos - this._thumbStartMin;
    var end   = start + this._thumbLength;
    var eType = complete ? DvtScrollbarEvent.SB_RESIZE_COMPLETE : DvtScrollbarEvent.SB_RESIZE;

    var sbEvent = new DvtScrollbarEvent(eType, this, start, end);

    this.FireListener(sbEvent, false);
}

DvtScrollbar.prototype.move = function(offsetPct, dispatchEvent, complete) {
    this.moveSliderPercent(offsetPct);
    if (dispatchEvent)
        this._fireMoveEvent(complete);      // fire MOVE_COMPLETE event
}

DvtScrollbar.prototype.moveSliderPercent = function(offsetPct) {
    if(offsetPct < -100 || offsetPct > 100)
      return;
    this.moveSlider((offsetPct/100) * (this._thumbLengthMax)); 
}

DvtScrollbar.prototype.moveSlider = function(deltaPxls) {
    // The moveTo function takes care of bounds for us, so don't worry about it here
    if(this.isHorizontal())
      this.moveTo(this._thumbStartPos + deltaPxls);
    else
      this.moveTo(this._thumbStartPos + deltaPxls);
}

DvtScrollbar.prototype.processGlobalMouseUp = function() {
    this._clearTimers();
}

DvtScrollbar.prototype.getX = function() {
    return this._container.getX();
}

DvtScrollbar.prototype.getY = function() {
    return this._container.getY();
}

DvtScrollbar.prototype.getWidth = function() {
    return this._container.getWidth();
}

DvtScrollbar.prototype.getHeight = function() {
    return this._container.getHeight();
}

/**
 * @override
 */
DvtScrollbar.prototype.dispatchTestEvent = function(subId, eventType, params) 
{
  // implementation of interface DvtAutomationContainer
  var subComponent;

  if(subId <= DvtScrollbar._TEST_ID_THIS)
    subComponent = this;
  else if(subId == DvtScrollbar._TEST_ID_SCROLL_THUMB)
    subComponent = this._thumb;
  else if(subId == DvtScrollbar._TEST_ID_SCROLL_BUTTON_DOWN)
    subcomponent = this._arrowDown;
  else if(subId == DvtScrollbar._TEST_ID_SCROLL_BUTTON_UP)
    subComponent = this._arrowUp;
             
  if(subComponent && subComponent instanceof DvtDisplayable)
    subComponent.dispatchDisplayableEvent(eventType, params);
  
}

DvtScrollbar.prototype.getScrollbarThumb = function() {
  return this._thumb;
}

/*
  *  These have been added for users of the scrollbar in a standalone
  *  situation (no MT involved).  Essentially, the existing setThumbStartAndLength()
  *  does not work correctly or logically. The thumb sits in the scrollable area,
  *  and its initial start position should be zero (and not related to any
  *  outside chrome such as the scroll-arrows).
  */
DvtScrollbar.prototype.setLogicalThumbStartAndLength = function(start, length) {

   this._thumbStartPos = this._arrowWidth + start ;
   this._thumbLength   = length ;

   this._thumb.updateSize();
}

DvtScrollbar.prototype.getLogicalThumbStart = function() {
  return this._thumbStartPos - this._arrowWidth ;
}

/**
 * @override
 */
DvtScrollbar.prototype.destroy = function () {
  //Bug 16893087 - HTML5 HV: SEARCH RESULTS SCROLLBAR TRAPS MOUSE
  if (this._container) {
    if (this._thumb) {
      this._thumb.destroy();
      this._container.removeChild(this._thumb);
      this._thumb = null;
    }

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

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

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

  DvtScrollbar.superclass.destroy.call(this);
  this._container = null;

}

/*--------------------------------------------------------------------*/
/*   DvtScrollbarArrow          ScrollbarArrow button                 */
/*--------------------------------------------------------------------*/
/**
  *  DvtScrollbarArrow subcomponent
  *  @extends DvtContainer
  *  @class DvtScrollbarArrow  a subcomponent created and used 
  *  by DvtScrollbar
  *  <p>
  *  @constructor  
  */

var   DvtScrollbarArrow = function(sb, orientation) {
    this.Init(sb, orientation);
}

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

// Constants
//
DvtScrollbarArrow.WIDTH = 15;   // default arrow width/height

DvtScrollbarArrow.ORIENT_RIGHT = 0;
DvtScrollbarArrow.ORIENT_UP = 1;
DvtScrollbarArrow.ORIENT_LEFT = 2;
DvtScrollbarArrow.ORIENT_DOWN = 3;

DvtScrollbarArrow.ARROW_UP_BORDER       = '#000000';
DvtScrollbarArrow.ARROW_UP_BORDER_ALPHA = 0;
DvtScrollbarArrow.ARROW_UP_FILL         = '#000000';
DvtScrollbarArrow.BG_UP_FILL_COLORS     = ['#ffffff', '#ffffff'];
DvtScrollbarArrow.BG_UP_FILL_ALPHAS     = [0, 0];
DvtScrollbarArrow.BG_UP_FILL_RATIOS     = [0, 255];

DvtScrollbarArrow.ARROW_OVER_BORDER       = '#000000';
DvtScrollbarArrow.ARROW_OVER_BORDER_ALPHA = 0;
DvtScrollbarArrow.ARROW_OVER_FILL         = '#000000';
DvtScrollbarArrow.BG_OVER_FILL_COLORS     = ['#0d8cf9', '#4ab5fc'];
DvtScrollbarArrow.BG_OVER_FILL_ALPHAS     = [1, 1];
DvtScrollbarArrow.BG_OVER_FILL_RATIOS     = [0, 255];

DvtScrollbarArrow.ARROW_DOWN_BORDER       = '#4e555e';
DvtScrollbarArrow.ARROW_DOWN_BORDER_ALPHA = 1;
DvtScrollbarArrow.ARROW_DOWN_FILL         = '#ffffff';
DvtScrollbarArrow.BG_DOWN_FILL_COLORS     = ['#4e555e', '#a7b7d4'];
DvtScrollbarArrow.BG_DOWN_FILL_ALPHAS     = [1, 1];
DvtScrollbarArrow.BG_DOWN_FILL_RATIOS     = [0, 255];


/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    @protected
 */
DvtScrollbarArrow.prototype.Init = function(sb, orientation)
{
    var x, y, w, h;
    this._sb = sb;
    this._orientation = orientation;
    this._arrow = null;
    this._disabled = false;
    
    if (sb.isHorizontal()) {
        if (orientation == DvtScrollbarArrow.ORIENT_LEFT) {
            x = sb.getX();
            y = sb.getY();
        }
        else if (orientation == DvtScrollbarArrow.ORIENT_RIGHT) {
            x = sb.getX() + sb.getWidth() - DvtScrollbarArrow.WIDTH;
            y = sb.getY();
        }
        w = DvtScrollbarArrow.WIDTH;
        h = sb.getHeight();
    }
    else {
        if (orientation == DvtScrollbarArrow.ORIENT_DOWN) {
            x = sb.getX();
            y = sb.getY() + sb.getHeight() - DvtScrollbarArrow.WIDTH;
        }
        else if (orientation == DvtScrollbarArrow.ORIENT_UP) {
            x = sb.getX();
            y = sb.getY();
        }
        w = sb.getWidth();
        h = DvtScrollbarArrow.WIDTH;

    }

    // initialize the underlying rect
    var context = sb.getCtx();
    DvtScrollbarArrow.superclass.Init.call(this, context);
    this._container = new DvtRect(context, x, y, w, h, null);

    this._arrowUpBorderColor = DvtScrollbarArrow.ARROW_UP_BORDER;
    this._arBGUpFillColors   = DvtScrollbarArrow.BG_UP_FILL_COLORS;

    this._arrowOverBorderColor = DvtScrollbarArrow.ARROW_OVER_BORDER;
    this._arBGOverFillColors   = DvtScrollbarArrow.BG_OVER_FILL_COLORS;

    this._arrowDownBorderColor = DvtScrollbarArrow.ARROW_DOWN_BORDER;
    this._arBGDownFillColors   = DvtScrollbarArrow.BG_DOWN_FILL_COLORS;

    this._upFill = new DvtLinearGradientFill(0, this._arBGUpFillColors, DvtScrollbarArrow.BG_UP_FILL_ALPHAS);
    this._overFill = new DvtLinearGradientFill(0, this._arBGOverFillColors, DvtScrollbarArrow.BG_OVER_FILL_ALPHAS);
    this._downFill = new DvtLinearGradientFill(0, this._arBGDownFillColors, DvtScrollbarArrow.BG_DOWN_FILL_ALPHAS);

    this._arrowUpFill = new DvtSolidFill(DvtScrollbarArrow.ARROW_UP_FILL, null);
    this._arrowOverFill = new DvtSolidFill(DvtScrollbarArrow.ARROW_OVER_FILL, null);
    this._arrowDownFill = new DvtSolidFill(DvtScrollbarArrow.ARROW_DOWN_FILL, null);

    this._arrowUpBorder = new DvtSolidStroke(DvtScrollbarArrow.ARROW_UP_BORDER, null, DvtScrollbarArrow.ARROW_UP_BORDER_ALPHA);
    this._arrowOverBorder = new DvtSolidStroke(DvtScrollbarArrow.ARROW_OVER_BORDER, null, DvtScrollbarArrow.ARROW_OVER_BORDER_ALPHA);
    this._arrowDownBorder = new DvtSolidStroke(DvtScrollbarArrow.ARROW_DOWN_BORDER, null, DvtScrollbarArrow.ARROW_DOWN_BORDER_ALPHA);

    this._arrow = this._createArrow();
    
    this._container.setPixelHinting(true);
    
    if (!DvtAgent.isTouchDevice()) {
        this.addEvtListener(DvtMouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
        this.addEvtListener(DvtMouseEvent.MOUSEOUT, this._mouseOutHandler, false, this);
        this.addEvtListener(DvtMouseEvent.MOUSEDOWN, this._mouseDownHandler, false, this);
        this.addEvtListener(DvtMouseEvent.MOUSEUP, this._mouseUpHandler, false, this);
    } else {
        this.addEvtListener(DvtTouchEvent.TOUCHSTART, this._touchHandler, false, this);
        this.addEvtListener(DvtTouchEvent.TOUCHMOVE, this._touchHandler, false, this);
        this.addEvtListener(DvtTouchEvent.TOUCHEND, this._touchHandler, false, this);
    }

    this._container.addChild(this._arrow);
    this.addChild(this._container);
    this._drawOutState();

}

DvtScrollbarArrow.prototype.getOrientation = function() {
    return this._orientation;
}

DvtScrollbarArrow.prototype._createArrow = function() {

    var centerX = this._container._impl.getX() + this._container._impl.getWidth() / 2;
    var centerY = this._container._impl.getY() + this._container._impl.getHeight() / 2;
    var aPoints = new Array();

    if (this._orientation == DvtScrollbarArrow.ORIENT_RIGHT) {
        aPoints.push(centerX - 2, centerY - 3);
        aPoints.push(centerX + 2, centerY);
        aPoints.push(centerX - 2, centerY + 3);
        aPoints.push(aPoints[0], aPoints[1]);
    }
    else if (this._orientation == DvtScrollbarArrow.ORIENT_UP) {
        aPoints.push(centerX - 3, centerY + 2);
        aPoints.push(centerX, centerY - 2);
        aPoints.push(centerX + 3, centerY + 2);
        aPoints.push(aPoints[0], aPoints[1]);
    }
    else if (this._orientation == DvtScrollbarArrow.ORIENT_LEFT) {
        aPoints.push(centerX + 2, centerY - 3);
        aPoints.push(centerX - 2, centerY);
        aPoints.push(centerX + 2, centerY + 3);
        aPoints.push(aPoints[0], aPoints[1]);
    }
    else if (this._orientation == DvtScrollbarArrow.ORIENT_DOWN) {
        aPoints.push(centerX - 3, centerY - 2);
        aPoints.push(centerX, centerY + 2);
        aPoints.push(centerX + 3, centerY - 2);
        aPoints.push(aPoints[0], aPoints[1]);
    }
    
    var shape = new DvtPolygon(this.getCtx(), aPoints, null);
    shape.setFill(this._arrowUpFill);
    shape.setStroke(this._arrowUpBorder);
    shape.setMouseEnabled(false);

    return shape;    
}

// Mouse event handling

DvtScrollbarArrow.prototype._mouseOverHandler = function(event) {
    this._drawOverState();
    this._disabled = false;
    
    if (event)
        event.preventDefault();
}

DvtScrollbarArrow.prototype._mouseOutHandler = function(event) {
    this._drawOutState();
    this._disabled = true;
    if (event)
        event.preventDefault();
}

DvtScrollbarArrow.prototype._mouseDownHandler = function(event) {
    this._drawDownState();
    this._sb.handleArrowDown(this, event);
    
    if (event)
        event.preventDefault();
}

DvtScrollbarArrow.prototype._mouseUpHandler = function(event) {
    this._drawOverState();
    this._sb.handleArrowUp(this, event);

    if (event)
        event.preventDefault();
}

DvtScrollbarArrow.prototype._touchHandler = function(event) {
    var type = event.type;
    if (type == DvtTouchEvent.TOUCHSTART) {
        if (!this._dragStarted) {
            var touch = event.targetTouches[0];
            if (touch) {
                this._sb.processArrowDown(this);
                this._dragStarted = true;
                this._touchId = touch.identifier;
            }
        }
    } else if (type == DvtTouchEvent.TOUCHMOVE) {
        if (this._dragStarted) { 
            var touch = DvtTouchManager.getTouchById(this._touchId, event.touches);
            if (touch) {
            }
        }
    } else if (type == DvtTouchEvent.TOUCHEND) {
        if (this._dragStarted) {
            var touch = DvtTouchManager.getTouchById(this._touchId, event.changedTouches);
            if (touch) {
                this._sb.processArrowUp(this);

                this._dragStarted = false;
                this._touchId = null;
            }
        }
    }
    event.blockTouchHold();
    event.preventDefault();
}

DvtScrollbarArrow.prototype._drawOutState = function() {
    this._container.setFill(this._upFill);
    this._arrow.setStroke(this._arrowUpBorder);
    this._arrow.setFill(this._arrowUpFill);
}

DvtScrollbarArrow.prototype._drawOverState = function() {
    this._container.setFill(this._overFill);
    this._arrow.setStroke(this._arrowOverBorder);
    this._arrow.setFill(this._arrowOverFill);
}

DvtScrollbarArrow.prototype._drawDownState = function() {
    this._container.setFill(this._downFill);
    this._arrow.setStroke(this._arrowDownBorder);
    this._arrow.setFill(this._arrowDownFill);
}

DvtScrollbarArrow.prototype.isDisabled = function() {
    return this._disabled;
}

/**
 * @override
 */
DvtScrollbarArrow.prototype.destroy = function () {
  //Bug 16893087 - HTML5 HV: SEARCH RESULTS SCROLLBAR TRAPS MOUSE
  if (DvtAgent.isTouchDevice()) {
    this.removeEvtListener(DvtTouchEvent.TOUCHSTART, this._touchHandler, false, this);
    this.removeEvtListener(DvtTouchEvent.TOUCHMOVE, this._touchHandler, false, this);
    this.removeEvtListener(DvtTouchEvent.TOUCHEND, this._touchHandler, false, this);
  }
  else {
    this.removeEvtListener(DvtMouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
    this.removeEvtListener(DvtMouseEvent.MOUSEOUT, this._mouseOutHandler, false, this);
    this.removeEvtListener(DvtMouseEvent.MOUSEDOWN, this._mouseDownHandler, false, this);
    this.removeEvtListener(DvtMouseEvent.MOUSEUP, this._mouseUpHandler, false, this);
  }

  if (this._container) {
    if (this._arrow) {
      this._container.removeChild(this._arrow);
      this._arrow = null;
    }
  }

  DvtScrollbarArrow.superclass.destroy.call(this);
  this._container = null;
}

/*--------------------------------------------------------------------*/
/*   DvtScrollbarBackground          Scrollbar background             */
/*--------------------------------------------------------------------*/
/**
  *  DvtScrollbarBackground subcomponent
  *  @extends DvtContainer
  *  @class DvtScrollbarBackground  a subcomponent created and used
  *  by DvtScrollbar
  *  <p>
  *  @constructor
  */

var   DvtScrollbarBackground = function(sb) {
    this.Init(sb);
}

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

// Constants
//
DvtScrollbarBackground.BORDER_COLOR = "rgb(206,211,226)";
DvtScrollbarBackground.FILL_COLORS = [ '#fafafb', '#e7e9ef' ];
/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    @private
 */
DvtScrollbarBackground.prototype.Init = function(sb)
{
    var x, y, w, h;
    this._sb = sb;

    x = sb.getX();
    y = sb.getY();
    w = sb.getWidth();
    h = sb.getHeight();

    // create and initialize the underlying rect
    var context = sb.getCtx();
    DvtScrollbarBackground.superclass.Init.call(this, context);
    this._container = new DvtRect(context, x, y, w, h, null) ;
    this.addChild(this._container);
    
    var fill; 
    var styleMap = sb.getStyleMap();
    var background;
    var borderColor;
    if (styleMap) {
      background = styleMap[DvtScrollbar.BACKGROUND];
      borderColor = styleMap[DvtScrollbar.BORDER_COLOR];
      if (background) {
        if (DvtColorUtils.isColor(background))
          fill = new DvtSolidFill(background);
      } else  {
          var grad = DvtGradientParser.parseCSSGradient(background);
          if (grad)
            fill = new DvtLinearGradientFill(grad.getAngle(), grad.getColors(), grad.getAlphas(), grad.getRatios());
      }
    }
    
    if (!fill) {
      var gradientAngle;
      if (sb.isHorizontal())
        gradientAngle = 90;
      else
        gradientAngle = 180;
      fill = new DvtLinearGradientFill(gradientAngle, DvtScrollbarBackground.FILL_COLORS);
    }
    
    this._container.setSolidStroke(borderColor ? borderColor : DvtScrollbarBackground.BORDER_COLOR);
    this._container.setFill(fill);

    this._container.setPixelHinting(true);
    
    if (!DvtAgent.isTouchDevice()) {
        this.addEvtListener(DvtMouseEvent.CLICK, this._mouseClickHandler, false, this);
    } else {
        this.addEvtListener(DvtTouchEvent.TOUCHSTART, this._touchHandler, false, this);
        this.addEvtListener(DvtTouchEvent.TOUCHMOVE, this._touchHandler, false, this);
        this.addEvtListener(DvtTouchEvent.TOUCHEND, this._touchHandler, false, this);
    }
}

DvtScrollbarBackground.prototype._touchHandler = function(event) {
    var type = event.type;
    if (type == DvtTouchEvent.TOUCHSTART) {
        if (!this._backgroundClickStarted) {
            var touch = event.targetTouches[0];
            if (touch) {
                this._backgroundClickStarted = true;
                this._touchId = touch.identifier;
            }
        }
    } else if (type == DvtTouchEvent.TOUCHMOVE) {
    } else if (type == DvtTouchEvent.TOUCHEND) {
        if (this._backgroundClickStarted) {
            var touch = DvtTouchManager.getTouchById(this._touchId, event.changedTouches);
            if (touch) {
                var pos = this.getCtx().getRelativePosition(touch.pageX, touch.pageY);
                var point = new DvtPoint(pos.x, pos.y);
                this._sb.processBackgroundClick(point);

                this._backgroundClickStarted = false;
                this._touchId = null;
            }
        }
    }
    event.blockTouchHold();
    event.preventDefault();
}

DvtScrollbarBackground.prototype._mouseClickHandler = function(event) {
    var pos =  this.getCtx().getRelativePosition(event._event.pageX, event._event.pageY);
    var point = new DvtPoint(pos.x, pos.y);
    this._sb.processBackgroundClick(point);

    if (event)
        event.preventDefault();
}

/**
 * @override
 */
DvtScrollbarBackground.prototype.destroy = function () {
  //Bug 16893087 - HTML5 HV: SEARCH RESULTS SCROLLBAR TRAPS MOUSE
  if (DvtAgent.isTouchDevice()) {
    this.removeEvtListener(DvtTouchEvent.TOUCHSTART, this._touchHandler, false, this);
    this.removeEvtListener(DvtTouchEvent.TOUCHMOVE, this._touchHandler, false, this);
    this.removeEvtListener(DvtTouchEvent.TOUCHEND, this._touchHandler, false, this);
  }
  else {
    this.removeEvtListener(DvtMouseEvent.CLICK, this._mouseClickHandler, false, this);
  }

  DvtScrollbarBackground.superclass.destroy.call(this);
  this._container = null;

}

/*--------------------------------------------------------------------*/
/*   DvtScrollbarGripper          Scrollbar gripper                   */
/*--------------------------------------------------------------------*/
/**
  *  DvtScrollbarGripper subcomponent
  *  @extends DvtContainer
  *  @class DvtScrollbarGripper  a subcomponent created and used
  *  by DvtScrollbarGripper
  *  <p>
  *  @constructor
  */

var   DvtScrollbarGripper = function(thumb, type) {
    this.Init(thumb, type);
}

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

// Constants
//
DvtScrollbarGripper.TYPE_LEFT   = 0;
DvtScrollbarGripper.TYPE_RIGHT  = 1;
DvtScrollbarGripper.TYPE_TOP    = 2;
DvtScrollbarGripper.TYPE_BOTTOM = 3;

DvtScrollbarGripper.SIZE_GRIPPY = 6;
DvtScrollbarGripper.SIZE_NAIL   = 2;

DvtScrollbarGripper.GRIPPY_UP_BORDER_COLOR = '#097bf8';
DvtScrollbarGripper.GRIPPY_UP_FILL_COLORS = ['#0477f8', '#68befe'];
DvtScrollbarGripper.GRIPPY_UP_FILL_ALPHAS = [1, 1];
DvtScrollbarGripper.GRIPPY_UP_FILL_RATIOS = [0, 255];

DvtScrollbarGripper.NAIL_UP_BORDER_COLOR = '#097bf8';
DvtScrollbarGripper.NAIL_UP_FILL_COLORS = ['#1280f8', '#c1ddfc'];
DvtScrollbarGripper.NAIL_UP_FILL_ALPHAS = [1, 1];
DvtScrollbarGripper.NAIL_UP_FILL_RATIOS = [0, 255];

DvtScrollbarGripper.GRIPPY_OVER_BORDER_COLOR = DvtScrollbarGripper.GRIPPY_UP_BORDER_COLOR;
DvtScrollbarGripper.GRIPPY_OVER_FILL_COLORS = DvtScrollbarGripper.GRIPPY_UP_FILL_COLORS;
DvtScrollbarGripper.GRIPPY_OVER_FILL_ALPHAS = DvtScrollbarGripper.GRIPPY_UP_FILL_ALPHAS;
DvtScrollbarGripper.GRIPPY_OVER_FILL_RATIOS = DvtScrollbarGripper.GRIPPY_UP_FILL_RATIOS;

DvtScrollbarGripper.NAIL_OVER_BORDER_COLOR = DvtScrollbarGripper.NAIL_UP_BORDER_COLOR;
DvtScrollbarGripper.NAIL_OVER_FILL_COLORS = DvtScrollbarGripper.NAIL_UP_FILL_COLORS;
DvtScrollbarGripper.NAIL_OVER_FILL_ALPHAS = DvtScrollbarGripper.NAIL_UP_FILL_ALPHAS;
DvtScrollbarGripper.NAIL_OVER_FILL_RATIOS = DvtScrollbarGripper.NAIL_UP_FILL_RATIOS;

DvtScrollbarGripper.GRIPPY_TOUCH_BORDER_COLOR = 'c2cadb';
DvtScrollbarGripper.GRIPPY_TOUCH_FILL_COLORS = ['#ffffff', '#c2cadb'];
DvtScrollbarGripper.GRIPPY_TOUCH_FILL_ALPHAS = DvtScrollbarGripper.GRIPPY_UP_FILL_ALPHAS;
DvtScrollbarGripper.GRIPPY_TOUCH_FILL_RATIOS = DvtScrollbarGripper.GRIPPY_UP_FILL_RATIOS;

DvtScrollbarGripper.NAIL_TOUCH_BORDER_COLOR = 'c2cadb';
DvtScrollbarGripper.NAIL_TOUCH_FILL_COLORS = ['#ffffff', '#c2cadb'];
DvtScrollbarGripper.NAIL_TOUCH_FILL_ALPHAS = DvtScrollbarGripper.NAIL_UP_FILL_ALPHAS;
DvtScrollbarGripper.NAIL_TOUCH_FILL_RATIOS = DvtScrollbarGripper.NAIL_UP_FILL_RATIOS;

DvtScrollbarGripper.GRIPPY_DOWN_BORDER_COLOR = '#097bf8';
DvtScrollbarGripper.GRIPPY_DOWN_FILL_COLORS = ['#3595fe', '#034ea0'];
DvtScrollbarGripper.GRIPPY_DOWN_FILL_ALPHAS = [1, 1];
DvtScrollbarGripper.GRIPPY_DOWN_FILL_RATIOS = [0, 255];

DvtScrollbarGripper.NAIL_DOWN_BORDER_COLOR = '#097bf8';
DvtScrollbarGripper.NAIL_DOWN_FILL_COLORS = ['#ffffff', '#bfdefd'];
DvtScrollbarGripper.NAIL_DOWN_FILL_ALPHAS = [1, 1];
DvtScrollbarGripper.NAIL_DOWN_FILL_RATIOS = [0, 255];

DvtScrollbarGripper.TOUCH_WIDTH = 28;
DvtScrollbarGripper.TOUCH_HEIGHT = 25;

/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    @protected
 */
DvtScrollbarGripper.prototype.Init = function(thumb, type)
{
    var x, y, w, h;
    this._thumb = thumb;    // parent
    this._type = type;

    var gradientAngle = 0;
    var nailGradientAngle = 0;

    if (thumb.isHorizontal()) {
        y = thumb.getY();
        w = DvtScrollbarGripper.SIZE_GRIPPY + DvtScrollbarGripper.SIZE_NAIL;
        h = thumb.getHeight();
        if (type == DvtScrollbarGripper.TYPE_LEFT)
            x = thumb.getX();
        else if (type == DvtScrollbarGripper.TYPE_RIGHT)
            x = thumb.getX() + thumb.getWidth() - w;

        gradientAngle = 270;
        nailGradientAngle = 90;
    }
    else {
        x = thumb.getX();
        w = thumb.getWidth();
        h = DvtScrollbarGripper.SIZE_GRIPPY + DvtScrollbarGripper.SIZE_NAIL;
        if (type == DvtScrollbarGripper.TYPE_TOP)
            y = thumb.getY();
        else if (type == DvtScrollbarGripper.TYPE_BOTTOM)
            y = thumb.getY() + thumb.getHeight() - h;

        nailGradientAngle = 180;
    }

    // create and initialize the underlying rect
    var context = thumb.getCtx();
    DvtScrollbarGripper.superclass.Init.call(this, context);
    this._container = new DvtRect(context, x, y, w, h, null) ;

    this._grippyUpBorderColor   = DvtScrollbarGripper.GRIPPY_UP_BORDER_COLOR;
    this._grippyUpFillColors    = DvtScrollbarGripper.GRIPPY_UP_FILL_COLORS;

    this._nailUpBorderColor     = DvtScrollbarGripper.NAIL_UP_BORDER_COLOR;
    this._nailUpFillColors      = DvtScrollbarGripper.NAIL_UP_FILL_COLORS;

    this._grippyOverBorderColor = DvtScrollbarGripper.GRIPPY_OVER_BORDER_COLOR;
    this._grippyOverFillColors  = DvtScrollbarGripper.GRIPPY_OVER_FILL_COLORS;

    this._grippyTouchBorderColor = DvtScrollbarGripper.GRIPPY_TOUCH_BORDER_COLOR;
    this._grippyTouchFillColors  = DvtScrollbarGripper.GRIPPY_TOUCH_FILL_COLORS;

    this._nailOverBorderColor   = DvtScrollbarGripper.NAIL_OVER_BORDER_COLOR;
    this._nailOverFillColors    = DvtScrollbarGripper.NAIL_OVER_FILL_COLORS;
    
    this._nailTouchBorderColor   = DvtScrollbarGripper.NAIL_TOUCH_BORDER_COLOR;
    this._nailTouchFillColors    = DvtScrollbarGripper.NAIL_TOUCH_FILL_COLORS;
    
    this._grippyDownBorderColor = DvtScrollbarGripper.GRIPPY_DOWN_BORDER_COLOR;
    this._grippyDownFillColors  = DvtScrollbarGripper.GRIPPY_DOWN_FILL_COLORS;

    this._nailDownBorderColor   = DvtScrollbarGripper.NAIL_DOWN_BORDER_COLOR;
    this._nailDownFillColors    = DvtScrollbarGripper.NAIL_DOWN_FILL_COLORS;
    
    this._upStroke = new DvtSolidStroke(this._grippyUpBorderColor, null, 1);
    this._upFill = new DvtLinearGradientFill(gradientAngle, this._grippyUpFillColors, DvtScrollbarGripper.GRIPPY_UP_FILL_ALPHAS);

    this._nailUpStroke = new DvtSolidStroke(this._nailUpBorderColor, null, 1);
    this._nailUpFill = new DvtLinearGradientFill(nailGradientAngle, this._nailUpFillColors, DvtScrollbarGripper.NAIL_UP_FILL_ALPHAS);

    this._overStroke = new DvtSolidStroke(this._grippyOverBorderColor, null, 1);
    this._overFill = new DvtLinearGradientFill(gradientAngle, this._grippyOverFillColors, DvtScrollbarGripper.GRIPPY_OVER_FILL_ALPHAS);

    this._touchStroke = new DvtSolidStroke(this._grippyTouchBorderColor, null, 1);
    this._touchFill = new DvtLinearGradientFill(gradientAngle, this._grippyTouchFillColors, DvtScrollbarGripper.GRIPPY_TOUCH_FILL_ALPHAS);

    this._nailOverStroke = new DvtSolidStroke(this._nailOverBorderColor, null, 1);
    this._nailOverFill = new DvtLinearGradientFill(nailGradientAngle, this._nailOverFillColors, DvtScrollbarGripper.NAIL_OVER_FILL_ALPHAS);

    this._touchStroke = new DvtSolidStroke(this._grippyTouchBorderColor, null, 1);
    this._touchFill = new DvtLinearGradientFill(gradientAngle, this._grippyTouchFillColors, DvtScrollbarGripper.GRIPPY_OVER_FILL_ALPHAS);

    this._nailTouchStroke = new DvtSolidStroke(this._nailTouchBorderColor, null, 1);
    this._nailTouchFill = new DvtLinearGradientFill(nailGradientAngle, this._nailTouchFillColors, DvtScrollbarGripper.NAIL_TOUCH_FILL_ALPHAS);

    this._downStroke = new DvtSolidStroke(this._grippyDownBorderColor, null, 1);
    this._downFill = new DvtLinearGradientFill(gradientAngle, this._grippyDownFillColors, DvtScrollbarGripper.GRIPPY_DOWN_FILL_ALPHAS);

    this._nailDownStroke = new DvtSolidStroke(this._nailDownBorderColor, null, 1);
    this._nailDownFill = new DvtLinearGradientFill(nailGradientAngle, this._nailDownFillColors, DvtScrollbarGripper.NAIL_DOWN_FILL_ALPHAS);

    if (!DvtAgent.isTouchDevice()) {
        this.addEvtListener(DvtMouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
        this.addEvtListener(DvtMouseEvent.MOUSEOUT, this._mouseOutHandler, false, this);
        this.addEvtListener(DvtMouseEvent.MOUSEDOWN, this._mouseDownHandler, false, this);
    } else {    
        this.addEvtListener(DvtTouchEvent.TOUCHSTART, this._touchHandler, false, this);
        this.addEvtListener(DvtTouchEvent.TOUCHMOVE, this._touchHandler, false, this);
        this.addEvtListener(DvtTouchEvent.TOUCHEND, this._touchHandler, false, this);
    }
    
    this._grippy = this._createGrippy();
    var grippyBounds = new DvtRectangle(this._grippy.getX(), this._grippy.getY(), this._grippy.getWidth(), this._grippy.getHeight());
    
    if (DvtAgent.isTouchDevice()) {
        this._touchArea = new DvtRect(this.getCtx(), grippyBounds.x - (DvtScrollbarGripper.TOUCH_WIDTH - grippyBounds.w)/2, grippyBounds.y - (DvtScrollbarGripper.TOUCH_HEIGHT - grippyBounds.h)/2, DvtScrollbarGripper.TOUCH_WIDTH, DvtScrollbarGripper.TOUCH_HEIGHT, null);  
        this._touchArea.setInvisibleFill();
        this._container.addChild(this._touchArea);
    }
    
    this._container.addChild(this._grippy);

    this.addChild(this._container);
    
    if (!DvtAgent.isTouchDevice()) {
        this._drawOutState();
    } else {
        this._drawOverState();
    }
    
    this._container.setPixelHinting(true);
    this._grippy.setPixelHinting(true);
    this._grippy.setMouseEnabled(false);
    DvtGraphSysUtils.makeDetObject(this._container);
    
    if (this._thumb.isHorizontal())
        this._container.getImpl().getElem().setAttributeNS(null, "cursor", "e-resize"); // TODO Remove SVG Hack
    else
        this._container.getImpl().getElem().setAttributeNS(null, "cursor", "n-resize"); // TODO Remove SVG Hack
}

/**
 * Processes "touchstart" event
 * @param {DvtTouchEvent} event Touch event
 * @param {DvtTouch} touch Touch object
 */
DvtScrollbarGripper.prototype.immediateTouchAttempted = function(event, touch) {
    var currentGrouping = this._thumb._sb;
    var tm = this.getTouchManager();
    var point = this.getCtx().getRelativePosition(touch.pageX, touch.pageY);
    this._thumb.processGripDragStart(this, point);
    tm.saveProcessedTouchDefault(touch.identifier, this, this._container, currentGrouping, "gripper", this);
    this._thumb.drawFullHighlightState();
    event.blockTouchHold();
}

/**
 * Processes "touchmove" event
 * @param {DvtTouchEvent} event Touch event
 * @param {DvtTouch} touch Touch object
 */
DvtScrollbarGripper.prototype.immediateTouchMoved = function(event, touch) {
    var point = this.getCtx().getRelativePosition(touch.pageX, touch.pageY);
    this._thumb.processGripDragMove(this, point);
    event.preventDefault();
}

/**
 * Processes "touchend" event
 * @param {DvtTouchEvent} event Touch event
 * @param {DvtTouch} touch Touch object
 */
DvtScrollbarGripper.prototype.immediateTouchEnded = function(event, touch) {
    if (!this.getTouchManager().containsGrouping(this._thumb._sb)) {
        this._thumb.drawFullPlainState();
    }
    this._thumb.processGripDragEnd();
}

/**
 * Handler for the touch event
 * @param {DvtTouchEvent} event Touch event
 * @private
 */
DvtScrollbarGripper.prototype._touchHandler = function(event) {
  this.getTouchManager().preEventBubble(event);
  this.getTouchManager().processAssociatedTouchDefault(event, this);
  this.getTouchManager().postEventBubble(event);
}

/*--------------------------------------------------------------------*/
/*  getType()                                                        */
/*--------------------------------------------------------------------*/
DvtScrollbarGripper.prototype.getType = function() {
    return this._type;
}


DvtScrollbarGripper.prototype._createGrippy = function() {
    var x, y, w, h;

    switch(this._type) {
        case DvtScrollbarGripper.TYPE_LEFT:
            x = this.getX() + DvtScrollbarGripper.SIZE_NAIL;
            y = this.getY();
            w = DvtScrollbarGripper.SIZE_GRIPPY;
            h = this.getHeight();
            break;

        case DvtScrollbarGripper.TYPE_RIGHT:
            x = this.getX();
            y = this.getY();
            w = DvtScrollbarGripper.SIZE_GRIPPY;
            h = this.getHeight();
            break;

        case DvtScrollbarGripper.TYPE_TOP:
            x = this.getX();
            y = this.getY() + DvtScrollbarGripper.SIZE_NAIL;
            w = this.getWidth();
            h = DvtScrollbarGripper.SIZE_GRIPPY;
            break;

        case DvtScrollbarGripper.TYPE_BOTTOM:
            x = this.getX();
            y = this.getY();
            w = this.getWidth();
            h = DvtScrollbarGripper.SIZE_GRIPPY;
            break;

        default:
            break;
    }

    var grippy = new DvtRect(this.getCtx(), x, y, w, h, null);

    return grippy;
}


DvtScrollbarGripper.prototype.updatePosition = function() {
    if (this._type == DvtScrollbarGripper.TYPE_LEFT) {
        this.setX(this._thumb.getX());
        this._grippy.setX(this.getX() + DvtScrollbarGripper.SIZE_NAIL);
        if (this._touchArea)
            this._touchArea.setX(this._grippy.getX() - (DvtScrollbarGripper.TOUCH_WIDTH - this._grippy.getWidth())/2);
    }
    else if (this._type == DvtScrollbarGripper.TYPE_RIGHT) {
        this.setX(this._thumb.getX() + this._thumb.getWidth() - DvtScrollbarGripper.SIZE_GRIPPY - DvtScrollbarGripper.SIZE_NAIL);
        this._grippy.setX(this.getX());
        if (this._touchArea)
            this._touchArea.setX(this._grippy.getX() - (DvtScrollbarGripper.TOUCH_WIDTH - this._grippy.getWidth())/2);
    }
    else if (this._type == DvtScrollbarGripper.TYPE_TOP) {
        this.setY(this._thumb.getY());
        this._grippy.setY(this.getY() + DvtScrollbarGripper.SIZE_NAIL);
        if (this._touchArea)
            this._touchArea.setY(this._grippy.getY() - (DvtScrollbarGripper.TOUCH_HEIGHT - this._grippy.getHeight())/2);
    }
    else if (this._type == DvtScrollbarGripper.TYPE_BOTTOM) {
        this.setY(this._thumb.getY() + this._thumb.getHeight() - DvtScrollbarGripper.SIZE_GRIPPY - DvtScrollbarGripper.SIZE_NAIL);
        this._grippy.setY(this.getY());
        if (this._touchArea)
            this._touchArea.setY(this._grippy.getY() - (DvtScrollbarGripper.TOUCH_HEIGHT - this._grippy.getHeight())/2);
    }
}


DvtScrollbarGripper.prototype.setAlpha = function(alpha) {
    // call stock implementation
    this._container.setAlpha(alpha);
    // show/hide the grippy
    if (alpha <= 0.001)
        this._grippy.setVisible(false);
    else 
        this._grippy.setVisible(true);
}

// Mouse event handlers

DvtScrollbarGripper.prototype._mouseOverHandler = function(event) {
    this._drawOverState();

    if (event)
        event.preventDefault();
}


DvtScrollbarGripper.prototype._mouseOutHandler = function(event) {
    this._drawOutState();

    if (event)
        event.preventDefault();
}

DvtScrollbarGripper.prototype._mouseDownHandler = function(event) {
    // remove 'mouseover' and 'mouseout' listeners while dragging
    this.removeEvtListener(DvtMouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
    this.removeEvtListener(DvtMouseEvent.MOUSEOUT, this._mouseOutHandler, false, this);

    this._thumb.handleGripDragStart(this, event);
    this._drawDownState();
    
    // add drag tracking listeners, listen everywhere
    this.getCtx().getStage().addEvtListener(DvtMouseEvent.MOUSEMOVE, this._mouseMoveHandler, false, this);
    this.getCtx().getStage().addEvtListener(DvtMouseEvent.MOUSEUP, this._mouseUpHandler, false, this);

    if (event) {
        event.preventDefault();
        event.stopPropagation();
    }
}


DvtScrollbarGripper.prototype._mouseMoveHandler = function(event) {
    this._thumb.handleGripDragMove(this, event);

    if (event)
        event.preventDefault();
}


DvtScrollbarGripper.prototype._mouseUpHandler = function(event) {
    // remove drag tracking listeners
    this.getCtx().getStage().removeEvtListener(DvtMouseEvent.MOUSEMOVE, this._mouseMoveHandler, false, this);
    this.getCtx().getStage().removeEvtListener(DvtMouseEvent.MOUSEUP, this._mouseUpHandler, false, this);

    // restore 'mouseover' and 'mouseout' listeners
    this.addEvtListener(DvtMouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
    this.addEvtListener(DvtMouseEvent.MOUSEOUT, this._mouseOutHandler, false, this);
    
    this._drawUpState();
    this._thumb.handleGripDragEnd(this, event);

    if (event)
        event.preventDefault();
}

DvtScrollbarGripper.prototype.drawHighlightState = function() {
    this.setStroke(this._nailOverStroke);
    this.setFill(this._nailOverFill);

    this._grippy.setStroke(this._overStroke);
    this._grippy.setFill(this._overFill);
}

DvtScrollbarGripper.prototype.drawPlainState = function() {
    this.setStroke(this._nailTouchStroke);
    this.setFill(this._nailTouchFill);

    this._grippy.setStroke(this._touchStroke);
    this._grippy.setFill(this._touchFill);
}

DvtScrollbarGripper.prototype._drawOverState = function() {
    this.setAlpha(1);
    
    if (!DvtAgent.isTouchDevice()) {
        this.drawHighlightState();
    } else {
        this.drawPlainState();
    }
}

DvtScrollbarGripper.prototype._drawUpState = function() {
    this.setStroke(this._nailUpStroke);
    this.setFill(this._nailUpFill);

    this._grippy.setStroke(this._upStroke);
    this._grippy.setFill(this._upFill);
}

DvtScrollbarGripper.prototype._drawDownState = function() {
    this.setStroke(this._nailDownStroke);
    this.setFill(this._nailDownFill);

    this._grippy.setStroke(this._downStroke);
    this._grippy.setFill(this._downFill);
}

DvtScrollbarGripper.prototype._drawOutState = function() {
    this.setAlpha(0.001);
    
    this.setStroke(this._nailUpStroke);
    this.setFill(this._nailUpFill);

    this._grippy.setStroke(this._upStroke);
    this._grippy.setFill(this._upFill);
}

DvtScrollbarGripper.prototype.getContainer = function() {
    return this._container;
}

DvtScrollbarGripper.prototype.getX = function() {
    return this._container.getX();
}

DvtScrollbarGripper.prototype.getY = function() {
    return this._container.getY();
}

DvtScrollbarGripper.prototype.getWidth = function() {
    return this._container.getWidth();
}

DvtScrollbarGripper.prototype.getHeight = function() {
    return this._container.getHeight();
}

DvtScrollbarGripper.prototype.setX = function(val) {
    this._container.setX(val);
}

DvtScrollbarGripper.prototype.setY = function(val) {
    this._container.setY(val);
}

DvtScrollbarGripper.prototype.setStroke = function(stroke) {
    this._container.setStroke(stroke);
}

DvtScrollbarGripper.prototype.setSolidStroke = function(color, alpha, strokeWidth) {
    this._container.setSolidStroke(color, alpha, strokeWidth);
}

DvtScrollbarGripper.prototype.setFill = function(fill) {
    this._container.setFill(fill);
}

DvtScrollbarGripper.prototype.setSolidFill = function(color, alpha) {
    this._container.setSolidFill(color, alpha);
}

/**
 * @override
 */
DvtScrollbarGripper.prototype.destroy = function () {
  //Bug 16893087 - HTML5 HV: SEARCH RESULTS SCROLLBAR TRAPS MOUSE
  if (DvtAgent.isTouchDevice()) {
    this.removeEvtListener(DvtTouchEvent.TOUCHSTART, this._touchHandler, false, this);
    this.removeEvtListener(DvtTouchEvent.TOUCHMOVE, this._touchHandler, false, this);
    this.removeEvtListener(DvtTouchEvent.TOUCHEND, this._touchHandler, false, this);
  }
  else {
    this.removeEvtListener(DvtMouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
    this.removeEvtListener(DvtMouseEvent.MOUSEOUT, this._mouseOutHandler, false, this);
    this.removeEvtListener(DvtMouseEvent.MOUSEDOWN, this._mouseDownHandler, false, this);

    this.getCtx().getStage().removeEvtListener(DvtMouseEvent.MOUSEMOVE, this._mouseMoveHandler, false, this);
    this.getCtx().getStage().removeEvtListener(DvtMouseEvent.MOUSEUP, this._mouseUpHandler, false, this);
  }

  if (this._container) {
    if (this._grippy) {
      this._container.removeChild(this._grippy);
      this._grippy = null;
    }
  }

  DvtScrollbarGripper.superclass.destroy.call(this);
  this._container = null;

}

/**
 * Returns a touch manager object
 * @return {DvtTouchManager} touch manager object for the scrollbar gripper
 */
DvtScrollbarGripper.prototype.getTouchManager = function () {
  if (!this._touchManager && DvtAgent.isTouchDevice())
    this._touchManager = this._thumb.getTouchManager();
  return this._touchManager;
}
/*--------------------------------------------------------------------*/
/*   DvtScrollbarThumb          ScrollbarThumb button                 */
/*--------------------------------------------------------------------*/
/**
  *  DvtScrollbarThumb subcomponent
  *  @extends DvtContainer
  *  @class DvtScrollbarThumb  a subcomponent created and used 
  *  by DvtScrollbar
  *  <p>
  *  @constructor  
  */

var   DvtScrollbarThumb = function(sb, drawGrippers, skinName) {
    this.Init(sb, drawGrippers, skinName);
}

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

// Constants
//

DvtScrollbarThumb.UP_BORDER_COLOR       = '#c2cadb';
DvtScrollbarThumb.UP_FILL_COLORS        = ['#ffffff', '#e4eef8'];
DvtScrollbarThumb.UP_FILL_ALPHAS        = [1, 1];
DvtScrollbarThumb.UP_FILL_RATIOS        = [70, 255];

DvtScrollbarThumb.OVER_BORDER_COLOR     = '#097bf8';
DvtScrollbarThumb.OVER_FILL_COLORS      = ['#ffffff', '#bfdefd'];
DvtScrollbarThumb.OVER_FILL_ALPHAS      = [1, 1];
DvtScrollbarThumb.OVER_FILL_RATIOS      = [70, 255];

DvtScrollbarThumb.DOWN_BORDER_COLOR     = '#c2cadb';
DvtScrollbarThumb.DOWN_FILL_COLORS      = ['#ffffff', '#e4eef8'];
DvtScrollbarThumb.DOWN_FILL_ALPHAS      = [1, 1];
DvtScrollbarThumb.DOWN_FILL_RATIOS      = [70, 255];

DvtScrollbarThumb.GRIP_ZONE_COLOR       = '#b6bbca';

/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    @private
 */
DvtScrollbarThumb.prototype.Init = function(sb, drawGrippers, skinName)
{
    var x, y, w, h;
    this._sb = sb;
    this._drawGrippers = drawGrippers;
    this._skinName = skinName;

    if (sb.isHorizontal()) {
        x = sb.getX() + sb.getThumbStart();
        y = sb.getY();
        w = sb.getThumbLength();
        h = sb.getHeight();
    }
    else {
        x = sb.getX();
        y = sb.getY() + sb.getHeight() - sb.getThumbStart() - sb.getThumbLength();
        w = sb.getWidth();
        h = sb.getThumbLength();
    }

    // create and initialize the underlying rect
    var context = sb.getCtx();
    DvtScrollbarThumb.superclass.Init.call(this, context);
    this._container = new DvtRect(context, x, y, w, h, null) ;

    var angle = (this._sb.isHorizontal()) ? 270 : 0;

    var styleMap = sb.getStyleMap();
    if(this.IsSkinAlta() && styleMap) {
      this._upFill = new DvtSolidFill(styleMap[DvtScrollbar.HANDLE_COLOR]);
      this._overFill = new DvtSolidFill(styleMap[DvtScrollbar.HANDLE_HOVER_COLOR]);
      this._downFill = new DvtSolidFill(styleMap[DvtScrollbar.HANDLE_ACTIVE_COLOR]);
      this._upStroke = null;
      this._overStroke = null;
      this._downStroke = null;      
    }
    else {
      this._upFill = new DvtLinearGradientFill(angle, DvtScrollbarThumb.UP_FILL_COLORS, DvtScrollbarThumb.UP_FILL_ALPHAS);
      this._overFill = new DvtLinearGradientFill(angle, DvtScrollbarThumb.OVER_FILL_COLORS, DvtScrollbarThumb.OVER_FILL_ALPHAS);
      this._downFill = new DvtLinearGradientFill(angle, DvtScrollbarThumb.DOWN_FILL_COLORS, DvtScrollbarThumb.DOWN_FILL_ALPHAS);
      this._upStroke = new DvtSolidStroke(DvtScrollbarThumb.UP_BORDER_COLOR, null, 1);
      this._overStroke = new DvtSolidStroke(DvtScrollbarThumb.OVER_BORDER_COLOR, null, 1);
      this._downStroke = new DvtSolidStroke(DvtScrollbarThumb.DOWN_BORDER_COLOR, null, 1);
      this._grip = this._createGrip();
      this._container.addChild(this._grip);
      if (this._drawGrippers) {
        this._startGripper = this._createStartGripper();
        this._endGripper = this._createEndGripper();
        this._container.addChild(this._startGripper);
        this._container.addChild(this._endGripper);
      }
    } 

    if (!DvtAgent.isTouchDevice()) {
        this.addEvtListener(DvtMouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
        this.addEvtListener(DvtMouseEvent.MOUSEOUT, this._mouseOutHandler, false, this);
        this.addEvtListener(DvtMouseEvent.MOUSEDOWN, this._mouseDownHandler, false, this);
    } else {    
        this.addEvtListener(DvtTouchEvent.TOUCHSTART, this._touchHandler, false, this);
        this.addEvtListener(DvtTouchEvent.TOUCHMOVE, this._touchHandler, false, this);
        this.addEvtListener(DvtTouchEvent.TOUCHEND, this._touchHandler, false, this);
    }

    this._container.setPixelHinting(true);
    DvtGraphSysUtils.makeDetObject(this._container);
    this.addChild(this._container);
    
    var tooltipText = this._makeTooltipText();
    this._container._Dlo.setUseTooltip(true);
    this._container._Dlo.setTooltip(tooltipText);
    if (this._drawGrippers) {
      this._startGripper.getContainer()._Dlo.setUseTooltip(true);
      this._startGripper.getContainer()._Dlo.setTooltip(tooltipText);
      this._endGripper.getContainer()._Dlo.setUseTooltip(true);
      this._endGripper.getContainer()._Dlo.setTooltip(tooltipText);
    }
    
    if (!DvtAgent.isTouchDevice()) {
        this._drawUpState();
    } else {
        this._drawOverState();
    }

}

/*--------------------------------------------------------------------*/
/*  setChart()                                                    */
/*--------------------------------------------------------------------*/
DvtScrollbarThumb.prototype.setChart = function(chart) {
    this._Dlo._Chart = chart;
    this._startGripper._Dlo._Chart = chart;
    this._endGripper._Dlo._Chart = chart;
}

DvtScrollbarThumb.prototype._makeTooltipText = function() {

    var axis = this._sb.getAxis();
    if (axis == null)
        return "";

    var startCoord;
    var endCoord;
    var startLabel = "";
    var endLabel = "";
    
    var chartType = this._Dlo._Chart.getType();
    var isBarHoriz = chartType.indexOf("BAR_HORIZ") >= 0;
    
    if (this._sb.isHorizontal()) {
        // thumb start pos relative to the scrollbar
        startCoord = this.getX() - this._sb.getX();
        endCoord = startCoord + this.getWidth();
    }
    else {
        // Start is at the higher y position, end is at the lower except for vertical horizontal bar
        if (isBarHoriz) {
          startCoord = this.getY() - this._sb.getY();
          endCoord = startCoord + this.getHeight();

        } else {
          endCoord = this.getY() - this._sb.getY();
          startCoord = endCoord + this.getHeight();
        }
    }

    if (axis instanceof DvtDataAxis) {
        startLabel = axis.getLabelAt(startCoord);
        endLabel = axis.getLabelAt(endCoord);
    }
    else if (axis instanceof DvtTimeAxis) {
        startLabel = axis.getLabelAt(startCoord);
        endLabel   = axis.getLabelAt(endCoord);
    }
    else if (axis instanceof DvtCustomAxis) {
        var numLabels = axis.getNumLabels();
        var isAreaLine = chartType.indexOf("LINE") >= 0 || chartType.indexOf("AREA") >= 0;

        var firstIndex;
        var lastIndex;
        var incrSize;

        // Adjust to start of scrollbar container 
        var firstLabelCoord = startCoord - DvtScrollbarArrow.WIDTH;
        var lastLabelCoord = endCoord - DvtScrollbarArrow.WIDTH;

        if (isAreaLine || isBarHoriz) {
            incrSize = this._sb.getScrollAreaLength() / (numLabels - 1);
            firstIndex = Math.round(firstLabelCoord / incrSize);
            lastIndex = Math.round(lastLabelCoord / incrSize);
        } else {
            var buffer = 0.02;   // group boundary overflow tolerance
            incrSize = this._sb.getScrollAreaLength() / numLabels;
            
            var firstGroup = firstLabelCoord / incrSize;
            firstIndex = Math.floor(firstGroup);
            var lastGroup = lastLabelCoord / incrSize;
            lastIndex = Math.floor(lastGroup);

            // for last group, if at group boundary, then should display tooltip for preceding group
            // check if current position is slightly after target last group
            if ((lastGroup - lastIndex) < buffer && lastIndex > 0)
                lastIndex--;

            // check if current position is slightly before target start group
            // ignore if exactly on boundary
            var check = Math.ceil(firstGroup) - firstGroup;
            if (check > 0 && check < buffer && firstIndex > 0)
                firstIndex++;
        }

        if (DvtAgent.isRightToLeft(this._context) && ((axis instanceof DvtCustomAxis) == this._sb.isHorizontal())){
            startLabel = axis.getLabelAt(numLabels - firstIndex -1);
            endLabel = axis.getLabelAt(numLabels - lastIndex -1);
        }
        
        else{
            // startLabel is the first group included in the thumb's area
            startLabel = axis.getLabelAt(firstIndex);
            // endLabel is the last group included in the thumb's area
            endLabel = axis.getLabelAt(lastIndex);
        }
    }
    else
        return "";
   
    if (startLabel == endLabel)
        return startLabel;
    else if (DvtAgent.isRightToLeft(this._context) && this._sb.isHorizontal())
        return endLabel + " - " + startLabel;
    else 
        return startLabel + " - " + endLabel;
}

DvtScrollbarThumb.prototype.updateTooltipText = function() {
    var tooltipText = this._makeTooltipText();
    this._container._Dlo.setTooltip(tooltipText);
    if (this._drawGrippers) {
      this._startGripper.getContainer()._Dlo.setTooltip(tooltipText);
      this._endGripper.getContainer()._Dlo.setTooltip(tooltipText);
    }
}


DvtScrollbarThumb.prototype.IsSkinAlta = function() {
  return (this._skinName == "alta");
};

/**
 * Processes "touchstart" event
 * @param {DvtTouchEvent} event Touch event
 * @param {DvtTouch} touch Touch object
 */
DvtScrollbarThumb.prototype.immediateTouchAttempted = function(event, touch) {
    // grippers are children of the thumb so this check is needed
    if (this.getTouchManager().containsTouchId(touch.identifier)) {
        return;
    }
    var point = this.getCtx().getRelativePosition(touch.pageX, touch.pageY);
    this.processThumbDragStart(point);
    this.getTouchManager().saveProcessedTouchDefault(touch.identifier, this, this._container, this._sb, "thumb", this);

    this.drawFullHighlightState();

    event.blockTouchHold();
}

/**
 * Processes "touchmove" event
 * @param {DvtTouchEvent} event Touch event
 * @param {DvtTouch} touch Touch object
 */
DvtScrollbarThumb.prototype.immediateTouchMoved = function(event, touch) {
    var point = this.getCtx().getRelativePosition(touch.pageX, touch.pageY);
    this.processThumbDragMove(point);
    event.preventDefault();
}

/**
 * Processes "touchend" event
 * @param {DvtTouchEvent} event Touch event
 * @param {DvtTouch} touch Touch object
 */
DvtScrollbarThumb.prototype.immediateTouchEnded = function(event, touch) {
    if (!this.getTouchManager().containsGrouping(this._sb)) {
        this.drawFullPlainState();
    }
    var point = this.getCtx().getRelativePosition(touch.pageX, touch.pageY);
    this.processThumbDragEnd(point);
}

/**
 * Handler for the touch event
 * @param {DvtTouchEvent} event Touch event
 * @private
 */
DvtScrollbarThumb.prototype._touchHandler = function(event) {
  this.getTouchManager().preEventBubble(event);
  this.getTouchManager().processAssociatedTouchDefault(event, this);
  this.getTouchManager().postEventBubble(event);
}

DvtScrollbarThumb.prototype.updateSize = function() {
    var sb = this._sb;

    if (sb.isHorizontal()) {
        this.setX(sb.getX() + sb.getThumbStart());
        this.setWidth(sb.getThumbLength());
    }
    else {
        // this.setY(sb.getY() + sb.getHeight() - sb.getThumbStart() - sb.getThumbLength());
        this.setY(sb.getY() + sb.getThumbStart());
        this.setHeight(sb.getThumbLength());
    }
    
    if (this._drawGrippers) {
      this._startGripper.updatePosition();
      this._endGripper.updatePosition();
    }

    if (this._grip != null && this._container._findChild(this._grip) != -1) {
        this._container.removeChild(this._grip);
        this._grip = this._createGrip();
        this._container.addChild(this._grip);
    }
    else {
        this._grip = this._createGrip();
    }
}


DvtScrollbarThumb.prototype.isHorizontal = function() {
    return this._sb.isHorizontal();
}

DvtScrollbarThumb.prototype._createGrip = function() {

    var centerX = this.getX() + this.getWidth() / 2;
    var centerY = this.getY() + this.getHeight() / 2;
    var w = 3;      // half of grip width

    var gripCmds = "";

    if (this._sb.isHorizontal()) {
        gripCmds += DvtPathUtils.moveTo(centerX - 4.5, centerY - w);
        gripCmds += DvtPathUtils.lineTo(centerX - 4.5, centerY + w);
        gripCmds += DvtPathUtils.moveTo(centerX - 1.5, centerY - w);
        gripCmds += DvtPathUtils.lineTo(centerX - 1.5, centerY + w);
        gripCmds += DvtPathUtils.moveTo(centerX + 1.5, centerY - w);
        gripCmds += DvtPathUtils.lineTo(centerX + 1.5, centerY + w);
        gripCmds += DvtPathUtils.moveTo(centerX + 4.5, centerY - w);
        gripCmds += DvtPathUtils.lineTo(centerX + 4.5, centerY + w);
    }
    else {
        gripCmds += DvtPathUtils.moveTo(centerX - w, centerY - 4.5);
        gripCmds += DvtPathUtils.lineTo(centerX + w, centerY - 4.5);
        gripCmds += DvtPathUtils.moveTo(centerX - w, centerY - 1.5);
        gripCmds += DvtPathUtils.lineTo(centerX + w, centerY - 1.5);
        gripCmds += DvtPathUtils.moveTo(centerX - w, centerY + 1.5);
        gripCmds += DvtPathUtils.lineTo(centerX + w, centerY + 1.5);
        gripCmds += DvtPathUtils.moveTo(centerX - w, centerY + 4.5);
        gripCmds += DvtPathUtils.lineTo(centerX + w, centerY + 4.5);
    }

    DvtPathUtils.closePath();
    var shape = new DvtPath(this.getCtx(), gripCmds);
    shape.setSolidStroke(DvtScrollbarThumb.GRIP_ZONE_COLOR);
    shape.setMouseEnabled(false);

    return shape;    
}


DvtScrollbarThumb.prototype._createStartGripper = function() {
    var gripper;

    if (this.isHorizontal())
        gripper = new DvtScrollbarGripper(this, DvtScrollbarGripper.TYPE_LEFT);
    else
        gripper = new DvtScrollbarGripper(this, DvtScrollbarGripper.TYPE_TOP);

    DvtGraphSysUtils.makeDetObject(gripper);

    return gripper;
}


DvtScrollbarThumb.prototype._createEndGripper = function() {
    var gripper;

    if (this.isHorizontal())
        gripper = new DvtScrollbarGripper(this, DvtScrollbarGripper.TYPE_RIGHT);
    else
        gripper = new DvtScrollbarGripper(this, DvtScrollbarGripper.TYPE_BOTTOM);

    DvtGraphSysUtils.makeDetObject(gripper);

    return gripper;
}

// Mouse event handlers

DvtScrollbarThumb.prototype._mouseOverHandler = function(event) {
    this._drawOverState();
    if (event)
        event.preventDefault();
}


DvtScrollbarThumb.prototype._mouseOutHandler = function(event) {
    this._drawUpState();

    if (event)
        event.preventDefault();
}

/**
 * Handles a mouse down event for this scrollbar thumb.
 * @param {DvtMouseEvent} event The mouse event to handle
 * @private
 */
DvtScrollbarThumb.prototype._mouseDownHandler = function(event) {

    // remove 'mouseover' and 'mouseout' listeners while dragging
    this.removeEvtListener(DvtMouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
    this.removeEvtListener(DvtMouseEvent.MOUSEOUT, this._mouseOutHandler, false, this);

    var point = new DvtPoint(event.pageX, event.pageY);
    this.processThumbDragStart(point);
    this._drawDownState();

    // add drag tracking listeners, listen everywhere
    this.getCtx().getStage().addEvtListener(DvtMouseEvent.MOUSEMOVE, this._mouseMoveHandler, false, this);
    this.getCtx().getStage().addEvtListener(DvtMouseEvent.MOUSEUP, this._mouseUpHandler, false, this);

    if (event)
        event.preventDefault();
}

DvtScrollbarThumb.prototype.processThumbDragStart = function(point) {
    this._sb.processThumbDragStart(point);
}

/**
 * Handles a mouse up event for this scrollbar thumb.
 * @param {DvtMouseEvent} event The event to handle
 * @private
 */
DvtScrollbarThumb.prototype._mouseUpHandler = function(event) {
    // remove drag tracking listeners
    this.getCtx().getStage().removeEvtListener(DvtMouseEvent.MOUSEMOVE, this._mouseMoveHandler, false, this);
    this.getCtx().getStage().removeEvtListener(DvtMouseEvent.MOUSEUP, this._mouseUpHandler, false, this);
    
    var point = new DvtPoint(event.pageX, event.pageY);
    this.processThumbDragEnd(point);
    this._drawUpState();

    // restore 'mouseover' and 'mouseout' listeners
    this.addEvtListener(DvtMouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
    this.addEvtListener(DvtMouseEvent.MOUSEOUT, this._mouseOutHandler, false, this);

    if (event)
        event.preventDefault();
}

DvtScrollbarThumb.prototype.processThumbDragEnd = function(point) {
    this._sb.processThumbDragEnd(point);
}

/**
 * Handles a mouse move event for this scrollbar thumb.
 * @param {DvtMouseEvent} event The mouse event to handle
 * @private
 */
DvtScrollbarThumb.prototype._mouseMoveHandler = function(event) {

    var point = new DvtPoint(event.pageX, event.pageY);

    this.processThumbDragMove(point);

    if (event)
        event.preventDefault();
}

DvtScrollbarThumb.prototype.processThumbDragMove = function(point) {
    this._sb.processThumbDragMove(point);    
}

DvtScrollbarThumb.prototype.handleGripDragStart = function(gripper, event) {
    this._sb.handleGripDragStart(gripper, event);
}

DvtScrollbarThumb.prototype.processGripDragStart = function(gripper, point) {
    this._sb.processGripDragStart(gripper, point);
}

DvtScrollbarThumb.prototype.handleGripDragMove = function(gripper, event) {
    this._sb.handleGripDragMove(gripper, event);
}

DvtScrollbarThumb.prototype.processGripDragMove = function(gripper, point) {
    this._sb.processGripDragMove(gripper, point);
}

DvtScrollbarThumb.prototype.handleGripDragEnd = function(gripper, event) {
    this._sb.handleGripDragEnd(gripper, event);
}

DvtScrollbarThumb.prototype.processGripDragEnd = function(gripper, point) {
    this._sb.processGripDragEnd(gripper, point);
}

DvtScrollbarThumb.prototype._drawUpState = function() {
    this.drawPlainState()
    if (this._drawGrippers) {
      this._startGripper.setAlpha(0.001);
      this._endGripper.setAlpha(0.001);
    }
}


DvtScrollbarThumb.prototype.drawFullHighlightState = function() {
    this.drawHighlightState();
    if (this._drawGrippers) {
      this._startGripper.drawHighlightState();
      this._endGripper.drawHighlightState();
    }
}

DvtScrollbarThumb.prototype.drawFullPlainState = function() {
    if (this._drawGrippers) {
      this._startGripper.drawPlainState();
      this._endGripper.drawPlainState();
    }

    this.drawPlainState();
}

DvtScrollbarThumb.prototype.drawHighlightState = function() {
  if (this._overStroke)
    this.setStroke(this._overStroke);
  this.setFill(this._overFill);
}

DvtScrollbarThumb.prototype.drawPlainState = function() {
  if (this._upStroke)
    this.setStroke(this._upStroke);
  this.setFill(this._upFill);
}

DvtScrollbarThumb.prototype._drawOverState = function() {
    if (!DvtAgent.isTouchDevice()) {
        this.drawHighlightState();
    } else {
        this.drawPlainState();
    }
    if (this._drawGrippers) {
      this._startGripper.setAlpha(1);
      this._endGripper.setAlpha(1);
    }
}

DvtScrollbarThumb.prototype._drawDownState = function() {
    if (this._downStroke)
      this.setStroke(this._downStroke);
    this.setFill(this._downFill);
    if (this._drawGrippers) {
      this._startGripper.setAlpha(0.001);
      this._endGripper.setAlpha(0.001);
    }
}

DvtScrollbarThumb.prototype.getX = function() {
    return this._container.getX();
}

DvtScrollbarThumb.prototype.getY = function() {
    return this._container.getY();
}

DvtScrollbarThumb.prototype.getWidth = function() {
    return this._container.getWidth();
}

DvtScrollbarThumb.prototype.getHeight = function() {
    return this._container.getHeight();
}

DvtScrollbarThumb.prototype.setX = function(val) {
    this._container.setX(val);
}

DvtScrollbarThumb.prototype.setY = function(val) {
    this._container.setY(val);
}

DvtScrollbarThumb.prototype.setWidth = function(val) {
    this._container.setWidth(val);
}

DvtScrollbarThumb.prototype.setHeight = function(val) {
    this._container.setHeight(val);
}

DvtScrollbarThumb.prototype.setStroke = function(stroke) {
    this._container.setStroke(stroke);
}

DvtScrollbarThumb.prototype.setSolidStroke = function(color, alpha, strokeWidth) {
    this._container.setSolidStroke(color, alpha, strokeWidth);
}

DvtScrollbarThumb.prototype.setFill = function(fill) {
    this._container.setFill(fill);
}

DvtScrollbarThumb.prototype.setSolidFill = function(color, alpha) {
    this._container.setSolidFill(color, alpha);
}

/**
 * @override
 */
DvtScrollbarThumb.prototype.destroy = function () {
  //Bug 16893087 - HTML5 HV: SEARCH RESULTS SCROLLBAR TRAPS MOUSE
  if (DvtAgent.isTouchDevice()) {
    this.removeEvtListener(DvtTouchEvent.TOUCHSTART, this._touchHandler, false, this);
    this.removeEvtListener(DvtTouchEvent.TOUCHMOVE, this._touchHandler, false, this);
    this.removeEvtListener(DvtTouchEvent.TOUCHEND, this._touchHandler, false, this);
  }
  else {
    this.removeEvtListener(DvtMouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
    this.removeEvtListener(DvtMouseEvent.MOUSEOUT, this._mouseOutHandler, false, this);
    this.removeEvtListener(DvtMouseEvent.MOUSEDOWN, this._mouseDownHandler, false, this);

    this.getCtx().getStage().removeEvtListener(DvtMouseEvent.MOUSEMOVE, this._mouseMoveHandler, false, this);
    this.getCtx().getStage().removeEvtListener(DvtMouseEvent.MOUSEUP, this._mouseUpHandler, false, this);
  }

  if (this._container) {
    if (this._grip) {
      this._container.removeChild(this._grip);
      this._grip = null;
    }

    if (this._startGripper) {
      this._startGripper.destroy();
      this._startGripper = null;
    }

    if (this._endGripper) {
      this._endGripper.destroy();
      this._endGripper = null;
    }
  }

  DvtScrollbarThumb.superclass.destroy.call(this);
  this._container = null;

}

/**
 * Creates if necessary and returns a touch manager object
 * @return {DvtTouchManager} touch manager object for the scrollbar thumb
 */
DvtScrollbarThumb.prototype.getTouchManager = function () {
  if (!this._touchManager && DvtAgent.isTouchDevice())
    this._touchManager = new DvtTouchManager("_touchManager_sb", this._context);
  return this._touchManager;  
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/**  
 * A transparent container that adds vertical and horizontal scrollbars as needed
 *  @extends DvtContainer
 *  @constructor
 *  @param {DvtContext} context The platform dependent context object (such as {@link DvtSvgContext}).
 *  @param {number}     minW    The min width of the scrollable container used for getDimensions.
 *  @param {number}     minH    The min height of the scrollable container used for getDimensions.
 *  @param {number}     maxW    The max width of the scrollable container. If null, no clipping will be performed.
 *  @param {number}     maxH    The max height of the scrollable container. If null, no clipping will be performed.
 *  @param {String}     id      Optional id for this object.
 */
var DvtScrollableContainer = function (context, minW, minH, maxW, maxH, gap, id) {
  this.Init(context, minW, minH, maxW, maxH, gap, id);
}

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

DvtScrollableContainer.DEFAULT_SCROLLBAR_WIDTH = 14;// thickness of scrollbar
DvtScrollableContainer.DEFAULT_ALTA_SCROLLBAR_WIDTH = 11;
  
/**
 * Initializer for this object
 * @param {DvtContext} context The platform specific context object
 * @param {number} minW The minimum width for this container
 * @param {number} minH The minimum height for this container
 * @param {number} maxW The maximum width for this container
 * @param {number} maxH The maximum height for this container
 * @param {number} gap The gap between the scrollbar and the content
 * @param {string} id The id for this container
 * @protected
 */
DvtScrollableContainer.prototype.Init = function (context, minW, minH, maxW, maxH, gap, id) {
  DvtScrollableContainer.superclass.Init.call(this, context, id);
  this._scrollbarContentGap = gap;
  this._sbWidth = DvtScrollableContainer.DEFAULT_SCROLLBAR_WIDTH;
  this._skinName = null;
  this._horizScrollEnabled = true;
  this._vertScrollEnabled = true;
  
  if (maxW) {
    this._maxW = maxW;
    this._maxContentW = maxW - this._sbWidth - this._scrollbarContentGap;
  } else {
    this._maxW = Number.MAX_VALUE;
    this._maxContentW = Number.MAX_VALUE;
  }
  
  if (maxH) {
    this._maxH = maxH;
    this._maxContentH = maxH - this._sbWidth - this._scrollbarContentGap;
  } else {
    this._maxH = Number.MAX_VALUE;
    this._maxContentH = Number.MAX_VALUE;
  }
  
  this._minW = minW;
  this._fullW = 0;// current contentPane width
  
  this._minH  = minH;
  this._fullH = 0;// current contentPane height
  
  
  this._hsb = null;// horiz scrollbar obj
  this._hsbScale = 0;
  this._hsbStart = 0;
  
  this._vsb = null;// vert scriollbar obj
  this._vsbScale = 0;
  this._vsbStart = 0;
  
  this._translateY = 0;
  this._translateX = 0;
  this._contentPane = new DvtContainer(context); // user adds objs to this
  // Want to add content pane to DvtScrollableContainer
  DvtScrollableContainer.superclass.addChild.call(this, this._contentPane);
  
  this._isBiDi = DvtAgent.isRightToLeft(context);
}

DvtScrollableContainer.prototype.setHorizontalScrollingEnabled = function (enabled) {
  this._horizScrollEnabled = enabled;
}

DvtScrollableContainer.prototype.setVerticalScrollingEnabled = function (enabled) {
  this._vertScrollEnabled = enabled;
}

DvtScrollableContainer.prototype.getSkinName = function () {
  return this._skinName;
}

DvtScrollableContainer.prototype.setSkinName = function (skinName) {
  this._skinName = skinName;
  if (this.IsSkinAlta())
    this._sbWidth = DvtScrollableContainer.DEFAULT_ALTA_SCROLLBAR_WIDTH;
}

DvtScrollableContainer.prototype.IsSkinAlta = function() {
  return (this._skinName == "alta");
};

DvtScrollableContainer.prototype.setStyleMap = function(styleMap) {
  this._styleMap = styleMap;
}

DvtScrollableContainer.prototype.getScrollbarWidth = function () {
  return this._sbWidth;
}

DvtScrollableContainer.prototype.setScrollbarWidth = function (width) {
  // update max content width/height
  this._maxH = this._maxH + this._sbWidth - width;
  this._maxContentW = this._maxContentW + this._sbWidth - width;
  this._sbWidth = width;
}

/**  
 * Adds an object to the scrollable container and updates the container to include scrolling as necessary
 *  @param {DvtObj} obj The object to be added
 *  @override
 */
DvtScrollableContainer.prototype.addChild = function (obj) {
  this._contentPane.addChild(obj);
  this.refresh();
}

/**  
 * Adds an object to the scrollable container at the specified index and updates the container to include scrolling as necessary
 *  @param {DvtObj} obj The object to be added
 *  @param {number} index The index at which to add this object
 *  @override
 */
DvtScrollableContainer.prototype.addChildAt = function (obj, index) {
  this._contentPane.addChildAt(obj, index);
  this.refresh();
}

/**  
 * Removes an object from the scrollable container and updates the container to remove scrolling as necessary
 *  @param {DvtObj} obj The object to be removed
 *  @override
 */
DvtScrollableContainer.prototype.removeChild = function (obj) {
  this._contentPane.removeChild(obj);
  this.refresh();
}

/**  
 * Removes an object from the scrollable container at the specified index and updates the container to remove scrolling as necessary
 *  @param {number} index The index at which to add this object
 *  @override
 */
DvtScrollableContainer.prototype.removeChildAt = function (idx) {
  this._contentPane.removeChildAt(idx);
  this.refresh();
}

/**
 * @override
 */
DvtScrollableContainer.prototype.getNumChildren = function () {
  return this._contentPane.getNumChildren();
}

/**
 * @override
 */
DvtScrollableContainer.prototype.contains = function (obj, bDeep) {
  return this._contentPane.contains(obj, bDeep);
}

/**
 * @override
 */
DvtScrollableContainer.prototype.getChildAfter = function (obj) {
  return this._contentPane.getChildAfter(obj);
}

/**
 * @override
 */
DvtScrollableContainer.prototype.getChildBefore = function (obj) {
  return this._contentPane.getChildBefore(obj);
}

/**
 * @override
 */
DvtScrollableContainer.prototype.getChildIndex = function (obj) {
  return this._contentPane.getChildIndex(obj);
}

/**
 * @override
 */
DvtScrollableContainer.prototype.getIterator = function (bDeep) {
  return this._contentPane.getIterator(bDeep);
}

/**
 * @override
 */
DvtScrollableContainer.prototype.removeChildren = function () {
  this._contentPane.removeChildren();
}

/**
 * @override
 */
DvtScrollableContainer.prototype.destroy = function () {
  DvtScrollableContainer.superclass.destroy.call(this);
  // content pane's children have already been destroyed, just need to destroy content pane
  this._contentPane.setClipPath(null);
  this._contentPane.removeAllDrawEffects(null);
  this._removeHSB();
  this._removeVSB();
}

/**
 * @override
 */
DvtScrollableContainer.prototype.swap = function (obj1, obj2) {
  this._contentPane.swap(obj1, obj2);
}

/**
 * @override
 */
DvtScrollableContainer.prototype.getDimensions = function (targetCoordinateSpace) {  
  // Bug 16815388 - HTML5 HV: TWO SCROLLBARS WERE DISPLAYED AFTER SELECTING A SEARCH RESULT
  var dims = this._getDimensionsForContentPane(this);

  if (dims) {
    if (this.hasHorizontalScrollbar())
      dims.h = Math.min(this._maxContentH, dims.h) + this._scrollbarContentGap + this._sbWidth;
    if (this.hasVerticalScrollbar())
      dims.w = Math.min(this._maxContentW, dims.w) + this._scrollbarContentGap + this._sbWidth;

    dims.x = 0;
    dims.y = 0;
    dims.w = Math.min(this._maxW, dims.w);
    dims.h = Math.min(this._maxH, dims.h);
  }
  return targetCoordinateSpace && dims ? this.ConvertCoordSpaceRect(dims, targetCoordinateSpace) : dims;
}

DvtScrollableContainer.prototype.setVerticalThumbPosition = function (start) {
  if (this._vsb) {
    this._vsbStart = start;
    this._contentPane.setTranslateY(-start);
    this._vsb.moveSlider(start - this._vsb.getThumbStart());
  }
}

DvtScrollableContainer.prototype.getVerticalThumbPosition = function () {
  return this._vsb.getThumbStart();
}

/**
 *  Returns the visibility state of the horizontal scrollbar.
 *  @returns {boolean} true if the horizontal scrollbar is visible, else false.
 */
DvtScrollableContainer.prototype.hasHorizontalScrollbar = function () {
  return (this._hsb != null);
}

/**
 * Checks the current dimensions of the objects added to the scrollable container and adds scrollbars as needed.
 */
DvtScrollableContainer.prototype.refresh = function () {
  this._removeVSB();
  this._removeHSB();

  var addVSB = false;
  var addHSB = false;
  //Bug 16815388 - HTML5 HV: TWO SCROLLBARS WERE DISPLAYED AFTER SELECTING A SEARCH RESULT
  var dim = this._getDimensionsForContentPane();
  if (dim) {
    if (this._horizScrollEnabled && (dim.x + dim.w) > this._maxW)
      addHSB = true;
    if (this._vertScrollEnabled && (dim.y + dim.h) > (addHSB ? this._maxContentH : this._maxH))
      addVSB = true;
  
    this._fullH = Math.max(dim.y + dim.h, this._minH);
    this._fullW = Math.max(dim.x + dim.w, this._minW);
  } else {
    this._fullH = this._minH;
    this._fullW = this._minW;
  }
    
  var cp = new DvtGraphClipPath(this.getId());
  if (addVSB && addHSB)
    cp.addRect(this._isBiDi ? this._scrollbarContentGap + this._sbWidth : 0, 0, this._maxContentW, this._maxContentH);
  else if (addVSB)
    cp.addRect(this._isBiDi ? this._scrollbarContentGap + this._sbWidth : 0, 0, this._maxContentW, this._maxH);
  else if (addHSB)
    cp.addRect(0, 0, this._maxW, this._maxContentH);
  else
    cp = null;
  this._contentPane.setClipPath(cp);
  

  if (addVSB)
    this._addVSB(addHSB);
   
  if (addHSB)
    this._addHSB(addVSB);
    
  if (this._isBiDi && addVSB)
    this._translateX = addHSB ? -(this._fullW - this._maxW) : this._sbWidth + this._scrollbarContentGap;
  else
    this._translateX = 0;
  this._contentPane.setTranslateX(this._translateX);
}

/**
 *  Returns the visibility state of the vertical scrollbar.
 *  @returns {boolean} true if the vertical scrollbar is visible, else false.
 */
DvtScrollableContainer.prototype.hasVerticalScrollbar = function () {
  return (this._vsb != null);
}

DvtScrollableContainer.prototype._removeHSB = function () {
  if (this._hsb) {
    this._hsb.removeEvtListener(DvtScrollbarEvent.SB_MOVE, this._onHorizontalScroll, false, this);
    this._hsb.destroy();
    DvtScrollableContainer.superclass.removeChild.call(this, this._hsb);
    this._hsb = null;
  }
}

DvtScrollableContainer.prototype._removeVSB = function () {
  if (this._vsb) {
    this._vsb.removeEvtListener(DvtScrollbarEvent.SB_MOVE, this._onVerticalScroll, false, this);
    this._vsb.destroy();
    DvtScrollableContainer.superclass.removeChild.call(this, this._vsb);
    this._vsb = null;
  }
}

/**
 * Creates a vertical scrollbar for this scrollable container
 * @param {boolean} hasHSB Whether there is a horizontal scrollbar 
 * @private
 */
DvtScrollableContainer.prototype._addVSB = function (hasHSB) {
  if (!this._vsb) {
    var scrollbarLength = hasHSB ? this._maxH - this._sbWidth : this._maxH;
    this._vsbLength = scrollbarLength;
    var x = this._isBiDi ? 0 : (hasHSB ? this._maxW - this._sbWidth : this._fullW + this._scrollbarContentGap);
    var width = this._sbWidth;
    var height = scrollbarLength;
    var y = 0;
    // if just vsb and is alta, adjust width/height/y so the scrollbar top, bottom, and right borders gets clipped
    var clip;
    if (this.IsSkinAlta() && !hasHSB) {
      width += 1;
      height += 2;
      y -= 1;
      clip = new DvtGraphClipPath(this.getId()+"vsb");
      clip.addRect(x, 0, width-1, height-2);
    }
    this._vsb = new DvtScrollbar(this.getCtx(), x, y, width, height, false, false, this.getSkinName(), this._styleMap);
    if (clip)
      this._vsb.setClipPath(clip);
    // Want to add scrollbar to DvtScrollableContainer, not content pane
    DvtScrollableContainer.superclass.addChild.call(this, this._vsb);
  }
  
  var viewableContentH = hasHSB ? this._maxContentH : this._maxH;
  var neededThumbLength = (viewableContentH/this._fullH) * this._vsb.getScrollAreaLength();
  // constrain thumb length to max and min available thumb size
  var thumbLength = Math.min(this._vsb.getScrollAreaLength(), Math.max(neededThumbLength, DvtScrollbar.MIN_THUMB_SIZE));
  
  this._vsbScale = (this._fullH-viewableContentH)/(this._vsb.getScrollAreaLength() - thumbLength);
  this._vsb.setLogicalThumbStartAndLength(this._vsbStart, thumbLength);
  this._vsb.addEvtListener(DvtScrollbarEvent.SB_MOVE, this._onVerticalScroll, false, this);
}

/**
 * Creates a horizontal scrollbar for this scrollable container
 * @param {boolean} hasVSB Whether there is a vertical scrollbar 
 * @private
 */
DvtScrollableContainer.prototype._addHSB = function (hasVSB) {
  if (!this._hsb) {
    var scrollbarLength = hasVSB ? this._maxW - this._sbWidth : this._maxW;
    this._hsbLength = scrollbarLength;
    var x = (this._isBiDi && hasVSB) ? this._sbWidth : 0;
    var y = hasVSB ? (this._maxH - this._sbWidth) : (this._fullH + this._scrollbarContentGap);
    
    var height = this._sbWidth;
    var width = scrollbarLength;
    // if just hsb and is alta, adjust width/height so the scrollbar bottom, left, and right borders get clipped 
    var clip
    if (this.IsSkinAlta() && !hasVSB) {
      height += 1;
      width += 2;
      clip = new DvtGraphClipPath(this.getId()+"hsb");
      clip.addRect(x, y, width-2, height-1);
    }
    
    this._hsb = new DvtScrollbar(this.getCtx(), x, y, width, height, true, false, this.getSkinName(), this._styleMap);
    if (clip)
      this._hsb.setClipPath(clip);
    // Want to add scrollbar to DvtScrollableContainer, not content pane
    DvtScrollableContainer.superclass.addChild.call(this, this._hsb);
  }
  
  var viewableContentW = hasVSB ? this._maxContentW : this._maxW;
  var neededThumbLength = (viewableContentW/this._fullW) * this._hsb.getScrollAreaLength();
  // constrain thumb length to max and min available thumb size
  var thumbLength = Math.min(this._hsb.getScrollAreaLength(), Math.max(neededThumbLength, DvtScrollbar.MIN_THUMB_SIZE));
  this._hsbScale = (this._fullW-viewableContentW)/(this._hsb.getScrollAreaLength() - thumbLength);
  if (this._isBiDi)
    this._hsbStart = this._hsb.getScrollAreaLength() - thumbLength;
  this._hsb.setLogicalThumbStartAndLength(this._hsbStart, thumbLength);
  this._hsb.addEvtListener(DvtScrollbarEvent.SB_MOVE, this._onHorizontalScroll, false, this);
}

/**
 *  Handle vertical scroll event
 *  @param {DvtScrollbarEvent} event scrollbar event
 *  @private
 */
DvtScrollableContainer.prototype._onVerticalScroll = function (event) {
  var delta = event.getMovedDelta();
  if (delta != 0) {
    this._translateY -= delta * this._vsbScale;
    this._contentPane.setTranslateY(this._translateY);

    //Bug 17329022 - HTML5: HORIZONTAL SCROLL TRIGGERS LOADING OF ADDT'L SEARCH RESULTS
    var x = null;
    var y = this._vsbLength*this._vsbScale + this._translateY;
    this._fireScrollEvent(x, y);
  }
}

/**
 *  Handle horizontal scroll event
 *  @param {DvtScrollbarEvent} event scrollbar event
 *  @private
 */
DvtScrollableContainer.prototype._onHorizontalScroll = function (event) {
  var delta = event.getMovedDelta();
  if (delta != 0) {
    this._translateX -= delta * this._hsbScale;
    this._contentPane.setTranslateX(this._translateX);
    var x = this._hsbLength*this._hsbScale + this._translateX;
    //Bug 17329022 - HTML5: HORIZONTAL SCROLL TRIGGERS LOADING OF ADDT'L SEARCH RESULTS
    var y = null;
    this._fireScrollEvent(x, y);
  }
}

DvtScrollableContainer.prototype._fireScrollEvent = function (x, y) {
  this.FireListener(new DvtScrollEvent(x, y));
}

// Bug 16815388 - HTML5 HV: TWO SCROLLBARS WERE DISPLAYED AFTER SELECTING A SEARCH RESULT
// Need to call getDimensions on each child of _contentPane in case 
// it has the method getDimensions overidden
DvtScrollableContainer.prototype._getDimensionsForContentPane = function (targetCoordinateSpace) {

  var dims = null;
  var cdims;
  
  for (var i = 0; i < this.getNumChildren(); i++) {
    var child = this._contentPane.getChildAt(i);
    
    if (child) {
      cdims = child.getDimensions(this._contentPane);
      if (cdims) {
        if (dims == null)
          dims = new DvtRectangle(cdims.x, cdims.y, cdims.w, cdims.h);
        else
          dims = dims.getUnion(cdims);
      }
    }
  }

  return targetCoordinateSpace && dims ? this.ConvertCoordSpaceRect(dims, targetCoordinateSpace) : dims; 
}
/**
 * A minimalist scrollbar.
 * @class
 * @constructor
 * @extends {DvtContainer}
 * @param {DvtContext} context
 * @param {object} [callback]
 * @param {object} [callbackObj]
 * @export
 */
var DvtSimpleScrollbar = function(context, callback, callbackObj) {
  DvtSimpleScrollbar.superclass.Init.call(this, context);
  this._callback = callback;
  this._callbackObj = callbackObj;
};

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

/**
 * Renders the simple scrollbar at the specified size.
 * @param {object} options The object containing specifications for the scrollbar.
 * @param {number} width The width of the scrollbar.
 * @param {number} height The height of the scrollbar.
 * @export
 */
DvtSimpleScrollbar.prototype.render = function(options, width, height) {  
  this._options = DvtJSONUtils.clone(options);
  this._width = width;
  this._height = height;
  
  // Global min/max
  this._globalMin = options['min'];
  this._globalMax = options['max'];
  
  this._isHoriz = options['isHorizontal'];
  this._isReversed = options['isReversed'];
  
  // Create background
  var background = new DvtRect(this.getCtx(), 0, 0, width, height);
  background.setSolidFill(options['backgroundColor']);
  background.setPixelHinting(true);
  this.addChild(background);
  
  // Create thumb
  this._thumb = new DvtRect(this.getCtx(), 0, 0, width, height);
  this._thumb.setSolidFill(options['color']);
  this._thumb.setPixelHinting(true);
  this.addChild(this._thumb);
  
  this._thumbMin = this._globalMin;
  this._thumbMax = this._globalMax;
  
  // Add event listeners
  this.addEvtListener(DvtMouseEvent.CLICK, this._onClick, false, this);
  if (DvtAgent.isTouchDevice()) {
    this.addEvtListener(DvtTouchEvent.TOUCHSTART, this._onDragStart, false, this);
    this.addEvtListener(DvtTouchEvent.TOUCHMOVE,  this._onDragMove,  false, this);
    this.addEvtListener(DvtTouchEvent.TOUCHEND,   this._onDragEnd,   false, this);
  } else {
    this.addEvtListener(DvtMouseEvent.MOUSEDOWN, this._onDragStart, false, this);
    this.addEvtListener(DvtMouseEvent.MOUSEMOVE, this._onDragMove,  false, this);
    this.addEvtListener(DvtMouseEvent.MOUSEUP,   this._onDragEnd,   false, this);
  } 
  
  this._dragged = false; // flag to indicate mouse/touch down
}

/**
 * Sets the range of the scrollbar thumb.
 * @param {number} min The min value of the range.
 * @param {number} max The max value of the range.
 */
DvtSimpleScrollbar.prototype.setViewportRange = function(min, max) {
  // Get the coords and modify the thumb
  var minCoord = this._getCoord(min);
  var maxCoord = this._getCoord(max);
  
  if (this._isHoriz) {
    this._thumb.setX(Math.min(minCoord, maxCoord));
    this._thumb.setWidth(Math.abs(maxCoord - minCoord));
  } else {
    this._thumb.setY(Math.min(minCoord, maxCoord));
    this._thumb.setHeight(Math.abs(maxCoord - minCoord));
  }
  
  this._thumbMin = min;
  this._thumbMax = max;
}

/**
 * Gets the coordinate of a value.
 * @param {number} value
 * @return {number} coord
 * @private
 */
DvtSimpleScrollbar.prototype._getCoord = function(value) {
  var ratio = (value - this._globalMin) / (this._globalMax - this._globalMin);
  if (this._isReversed)
    ratio = 1 - ratio;
  
  if (this._isHoriz)
    return ratio * this._width;
  else
    return ratio * this._height;
}

/**
 * Gets the value from a coord.
 * @param {number} pageX
 * @param {number} pageY
 * @return {number} value
 * @private
 */
DvtSimpleScrollbar.prototype._getValue = function(pageX, pageY) {
  var coord = this.stageToLocal(this.getCtx().getRelativePosition(pageX, pageY));
  var ratio = this._isHoriz ? coord.x / this._width : coord.y / this._height;
  if (this._isReversed)
    ratio = 1 - ratio;
  return this._globalMin + ratio * (this._globalMax - this._globalMin);
}

/**
 * Handles click event.
 * @param {object} event
 * @private
 */
DvtSimpleScrollbar.prototype._onClick = function(event) {
  var val = this._getValue(event.pageX, event.pageY);
  if (val >= this._thumbMin && val <= this._thumbMax)
    return;
  
  var range = this._thumbMax - this._thumbMin;
  this._setViewportRange(val - range/2, val + range/2, DvtOverviewEvent.SUBTYPE_SCROLL_TIME);
}

/**
 * Handles drag start.
 * @param {object} event
 * @private
 */
DvtSimpleScrollbar.prototype._onDragStart = function(event) {
  var val;
  if (event.touches) {
    if (event.touches.length != 1)
      return;
    val = this._getValue(event.touches[0].pageX, event.touches[0].pageY);
  } 
  else
    val = this._getValue(event.pageX, event.pageY);
  
  if (val >= this._thumbMin && val <= this._thumbMax) {
    this._dragged = true;
    this._prevVal = val;
    
    event.stopPropagation(); // prevent click event
  }
}

/**
 * Handles drag move.
 * @param {object} event
 * @private
 */
DvtSimpleScrollbar.prototype._onDragMove = function(event) {
  if (!this._dragged)
    return;
  
  var val;
  if (event.touches) {
    if (event.touches.length != 1)
      return;
    val = this._getValue(event.touches[0].pageX, event.touches[0].pageY);
  } 
  else
    val = this._getValue(event.pageX, event.pageY);
  
  var dragOffset = val - this._prevVal;
  this._setViewportRange(this._thumbMin + dragOffset, this._thumbMax + dragOffset, DvtOverviewEvent.SUBTYPE_SCROLL_POS);
  this._prevVal = val;
}

/**
 * Handles drag end.
 * @param {object} event
 * @private
 */
DvtSimpleScrollbar.prototype._onDragEnd = function(event) {
  if (!this._dragged)
    return;
  
  if (event.touches) 
    this._setViewportRange(this._thumbMin, this._thumbMax, DvtOverviewEvent.SUBTYPE_SCROLL_END);
  else {
    var val = this._getValue(event.pageX, event.pageY);
    var dragOffset = val - this._prevVal;
    this._setViewportRange(this._thumbMin + dragOffset, this._thumbMax + dragOffset, DvtOverviewEvent.SUBTYPE_SCROLL_END);
  }
  
  this._dragged = false;
}

/**
 * Sets the range of the scrollbar thumb and fires an overview event.
 * @param {number} min The min value of the range.
 * @param {number} max The max value of the range.
 * @param {string} eventType The subtype of the overview event.
 * @private
 */
DvtSimpleScrollbar.prototype._setViewportRange = function(min, max, eventType) {
  // Adjust min/max so that they are within globalMin/Max
  var range = max - min;
  if (min < this._globalMin) {
    min = this._globalMin;
    max = this._globalMin + range;
  }
  if (max > this._globalMax) {
    max = this._globalMax;
    min = this._globalMax - range;
  }
  
  this.setViewportRange(min, max);
  
  // Fire event
  if (this._callback) {
    var evt = new DvtOverviewEvent(eventType);
    evt.setNewStartTime(min);
    evt.setNewEndTime(max);
    this._callback.call(this._callbackObj, evt, this);
  }
}
/**
 * Drag recognizer
 * @class DvtDragRecognizer
 * @constructor
 */
var DvtDragRecognizer = function () {};

DvtObj.createSubclass(DvtDragRecognizer, DvtObj, "DvtDragRecognizer");

/**
 * Notifies the component that a drag may have started.
 */
DvtDragRecognizer.prototype.prepDrag = function () {
  // subclasses should override
}

/**
 * Notifies the component that a drag has been aborted.
 */
DvtDragRecognizer.prototype.abortPrep = function () {
  // subclasses should override
}

/**
 * Returns true if the component allows drag. This function gives the peer the
 * chance to prevent a drag.  All other conditions for a drag
 * must be satisfied even if the peer returns true.
 */
DvtDragRecognizer.prototype.recognizeDrag = function () {
  return true; // subclasses should override
}
/**
 * Drag source event handler.
 * @param {DvtContext} context
 * @class DvtDragSource
 * @constructor
 */
var DvtDragSource = function (context) {
  this.Init(context);
};

DvtObj.createSubclass(DvtDragSource, DvtObj, "DvtDragSource");

// Margin to add to drag feedback bounds to minimize clipping
DvtDragSource.DRAG_FEEDBACK_MARGIN = 2;

/**
 * @param {DvtContainer} container
 * @param {DvtContext} context
 */
DvtDragSource.prototype.Init = function(context) {
    this._context = context;
    
    // current draggable object under the mouse pointer
    this._dragCandidate = null;
    
    // object being dragged
    this._dragObj = null;
    this._dragCoords = null;
} ;

/**
 * Returns the current drag candidate object.
 */
DvtDragSource.prototype.getDragCandidate = function () {
  return this._dragCandidate;
}

/**
 * Sets the specified object as the current drag candidate, if it supports the DvtDraggable interface.
 * @param {object} obj 
 */
DvtDragSource.prototype.setDragCandidate = function (obj) {
  // a DnD candidate object must support all DvtDraggable methods
  if (obj && obj.isDragAvailable && obj.getDragTransferable)
    this._dragCandidate = obj;
  else
    this._dragCandidate = null;
}

/**
 * If there are any drag candidate, returns the client id of the drag component.
 * Otherwise returns null.
 */
DvtDragSource.prototype.isDragAvailable = function (clientIds) {
  if (this._dragCandidate != null) {
    return this._dragCandidate.isDragAvailable(clientIds);
  }
  return null;
};


/**
 * If there are a drag candidate, return the dragTransferable object.
 */
DvtDragSource.prototype.getDragTransferable = function (mouseX, mouseY) {
  // if there's anything to drag
  if (this._dragCandidate) {
    // store the dragObj and current mouse coords
    this._dragObj = this._dragCandidate;
    this._dragCoords = {
      x : mouseX, y : mouseY
    };

    return this._dragCandidate.getDragTransferable(mouseX, mouseY);
  }
  return null;
};

/**
 * Returns the feedback for the drag operation.
 */
DvtDragSource.prototype.getDragOverFeedback = function (mouseX, mouseY) {
  // Hide the tooltip, since this is the last hook before the drag feedback is created
  this._context.getTooltipManager().hideTooltip();

  // Return the drag feedback
  var dragObj = this.getDragObject();
  if (dragObj && dragObj.getDragFeedback)
    return dragObj.getDragFeedback(mouseX, mouseY);
  else 
    return null;
}

/**
 * Returns component specific context for the drag.
 */
DvtDragSource.prototype.getDragContext = function (mouseX, mouseY) {
  if (this._dragObj && this._dragObj.getDragContext) {
    return this._dragObj.getDragContext(mouseX, mouseY);
  }
  return null;
}

/**
 * Returns the object currently being dragged.
 */
DvtDragSource.prototype.getDragObject = function() {
    return this._dragObj;
}

/**
 * Returns the coordinates where the current drag was initiated.
 */
DvtDragSource.prototype.getDragCoords = function () {
  return this._dragCoords;
}

/**
 * Return the offset to use for the drag feedback.
 */
DvtDragSource.prototype.getDragOffset = function (mouseX, mouseY) {
  var offset = null;
  var feedback = this.getDragOverFeedback(mouseX, mouseY);
  if(feedback) {
    var bounds = DvtDnDUtils.getDragFeedbackBounds(feedback, this._context.getStage());
    
    //return displayables to their original condition, if necessary,
    //after determining bounds
    var dragObj = this.getDragObject();
    if (dragObj && dragObj.afterDragOverFeedback) {
      dragObj.afterDragOverFeedback(feedback);
    }
    
    if (bounds) {
      offset = {};
      offset.x = mouseX - bounds.x;
      offset.y = mouseY - bounds.y;
      offset.x += DvtDragSource.DRAG_FEEDBACK_MARGIN;
      offset.y += DvtDragSource.DRAG_FEEDBACK_MARGIN;
    }
  }
  return offset;
}

/**
 * Returns the offset from the mouse pointer where the drag is considered to be located.
 * @param {number} xOffset A suggested offset, usually the center of the drag feedback.
 * @param {number} yOffset A suggested offset, usually the center of the drag feedback.
 * @return {object}
 */
DvtDragSource.prototype.getPointerOffset = function (xOffset, yOffset) {
  // Default implementation does not apply an offset, mouse position is used instead
  return {x: 0, y: 0};
}

/**
 * Called when a drag is started.
 */
DvtDragSource.prototype.initiateDrag = function () {
  // On touch devices, when a drag is initiated, immediately cancel any touch and hold
  var tm = this.getTouchManager();
  if(DvtAgent.isTouchDevice() && tm) 
    tm.cancelTouchHold();
}

/**
 * Clean up after the drag is completed.
 */
DvtDragSource.prototype.dragDropEnd = function () {
  this._dragCandidate = null;
  this._dragObj = null;
  this._dragCoords = null;
}

/**
 * Sets a touch manager object
 * @param {DvtTouchManager} touch manager object
 */ 
DvtDragSource.prototype.setTouchManager = function (touchManager) {
  this._touchManager = touchManager;
}

/**
 * Gets a touch manager object
 * @return {DvtTouchManager} touch manager object
 */
DvtDragSource.prototype.getTouchManager = function () {
  return this._touchManager;
}
/**
 * Drop target event handler.
 * @class DvtDropTarget
 * @constructor
 */
var DvtDropTarget = function() {};

DvtObj.createSubclass(DvtDropTarget, DvtObj, "DvtDropTarget");

/**
 * If a drop is possible at these mouse coordinates, returns the client id
 * of the drop component. Returns null if drop is not possible.
 */
DvtDropTarget.prototype.acceptDrag = function(mouseX, mouseY, clientIds) {
  return null; // subclasses should override
}

/**
 * Paints drop site feedback as a drag enters the drop site.
 */
DvtDropTarget.prototype.dragEnter = function() {
  // subclasses should override
}

/**
 * Cleans up drop site feedback as a drag exits the drop site.
 */
DvtDropTarget.prototype.dragExit = function() {
  // subclasses should override
}

/**
 * Returns the object representing the drop site. This method is called when a valid
 * drop is performed.
 */
DvtDropTarget.prototype.getDropSite = function(mouseX, mouseY) {
  return null; // subclasses should override
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.

/**
 * DvtButton
 * @param {DvtContext} context
 * @param {DvtDisplayable} upState
 * @param {DvtDisplayable} overState
 * @param {DvtDisplayable} downState
 * @param {DvtDisplayable} disabledState
 * @param {string} id
 * @param {object} [callback]
 * @param {object} [callbackObj]
 * @constructor
 */
var DvtButton = function (context, upState, overState, downState, disabledState, id, callback, callbackObj)
{
  this.Init(context, upState, overState, downState, disabledState, id, callback, callbackObj);
};

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

DvtButton.STATE_ENABLED  = 0;
DvtButton.STATE_OVER     = 1;
DvtButton.STATE_DOWN     = 2;
DvtButton.STATE_DISABLED = 3;

DvtButton.NO_EVENT_LISTENERS = true;       //dont register any event listeners


/**
 * Initialization method called by the constructor
 * 
 * @protected
 * 
 * @param {DvtContext} context  Platform specific context object
 * @param {DvtDisplayable} upState      Button up state
 * @param {DvtDisplayable} overState    Button over state
 * @param {DvtDisplayable} downState    Button down state
 * @param {DvtDisplayable} disabledState    Button disabled state
 * @param {string} id   The id for this object
 * @param {function} callback   The optional function that should be called on click event
 * @param {object} callbackObj  The optional object instance on which the callback function is defined
 * 
 */
DvtButton.prototype.Init = function (context, upState, overState, downState, disabledState, id, callback, callbackObj)
{
  DvtButton.superclass.Init.call(this, context, id);
  
  this.setCallback(callback, callbackObj);
  
  //FIX BUG 16173601: don't do anything special in setUpState() during construction
  this._bDuringInit = true;

  this.setUpState(upState);
  this.setOverState(overState);
  this.setDownState(downState);

  if (disabledState) {
    this.setDisabledState(disabledState);
  }

  this.setEnabled(true);                 // enable mouse events for upState
  this._drawUpState();
  
  this._tooltip = null;
  
  this._bToggleEnabled = false;
  this._bToggled = false;
  
  //FIX BUG 16173601: don't do anything special in setUpState() during construction
  this._bDuringInit = false;
};



/**
  *  Returns the enabled state of this button
  *  @returns {boolean}  true if the button is enabled,, else false.
  */
DvtButton.prototype.isEnabled = function () {
  return this._enabled;
};




/**
  *   Enable/disable the button
  */
DvtButton.prototype.setEnabled = function (bEnabled)
{
  if (this._enabled != bEnabled)
  {
    this._enabled = bEnabled;
    var clickEvent = DvtAgent.isTouchDevice() ? DvtTouchEvent.TOUCHSTART : DvtMouseEvent.CLICK;

    if (bEnabled) {
      this.addEvtListener(DvtMouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
      this.addEvtListener(DvtMouseEvent.MOUSEOUT,  this._mouseOutHandler,  false, this);
      this.addEvtListener(DvtMouseEvent.MOUSEDOWN, this._mouseDownHandler, false, this);
      this.addEvtListener(DvtMouseEvent.MOUSEUP,   this._mouseUpHandler,   false, this);
      this.addEvtListener(clickEvent,              this._clickHandler,     false, this);
    }
    else {
      this.removeEvtListener(DvtMouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
      this.removeEvtListener(DvtMouseEvent.MOUSEOUT,  this._mouseOutHandler,  false, this);
      this.removeEvtListener(DvtMouseEvent.MOUSEDOWN, this._mouseDownHandler, false, this);
      this.removeEvtListener(DvtMouseEvent.MOUSEUP,   this._mouseUpHandler,   false, this);
      this.removeEvtListener(clickEvent,              this._clickHandler,     false, this);
    }
  }
};



/**
 * Mouse over handler
 * @private
 * @param {DvtMouseEvent} event The dispatched event to be processed by the object
 */
DvtButton.prototype._mouseOverHandler = function (event) {
  this._logEvent(event);
  if (!this._bToggled) {
    this._drawOverState();
  }
  else {
    this._drawOverDownState();
  }
};

/**
 * Mouse out handler
 * @private
 * @param {DvtMouseEvent} event The dispatched event to be processed by the object
 */
DvtButton.prototype._mouseOutHandler = function (event) {
  this._logEvent(event);
  if (!this._bToggled) {
    this._drawUpState();
  }
  else {
    this._drawDownState();
  }
};

/**
 * Mouse down handler
 * @private
 * @param {DvtMouseEvent} event The dispatched event to be processed by the object
 */
DvtButton.prototype._mouseDownHandler = function (event) {
  this._logEvent(event);
  this._drawDownState();
};

/**
 * Mouse up handler
 * @private
 * @param {DvtMouseEvent} event The dispatched event to be processed by the object
 */
DvtButton.prototype._mouseUpHandler = function (event) {
  this._logEvent(event);
  if (!this._bToggled) {
    DvtAgent.isTouchDevice() ? this._drawUpState() : this._drawOverState();
  }
  else {
    DvtAgent.isTouchDevice() ? this._drawDownState() : this._drawOverDownState();
  }
};

DvtButton.prototype._clickHandler = function (event) {
  this._logEvent(event);
  if (this._bToggleEnabled) {
    this._bToggled = !this._bToggled;
    if (!this._bToggled) {
      DvtAgent.isTouchDevice() ? this._drawUpState() : this._drawOverState();
    }
    else {
      DvtAgent.isTouchDevice() ? this._drawDownState() : this._drawOverDownState();
    }
  }
  
  // Call the callback
  if (this._callback) {
    this._callback.call(this._callbackObj, event);
    event.stopPropagation();
    event.preventDefault();
  }
};

DvtButton.prototype._drawUpState = function () {
  this._enableButton(this.upState, true);
  this._enableButton(this.downState, false);
  this._enableButton(this.overState, false);
  this._enableButton(this.disabledState, false);
  this._enableButton(this.overDownState, false);
};

DvtButton.prototype._drawOverState = function () {
  this._enableButton(this.upState, false);
  this._enableButton(this.downState, false);
  this._enableButton(this.overState, true);
  this._enableButton(this.disabledState, false);
  this._enableButton(this.overDownState, false);
};

DvtButton.prototype._drawDownState = function () {
  this._enableButton(this.upState, false);
  this._enableButton(this.downState, true);
  this._enableButton(this.overState, false);
  this._enableButton(this.disabledState, false);
  this._enableButton(this.overDownState, false);
};

DvtButton.prototype._drawDisabledState = function () {
  this._enableButton(this.upState, false);
  this._enableButton(this.overState, false);
  this._enableButton(this.downState, false);
  this._enableButton(this.disabledState, true);
  this._enableButton(this.overDownState, false);
};

/**
 * Draws the over + down (hover selected) state.
 * If the state doesn't exist, defaults to drawing down state.
 * @private
 */
DvtButton.prototype._drawOverDownState = function () {
  if (this.overDownState) {
    this._enableButton(this.upState, false);
    this._enableButton(this.overState, false);
    this._enableButton(this.downState, false);
    this._enableButton(this.disabledState, false);
    this._enableButton(this.overDownState, true);
  }
  else
    this._drawDownState();
};

/**
  *   Show or hide the specified button face shape.
  */
DvtButton.prototype._enableButton = function (button, enabled) {
  //   this._enableMouseEvents(button, enabled);
  if (button) {
    button.setAlpha(enabled ? 1 : 0);
  }

  //image button does not work when swapping different image states
  //   button.setVisible(enabled);
};


DvtButton.prototype._enableMouseEvents = function (container, enabled) {
  if (container.setMouseEnabled) {
    container.setMouseEnabled(enabled);
  }
  var child = container.firstChild;

  //loop over all childs
  while (child != null) {
    this._enableMouseEvents(child, enabled);
    child = child.nextSibling;
  }
};

DvtButton.prototype.setUpState = function (upState) {

  if ( (!this.upState) || (this.upState != upState))
  {
     var enabled = false;
     if (this.upState) {
       enabled = this._isButtonEnabled(this.upState);
       this.removeChild(this.upState);
     }
     //FIX BUG 16173601: if the button has no enabled states, enable the up state by default
     else if (!this._bDuringInit) {
       var bDownEnabled = false;
       var bOverEnabled = false;
       var bDisabledEnabled = false;
       if (this.downState) {
         bDownEnabled = this._isButtonEnabled(this.downState);
       }
       if (this.overState) {
         bOverEnabled = this._isButtonEnabled(this.overState);
       }
       if (this.disabledState) {
         bDisabledEnabled = this._isButtonEnabled(this.disabledState);
       }
       if (!(bDownEnabled || bOverEnabled || bDisabledEnabled)) {
         enabled = true;
       }
     }

     if (upState) {
       this.addChild(upState);
     }

     this.upState = upState;
     this._enableButton(this.upState, enabled);
  }
};

DvtButton.prototype.setDownState = function (downState) {

  if ( (! this.downState) || (this.downState != downState))
  {
    var enabled = false;
    if (this.downState) {
      enabled = this._isButtonEnabled(this.downState);
      this.removeChild(this.downState);
    }

    if (downState) {
      this.addChild(downState);
    }

    this.downState = downState;
    this._enableButton(this.downState, enabled);
  }
};

DvtButton.prototype.setOverState = function (overState) {

  if ( (! this.overState)  || (this.overState != overState))
  {
     var enabled = false;
     if (this.overState) {
       enabled = this._isButtonEnabled(this.overState);
       this.removeChild(this.overState);
     }

     if (overState) {
       this.addChild(overState);
     }

     this.overState = overState;
     this._enableButton(this.overState, enabled);
  } 
};

DvtButton.prototype.setDisabledState = function (disabledState) {

  if ((! this.disabledState) || (this.disabledState == disabledState))
  {
    var enabled = false;
    if (this.disabledState) {
      enabled = this._isButtonEnabled(this.disabledState);
      this.removeChild(this.disabledState);
    }

    if (disabledState) {
      this.addChild(disabledState);
    }

    this.disabledState = disabledState;
    this._enableButton(this.disabledState, enabled);
  }
};

/**
 * Sets the over + down (hover selected) state of a toggle button
 * @param {DvtShape} overDownState
 */
DvtButton.prototype.setOverDownState = function (overDownState) {

  if ((! this.overDownState) || (this.overDownState == overDownState))
  {
    var enabled = false;
    if (this.overDownState) {
      enabled = this._isButtonEnabled(this.overDownState);
      this.removeChild(this.overDownState);
    }

    if (overDownState) {
      this.addChild(overDownState);
    }

    this.overDownState = overDownState;
    this._enableButton(this.overDownState, enabled);
  }
};

/**
 * Set the callback function handler
 * 
 * @public
 * 
 * @param {function} callback    The function that should be called on click event
 * @param {object} callbackObj   The object instance on which the callback function is defined
 */
DvtButton.prototype.setCallback = function (callback, callbackObj) {
  this._callback = callback;
  this._callbackObj = callbackObj;
}

DvtButton.prototype._logEvent = function (event) {
//  logEvent(event, "Button");
};

DvtButton.prototype.setTooltip = function(tooltip) {
  this._tooltip = tooltip;
};

DvtButton.prototype.getTooltip = function() {
  return this._tooltip;
};

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


DvtButton.prototype.initState = function()
{
  this.isEnabled() ? this._drawUpState() : this._drawDisabledState();
};


DvtButton.prototype.setSize = function(width, height) {
  var up = this.upState;
  if (up) {
    if (up.setWidth && width) {
      up.setWidth(width);
    }
    if (up.setHeight && height) {
      up.setHeight(height);
    }
  }

  var down = this.downState;
  if (down) {
    if (down.setWidth && width) {
      down.setWidth(width);
    }
    if (down.setHeight && height) {
      down.setHeight(height);
    }
  }

  var over = this.overState;
  if (over) {
    if (over.setWidth && width) {
      over.setWidth(width);
    }
    if (over.setHeight && height) {
      over.setHeight(height);
    }
  }

  var disabled = this.disabledState;
  if (disabled) {
    if (disabled.setWidth && width) {
      disabled.setWidth(width);
    }
    if (disabled.setHeight && height) {
      disabled.setHeight(height);
    }
  }
}


/*
 * Called from DvtAfMenu and DvtAfGoButton to right align this component or its content.
 */
DvtButton.prototype.rightAlign = function(maxw, ww, self) {
  var delta = maxw - ww;

  var up = this.upState;
  if (up && maxw) {
    if (self)
      up.setTranslateX(delta);
    else
      this._setChildX(up, delta)
  }

  var down = this.downState;
  if (down && maxw) {
    if (self)
      down.setTranslateX(delta);
    else
      this._setChildX(down, delta);
  }

  var over = this.overState;
  if (over && maxw) {
    if (self)
      over.setTranslateX(delta);
    else
      this._setChildX(over, delta);
  }

  var disabled = this.disabledState;
  if (disabled) {
    if (self)
      disabled.setTranslateX(delta);
    else
      this._setChildX(disabled, delta);
  }
}

DvtButton.prototype._setChildX = function(state, delta) {
  for (var i = 0; i < state.getNumChildren(); i++) {
    var child = state.getChildAt(i);

    if (child instanceof DvtDisplayable) {
      child.setTranslateX(delta);
    }
  }
}

DvtButton.prototype.setToggleEnabled = function(bToggleEnabled) {
  this._bToggleEnabled = bToggleEnabled;
};

DvtButton.prototype.isToggleEnabled = function() {
  return this._bToggleEnabled;
};

DvtButton.prototype.setToggled = function(bToggled) {
  if (this._bToggleEnabled) {
    this._bToggled = bToggled;
    if (!this._bToggled) {
      this._drawUpState();
    }
    else {
      this._drawDownState();
    }
  }
};

DvtButton.prototype.isToggled = function() {
  return this._bToggled;
};

DvtButton.prototype._isButtonEnabled = function (button) {
  var enabled = false;
  if (button) {
    enabled = button.getAlpha() > 0 ? true : false;
  }
  return enabled;
}

DvtButton.prototype.getUpState = function () {
  return this.upState;
}

DvtButton.prototype.getDownState = function () {
  return this.downState;
}

DvtButton.prototype.getOverState = function () {
  return this.overState;
}

/**
 * A logical object for a set of displayables.
 * @interface
 */
var DvtLogicalObject = function() {};

DvtObj.createSubclass(DvtLogicalObject, DvtObj, "DvtLogicalObject");

/**
 * Returns the array of DvtDisplayables corresponding to this object.
 * @return {array}
 */
DvtLogicalObject.prototype.getDisplayables = function() {};
/**
 * An object that displays tooltips.
 * @interface
 */
var DvtTooltipSource = function() {};

DvtObj.createSubclass(DvtTooltipSource, DvtObj, "DvtTooltipSource");

/**
 * Returns the tooltip text for this object.
 * @param {DvtDisplayable} target The displayable that was the target of the event.
 * @return {string} The tooltip text for this object.
 */
DvtTooltipSource.prototype.getTooltip = function(target, x, y) {};

/**
 * Returns the datatip text for this object.
 * @param {DvtDisplayable} target The displayable that was the target of the event.
 * @return {string} The datatip text for this object.
 */
DvtTooltipSource.prototype.getDatatip = function(target, x, y) {};

/**
 * Returns the border color of the datatip for this object.
 * @return {string} The datatip border color.
 */
DvtTooltipSource.prototype.getDatatipColor = function() {};
/**
 * An object that can be selected.
 * @interface
 */
var DvtSelectable = function() {};

DvtObj.createSubclass(DvtSelectable, DvtObj, "DvtSelectable");

/**
 * Returns true if this object is selectable.
 * @return {boolean} true if this object is selectable.
 */
DvtSelectable.prototype.isSelectable = function() {};

/**
 * Returns true if this object is selected.
 * @return {boolean} true if this object is selected.
 */
DvtSelectable.prototype.isSelected = function() {};

/**
 * Specifies whether this object is selected.
 * @param {boolean} selected true if this object is selected.
 * @protected
 */
DvtSelectable.prototype.setSelected = function(selected) {};

/**
 * Displays the hover effect.
 */
DvtSelectable.prototype.showHoverEffect = function() {};

/**
 * Hides the hover effect.
 */
DvtSelectable.prototype.hideHoverEffect = function() {};
/**
 * An object that can handle keyboard events delegated to it by a DvtEventManager
 * @interface
 */
var DvtComponentKeyboardHandler = function() {};


/**
 * Process a keyboard event
 * @param {DvtKeyboardEvent} event
 * @return {Boolean} True if this DvtComponentKeyboardHandler consumed the event
 */
DvtComponentKeyboardHandler.prototype.handleKeyboardEvent = function(event) {};

/**
 * Hide the component's keyboard focus effect.  Used by the DvtEventManager
 * to hide keyboard focus on one component when another component receives
 * focus via mouse (e.g., keyboard focus is on breadcrumbs but then you mouse
 * click on a treemap node).
 */
DvtComponentKeyboardHandler.prototype.hideKeyboardFocusEffect = function() {};
/**
 * An object that can receive keyboard focus, and thus can be navigated to via the keyboard
 * @interface
 */
var DvtKeyboardNavigable = function() {};

DvtObj.createSubclass(DvtKeyboardNavigable, DvtObj, "DvtKeyboardNavigable");

/**
 * Returns the next item to receive keyboard focus
 * @param {DvtBaseEvent} event
 * @return {Object} The next item to get keyboard focus
 */
DvtKeyboardNavigable.prototype.getNextNavigable = function(event) {};

/**
 * Returns the bounding box for this navigable item
 * @return {DvtRectangle}
 */
DvtKeyboardNavigable.prototype.getKeyboardBoundingBox = function() {};

/**
 * Show a visual effect to indicate if this DvtKeyboardNavigable has keyboard focus
 */
DvtKeyboardNavigable.prototype.showKeyboardFocusEffect = function() {};

/**
 * Hide the visual effect to indicate if this DvtKeyboardNavigable has keyboard focus
 */
DvtKeyboardNavigable.prototype.hideKeyboardFocusEffect = function() {};

/**
 * Returns true if this navigable is showing its keyboard focus effect
 * @return {Boolean}
 */
DvtKeyboardNavigable.prototype.isShowingKeyboardFocusEffect = function() {};

/**
 * Optional method to implement. Returns the location for where a context menu should appear.
 * @return {DvtPoint}
 */
DvtKeyboardNavigable.prototype.getContextMenuLocation = function() {};
/**
 * An object that can belongs to a category.  
 * @interface
 */
var DvtCategoricalObject = function() {};

DvtObj.createSubclass(DvtCategoricalObject, DvtObj, "DvtCategoricalObject");

/**
 * Returns an array containing all categories to which this object belongs.
 * @return {array} The array of categories.
 */
DvtCategoricalObject.prototype.getCategories = function() {};
/**
 * An object that displays context menus.
 * @interface
 */
var DvtContextMenuSource = function() {};

DvtObj.createSubclass(DvtContextMenuSource, DvtObj, "DvtContextMenuSource");

/**
 * Returns the id of the specific context menu to display for the object.
 * @return {object} The menu id to pass into the context menu handler.
 */
DvtContextMenuSource.prototype.getContextMenuId = function() {};

/**
 * Returns the x,y coordinate of the upper left hand corner for the context menu, when the context menu
 * is triggered by a keyboard shortcut
 * Optional method to implement.
 * @return {DvtPoint} 
 */
DvtContextMenuSource.prototype.getContextMenuLocation = function() {};
/**
 * An object that displays popups.
 * @interface
 */
var DvtPopupSource = function() {};

DvtObj.createSubclass(DvtPopupSource, DvtObj, "DvtPopupSource");

/**
 * Returns the server side id for this object, which is used to set context on
 * the server.
 * @return {string} The server side id for this object.
 */
DvtPopupSource.prototype.getId = function() {};

/**
 * Returns the array of showPopupBehaviors for the object.
 * @return {array} The array of applicable DvtShowPopupBehaviors
 */
DvtPopupSource.prototype.getShowPopupBehaviors = function() {};
/**
 * For changing z-order of an object
 * @interface
 */
var DvtZOrderManager = function() {};

DvtObj.createSubclass(DvtZOrderManager, DvtObj, "DvtZOrderManager");

/**
 * Bring the object to the front of the z-order.
 *
 * @param {DvtDisplayable}  displayable  object to push back in the z-order
 */
DvtZOrderManager.prototype.bringToFrontOfSelection = function(displayable) {};

/**
 * Push the object to the back of the selected objects in the z-order.
 *
 * @param {DvtDisplayable}  displayable  object to push back in the z-order
 */
DvtZOrderManager.prototype.pushToBackOfSelection = function(displayable) {};

/**
 * Set the number of objects that must be kept in front of any selected
 * objects in the z-order.  
 * 
 * @param {number} num  number of objects to keep in front of selected objects
 */
DvtZOrderManager.prototype.setNumFrontObjs = function(num) {};
/**
 * An object that contains children that can be accessed by test automation 
 * @interface
 */
var DvtAutomationContainer = function() {};

DvtObj.createSubclass(DvtAutomationContainer, DvtObj, "DvtAutomationContainer");

/**
 * Dispatches an event triggered by test automation on the given subId
 * @param {Number} subId
 * @param {String} eventType
 * @param {String} params Parameters to initialize the event 
 */
DvtAutomationContainer.prototype.dispatchTestEvent = function(subId, eventType, params) {}; 

/**
 * Simple logical object for tooltip support.
 * @param {string} tooltip The tooltip to display.
 * @param {string} datatip The datatip to display.
 * @param {string} datatipColor The border color of the datatip.
 * @param {object} [params] Optional object containing additional parameters for use by component.
 * @class
 * @constructor
 * @implements {DvtTooltipSource}
 */
var DvtSimpleObjPeer = function(tooltip, datatip, datatipColor, params) {
  this.Init(tooltip, datatip, datatipColor, params);
}

DvtObj.createSubclass(DvtSimpleObjPeer, DvtObj, "DvtSimpleObjPeer");

/**
 * @param {string} tooltip The tooltip to display.
 * @param {string} datatip The datatip to display.
 * @param {string} datatipColor The border color of the datatip.
 * @param {object} [params] Optional object containing additional parameters for use by component.
 */
DvtSimpleObjPeer.prototype.Init = function(tooltip, datatip, datatipColor, params) {
  this._tooltip = tooltip;
  this._datatip = datatip;
  this._datatipColor = datatipColor;
  this._params = params;
}

/**
 * Specifies the tooltip for this object.
 * @param {string} tooltip
 */
DvtSimpleObjPeer.prototype.setTooltip = function(tooltip) {
  this._tooltip = tooltip;
}

/**
 * Specifies the datatip for this object.
 * @param {string} datatip
 */
DvtSimpleObjPeer.prototype.setDatatip = function(datatip) {
  this._datatip = datatip;
}

/**
 * Specifies the datatip color for this object.
 * @param {string} datatipColor
 */
DvtSimpleObjPeer.prototype.setDatatipColor = function(datatipColor) {
  this._datatipColor = datatipColor;
}

/**
 * Returns additional parameters for this object, if available.
 * @return {object}
 */
DvtSimpleObjPeer.prototype.getParams = function() {
  return this._params;
};

//---------------------------------------------------------------------//
// Tooltip Support: DvtTooltipSource impl                              //
//---------------------------------------------------------------------//

/**
 * @override
 */
DvtSimpleObjPeer.prototype.getTooltip = function(target) {
  return this._tooltip;
};

/**
 * @override
 */
DvtSimpleObjPeer.prototype.getDatatip = function(target) {
  return this._datatip;
};

/**
 * @override
 */
DvtSimpleObjPeer.prototype.getDatatipColor = function(target) {
  return this._datatipColor;
};
var DvtAgent = function () {
}

DvtObj.createSubclass(DvtAgent, DvtObj, "DvtAgent");

// Version Constants
DvtAgent.UNKNOWN_VERSION = -1;

// Platform Constants
DvtAgent.IE_PLATFORM = "ie";
DvtAgent.FLASH_PLATFORM = "flash";
DvtAgent.GECKO_PLATFORM = "gecko";
DvtAgent.WEBKIT_PLATFORM = "webkit";
DvtAgent.UNKNOWN_PLATFORM = "unknown";

// OS Constants
DvtAgent.WINDOWS_OS = "Windows";
DvtAgent.SOLARIS_OS = "Solaris";
DvtAgent.MAC_OS = "Mac";
DvtAgent.UNKNOWN_OS = "Unknown";

/**
 * Returns the ratio between physical pixels and device independent pixels for the current device.
 * @return {number} the device pixel ratio
 */
DvtAgent.getDevicePixelRatio = function () {
  DvtAgent._initialize();
  return DvtAgent._devicePixelRatio;
}

/**
 * Returns the position of the specified DOM element in page coordinates.
 * @param {object} element
 * @return {DvtPoint}
 */
DvtAgent.getElementPosition = function (element) {
  if(DvtAgent.getPlatform() == DvtAgent.FLASH_PLATFORM)
    return new DvtPoint();
  else if(DvtAgent.isPlatformIE())
    return DvtAgent._getElementPositionIE(element);
  else if(DvtAgent.isPlatformGecko())
    return DvtAgent._getElementPositionGecko(element);
  else
    return DvtAgent._getElementPositionDefault(element);
}

/**
 * Returns the platform string.  Valid values are DvtAgent.***_PLATFORM.
 * @return {string}
 */
DvtAgent.getPlatform = function () {
  DvtAgent._initialize();
  return DvtAgent._platform;
}

/**
 * Returns a guess of the operating system based on the user agent.
 * @return {string} 
 */
DvtAgent.getOS = function () {
  DvtAgent._initialize();
  return DvtAgent._os;
}

/**
 * Returns the version of the browser reported by the user agent.
 * @return {number} 
 */
DvtAgent.getVersion = function () {
  DvtAgent._initialize();
  return DvtAgent._version;
}

/**
 * Returns true if rendering in Chrome.
 * @return {boolean}
 */
DvtAgent.isBrowserChrome = function () {
  DvtAgent._initialize();
  return DvtAgent._bChrome;
}

/**
 * Returns true if rendering in Safari.
 * @return {boolean}
 */
DvtAgent.isBrowserSafari = function () {
  return DvtAgent.isPlatformWebkit() && !DvtAgent.isBrowserChrome();
}

/**
 * Returns true if rendering in Gecko.
 * @return {boolean}
 */
DvtAgent.isPlatformGecko = function () {
  return DvtAgent.getPlatform() == DvtAgent.GECKO_PLATFORM;
}

/**
 * Returns true if rendering in IE.
 * @return {boolean}
 */
DvtAgent.isPlatformIE = function () {
  return DvtAgent.getPlatform() == DvtAgent.IE_PLATFORM;
}

/**
 * Returns true if rendering in Webkit.
 * @return {boolean}
 */
DvtAgent.isPlatformWebkit = function () {
  return DvtAgent.getPlatform() == DvtAgent.WEBKIT_PLATFORM;
}

/**
 * Returns true if rendering in a right to left locale.
 * @param {DvtContext} [context] If specified, first tries to determine the reading direction from the context.
 * @return {boolean}
 */
DvtAgent.isRightToLeft = function (context) {
  DvtAgent._initialize();
  if (context && context.getReadingDirection() != null)
    return context.getReadingDirection() == "rtl";
  else 
    return DvtAgent._bRtl;
}

/**
 * Returns true if rendering on a touch device.
 * @return {boolean}
 */
DvtAgent.isTouchDevice = function () {
  DvtAgent._initialize();
  return DvtAgent._bTouchDevice;
}


/**
 * Sets the current environment type
 * @param {String} type The environment type.  Currently supports 'batik' and 'test'
 * @export
 */
DvtAgent.setEnvironment = function (type) {
  DvtAgent._environment = type;
}

/**
 * Returns whether the current environment is test
 * @return {boolean} 
 */
DvtAgent.isEnvironmentTest = function () {
  return DvtAgent._environment == 'test';
}

/**
 * Returns whether the current environment is batik
 * @return {boolean}
 */
DvtAgent.isEnvironmentBatik = function () {
  return DvtAgent._environment == 'batik';
}

/**
 * Returns whether the current environment is browser
 * @return {boolean}
 */
DvtAgent.isEnvironmentBrowser = function () {
  return !DvtAgent.isEnvironmentTest() && !DvtAgent.isEnvironmentBatik();
}

/**
 * Initializes the agent.  This caches all the relevant agent parameters and must be called before accessing any of
 * the variables on DvtAgent.
 * @return {DvtAgent}
 * @private
 */
DvtAgent._initialize = function () {
  // Initialize DvtAgent if it hasn't been already.
  if(!DvtAgent._bInitialized) {  
    var userAgent = (navigator && navigator.userAgent) ? navigator.userAgent.toLowerCase() : null;

    DvtAgent._os = DvtAgent._determineOS(userAgent);
    DvtAgent._platform = DvtAgent._determinePlatform(userAgent);
    // Chrome must be initialized before _determineVersion()
    DvtAgent._bChrome = userAgent && (userAgent.indexOf('chrome/') != -1) && userAgent.indexOf('edge') === -1;
    DvtAgent._version = DvtAgent._determineVersion(userAgent, DvtAgent._platform);

    DvtAgent._bRtl = (document && document.documentElement) ? document.documentElement.dir == 'rtl' : false;
    DvtAgent._bTouchDevice = (DvtAgent._platform == DvtAgent.WEBKIT_PLATFORM) && userAgent &&
                             (userAgent.indexOf('mobile') != -1 || userAgent.indexOf('android') != -1);
    DvtAgent._devicePixelRatio = (window && window.devicePixelRatio != null) ? window.devicePixelRatio : 1;

    // Don't initialize again
    DvtAgent._bInitialized = true;
  }
}


/**
 * Determines the operating system. This value should be cached to prevent costly calculations. This value should be 
 * treated as a guess, as this code is copied from AdfAgent.guessOS().
 * @param {string} userAgent The lowercase user agent string, if available.
 * @return {string} The DvtAgent.***_OS constant describing the platform.
 * @private
 */
DvtAgent._determineOS = function(userAgent)
{  
  if(userAgent) {
    if (userAgent.indexOf('win')!= -1)
      return DvtAgent.WINDOWS_OS;
    else if (userAgent.indexOf('mac') != -1)
      return DvtAgent.MAC_OS;
    else if (userAgent.indexOf('sunos') != -1)
      return DvtAgent.SOLARIS_OS;
  }
  
  return DvtAgent.UNKNOWN_OS;
}

/**
 * Determines the name of the platform. This value should be cached to prevent costly calculations.
 * @param {string} userAgent The lowercase user agent string, if available.
 * @return {string} The DvtAgent.***_PLATFORM constant describing the platform.
 * @private
 */
DvtAgent._determinePlatform = function(userAgent)
{  
  // window is always defined when running in the browser.  It is not defined when running in Java ScriptEngine.
  if(window) {
    if(window.isFlashEnvironment)
      return DvtAgent.FLASH_PLATFORM;
  
    if(userAgent.indexOf("opera") != -1) // check opera first, since it mimics other browsers
      return DvtAgent.UNKNOWN_PLATFORM;
    else if (userAgent.indexOf("trident") != -1 || userAgent.indexOf("msie")!= -1 || userAgent.indexOf('edge') != -1)
      return DvtAgent.IE_PLATFORM;
    else if ((userAgent.indexOf("applewebkit") != -1) || (userAgent.indexOf("safari") != -1))
      return DvtAgent.WEBKIT_PLATFORM;
    else if(userAgent.indexOf("gecko/") != -1)
      return DvtAgent.GECKO_PLATFORM;
  } else {
    if (DvtAgent.isEnvironmentTest())
      return DvtAgent.WEBKIT_PLATFORM;
  }
  return DvtAgent.UNKNOWN_PLATFORM;
}

/**
 * Determines the version of the platform. This value should be cached to prevent costly calculations.
 * @param {string} userAgent The lowercase user agent string, if available.
 * @param {string} platform The DvtAgent.***_PLATFORM constant describing the platform.
 * @return {number} The version number.
 * @private
 */
DvtAgent._determineVersion = function(userAgent, platform)
{  
  if(userAgent) {
    if(platform == DvtAgent.GECKO_PLATFORM)
      return DvtAgent._parseFloatVersion(userAgent, /rv:(\d+[.]\d+)/);
    else if (platform == DvtAgent.IE_PLATFORM) {
      var tridentVersion = DvtAgent._parseFloatVersion(userAgent, /trident\/(\d+[.]\d+)/);
      // Match found, resolve to IE version number, which is trident version + 4
      if (tridentVersion != DvtAgent.UNKNOWN_VERSION)
        return tridentVersion + 4;
      // Edge
      var edgeVersion = DvtAgent._parseFloatVersion(userAgent, /edge\/(\d+[.]\d+)/);
      if (edgeVersion != DvtAgent.UNKNOWN_VERSION)
        return edgeVersion;

      var msieVersion = DvtAgent._parseFloatVersion(userAgent, /msie (\d+\.\d+);/);
      if (msieVersion != DvtAgent.UNKNOWN_VERSION)
        return msieVersion;

      return DvtAgent._parseFloatVersion(userAgent, /msie (\d+\.\d+)b;/);
    }
    else if(platform == DvtAgent.WEBKIT_PLATFORM)
      return DvtAgent._parseFloatVersion(userAgent, /applewebkit\/(\d+([.]\d+)*)/);
  } else {
    if (DvtAgent.isEnvironmentTest())
      return 999;
  }
  
  return DvtAgent.UNKNOWN_VERSION;
}

/**
 * Parses the version from the specified agent string using the regular expression.
 * @param {string} userAgent The lowercase user agent string.
 * @param {regexp} versionNumberPattern The regular expression used to parse the version number.
 * @return {number}
 * @private
 */
DvtAgent._parseFloatVersion = function (userAgent, versionNumberPattern)
{
  try {
    var matches = userAgent.match(versionNumberPattern);
    return (matches && matches[1]) ? parseFloat(matches[1]) : DvtAgent.UNKNOWN_VERSION;
  }
  catch(e) {
    // Technically we should not assume console exists, but it will in all cases where we call this function.
    console.error("DvtAgent._parseFloatVersion error: ");
    console.error(e);
    return DvtAgent.UNKNOWN_VERSION;
  }
}

/**
 * Returns the position of an HTML element relative to the document body.
 * @param {object} element The HTML element.
 * @return {DvtPoint}
 * @private
 */
DvtAgent._getElementPositionGecko = function(element)
{
  // Note: This code was copied from AdfAgent and is not cleaned up for purposes of maintainability and comparison.
  //AdfAssert.assertDomElement(element);
  var boundingRect = element.getBoundingClientRect();
  // top and bottom are not rounded off in Gecko1.9
  // http://www.quirksmode.org/dom/w3c_cssom.html#elementviewm
  var elemTop = Math.round(boundingRect.top);
  var elemLeft = boundingRect.left;
  var docElement = element.ownerDocument.documentElement;
  // clientLeft and clientTop would be 0 for Gecko1.9
  // https://bugzilla.mozilla.org/show_bug.cgi?id=174397#c34
  elemLeft += docElement.scrollLeft;
  elemTop += docElement.scrollTop;
  return new DvtPoint(elemLeft, elemTop);
}

/**
 * Returns the position of an HTML element relative to the document body.
 * @param {object} element The HTML element.
 * @return {DvtPoint}
 * @private
 */
DvtAgent._getElementPositionIE = function(element)
{
  // Note: This code was copied from AdfAgent and is not cleaned up for purposes of maintainability and comparison.
  //AdfAssert.assertDomElement(element);
  var boundingRect = element.getBoundingClientRect();
  var elemTop = boundingRect.top;
  var elemLeft = boundingRect.left;
  var docElement = element.ownerDocument.documentElement;
  var scrollLeft = docElement.scrollLeft;

  // RTL adjustment for IE scrolled view
  //if(AdfPage.PAGE.getLocaleContext().isRightToLeft())
  //  scrollLeft += docElement.clientWidth - docElement.scrollWidth;

  // adjust for the document scroll positions and window borders
  elemLeft -= (docElement.clientLeft - scrollLeft);
  elemTop -= (docElement.clientTop - docElement.scrollTop);
  return {x:elemLeft, y:elemTop};
}

/**
 * Returns the position of an HTML element relative to the document body.
 * @param {object} element The HTML element.
 * @return {DvtPoint}
 * @private
 */
DvtAgent._getElementPositionDefault = function(element)
{ 
  // Note: This code was copied from AdfAgent and is not cleaned up for purposes of maintainability and comparison.
  // =-= VG Default implementation for Safari/Opera
  var bodyElement = element.ownerDocument.body;

  var currParent = element.offsetParent;
  var currLeft   = element.offsetLeft;
  var currTop   = element.offsetTop;
  //In safari/opera position absolute incorrectly account for body offsetTop
  //if (this.getComputedStyle(element).position == "absolute")
  //{
  //  currTop -= bodyElement.offsetTop;
  //}

  // store cssLengthAsInt function in local
  //var cssLengthAsInt = AdfAgent.getCSSLengthAsInt;

  while (currParent)
  {
    element = currParent;
    currLeft += element.offsetLeft;
    currTop += element.offsetTop;
    if (element != bodyElement)
    {
      //adjust for border
      //var style = this.getComputedStyle(element);
      //currLeft += cssLengthAsInt(style.borderLeftWidth);
      //currTop += cssLengthAsInt(style.borderTopWidth);

      currLeft -= element.scrollLeft;
      currTop -= element.scrollTop;
    }

    currParent = currParent.offsetParent;
  }

  return {x:currLeft, y:currTop};
}
/**
 * Resource bundle base class.
 * @class
 * @constructor
 * @extends {DvtObj}
 * @export
 */
var DvtBundle = function() {}
DvtBundle["_localizedStrings"] = {};

DvtObj.createSubclass(DvtBundle, DvtObj, "DvtBundle");

/**
 * Adds localized resources into DvtBundle. Called by the resource bundle implementations to register localized strings.
 * Example: 
 * var DvtChartBundle_CS = {
 *   "DvtChartBundle.LABEL_LEGEND" : "Legenda"
 * }
 * DvtBundle.addLocalizedStrings(DvtChartBundle_CS);
 * @param {object} object containing key - value pairs to be added
 * @export
 */
DvtBundle.addLocalizedStrings = function(jsonObj) {
  for(key in jsonObj) {
    DvtBundle["_localizedStrings"][key] = jsonObj[key];
  } 
}

/**
 * Returns localized string for given key.
 * @private
 * @param {string} key
 * @return {string} localized string for given key
 */
DvtBundle.prototype._getLocalizedStringForKey = function(key) {
  var fqKey = this._getFQKey(key);
  return DvtBundle["_localizedStrings"][fqKey];
}

/**
 * Returns bundle prefix. This prefix specifies the prefix of all keys in a localized bundle,
 * e.g. in case of localized dvt gauge bundle the key prefix is 'DvtGaugeBundle' since every key
 * is of the form 'DvtGaugeBundle.KEY_NAME'.
 * This method is called by the getTranslatedString function to construct fully qualified key and is intended to be overriden by descendands.
 * @protected
 * @return {string} bundle prefix
 */
DvtBundle.prototype.GetBundlePrefix = function() {}

/**
 * Returns default string for given key. This method is intended to be overriden by descendands.
 * @protected
 * @param {string} key
 * @return {string} default string for given key
 */
DvtBundle.prototype.GetDefaultStringForKey = function(key) {}

/**
 * Substitutes the replacement objects into the specified pattern.
 * @param {string} pattern The pattern string with placeholders.
 * @param {array} replacements The array of replacement strings.
 * @return {string} The resulting string.
 */
DvtBundle.format = function(pattern, replacements) {
  return pattern.replace(/\{(\d+)\}/g, function() {return replacements[arguments[1]];});
}

/**
 * Returns a translated string with replacements. If an array of replacement
 * strings is provided, these strings will be substituted into the translated string.
 * @param {string} key The key in the message bundle.
 * @param {replacements} replacements The array of replacement strings.
 * @return {string} The translated string.
 */ 
DvtBundle.prototype.getTranslatedString = function(key, replacements) {
  var ret = this._getLocalizedStringForKey(key);
  if(!ret)
    ret = this.GetDefaultStringForKey(key);
  
  // Substitute the replacements if provided
  if(ret && replacements !== null && replacements !== undefined) {
    if(replacements instanceof Array)
      ret = DvtBundle.format(ret, replacements);
    else // Wrap a single object for convenience
      ret = DvtBundle.format(ret, [replacements]);
  }
    
  return ret; 
}

/**
 * Returns a fully qualified key.
 * @private
 * @param {string} key The key in the message bundle.
 * @return {string} The fully qualified key.
 */ 
DvtBundle.prototype._getFQKey = function(key) {
  var bPrefix = this.GetBundlePrefix();
  var fqKey = key;
  if(key.length <= bPrefix.length || !(key.substring(0, bPrefix.length) === bPrefix)) {
    fqKey = bPrefix + "." + key;
  }
  return fqKey;
}
/**
 * Interactivity manager for context menu support.
 * @param {DvtContext} context The platform specific context object.
 * @class DvtContextMenuHandler
 * @constructor
 */
var DvtContextMenuHandler = function(context) {
  this.Init(context);
};

DvtObj.createSubclass(DvtContextMenuHandler, DvtObj, "DvtContextMenuHandler");

DvtContextMenuHandler.TYPE_BODY_CONTEXT_MENU = "bodyContextMenu";
DvtContextMenuHandler.TYPE_CONTEXT_MENU = "contextMenu";
DvtContextMenuHandler.TYPE_MULTI_SELECT_CONTEXT_MENU = "multiSelectContextMenu";

DvtContextMenuHandler._ATTR_MENU_CONTAINER_TYPE = "t";
DvtContextMenuHandler._ATTR_MENU_CONTAINER_POPUP_ID = "id";
DvtContextMenuHandler._ATTR_MENU_ITEMS = "i";
// Menu Item Attrs
DvtContextMenuHandler._ATTR_ITEM_TEXT = "t";
DvtContextMenuHandler._ATTR_ITEM_DISABLED = "di";
DvtContextMenuHandler._ATTR_ITEM_SEP_BEFORE = "sb";
// CommandMenuItem Attrs
DvtContextMenuHandler._ATTR_ITEM_CLIENT_ID = "id";
// GoMenuItem Attrs
DvtContextMenuHandler._ATTR_ITEM_DESTINATION = "d";
DvtContextMenuHandler._ATTR_ITEM_TARGET_FRAME = "tf";

DvtContextMenuHandler.prototype.Init = function(context) {
  this._context = context;
  this._xmlNodes = new Array();
  // Create the impl object
  this._impl = context.getImplFactory().newContextMenuHandler();
  this._impl.setObj(this);
}

/**
 * Adds the specified context menu definition to this handler.
 * @param {DvtXmlNode} xmlNode The xml node defining the context menu.
 */
DvtContextMenuHandler.prototype.add = function(xmlNode) {
  this._xmlNodes.push(xmlNode);  
}

/**
 * Displays the context menu with the specified type and id.  Depending on the rendering context,
 * a context menu will either be shown immediately, or a contextMenuEvent will be returned.  If
 * a contextMenuEvent is returned, it should be dispatched through the event callback mechanism.
 * @param {object} event The event that is triggering the context menu.
 * @param {string} menuType The type of context menu being requested, such as bodyContextMenu.
 * @param {string} menuId The id of the specific menu being requested.
 * @return {DvtGraphContextMenuEvent} A context menu event to dispatch or null, depending on the rendering context.
 */
DvtContextMenuHandler.prototype.show = function(event, menuType, menuId) {
  return this._impl.getMenuEvent(this._xmlNodes, event, menuType, menuId);
}

DvtContextMenuHandler.prototype.prepareMenuItems = function(event, menuType, menuId) {
  this._impl.prepareMenuItems(event, menuType, menuId);
}

/**
 * Returns the array of menu items for the specified menu type and menu id.
 * @param {string} menuType The type of context menu being requested, such as bodyContextMenu.
 * @param {string} menuId The id of the specific menu being requested.
 * @return {array} The array of DvtContextMenuItems.
 */
DvtContextMenuHandler.prototype.getMenuItems = function(menuType, menuId) {
  if (!menuId)
    menuId = 0;
  if (menuType === DvtContextMenuHandler.TYPE_BODY_CONTEXT_MENU || menuType === DvtContextMenuHandler.TYPE_MULTI_SELECT_CONTEXT_MENU) {
    menuId = 0;
  }
  // Look through all context menu definitions for the right context menu to show
  for(var i=0; i<this._xmlNodes.length; i++) {
    var xmlNode = this._xmlNodes[i];
    if(menuType == xmlNode.getAttr(DvtContextMenuHandler._ATTR_MENU_CONTAINER_TYPE)) {
      if(menuId != null && menuId >= 0) {
        // menuNode is the menu definition that we are looking for
        var childNodes = xmlNode.getChildNodes();
        if(childNodes && childNodes.length > menuId) {
          var menuNode = childNodes[menuId];
          // Parse the list of items and return them
          var itemsStr = menuNode.getAttr(DvtContextMenuHandler._ATTR_MENU_ITEMS);
          if(itemsStr) {
            // itemsContainer is the xml node containing all menu item definitions
            var itemsContainer = childNodes[childNodes.length-1];
            
            // menuItemNodes is the array of all menu items
            var menuItemNodes = itemsContainer.getChildNodes();
            
            // targetIds is the array of menu items to be added into this menu
            var targetIds = itemsStr.split(",");
            
            // Gather all target menu items and return them
            var targetMenuItems = new Array();
            for(var j=0; j<targetIds.length; j++) {
              var menuItemXmlNode = menuItemNodes[targetIds[j]];
              
              // Parse the display attributes
              var text = menuItemXmlNode.getAttr(DvtContextMenuHandler._ATTR_ITEM_TEXT);
              var disabled = menuItemXmlNode.getAttr(DvtContextMenuHandler._ATTR_ITEM_DISABLED);
              var sepBefore = menuItemXmlNode.getAttr(DvtContextMenuHandler._ATTR_ITEM_SEP_BEFORE);
              
              // Create the event that would be fired on select
              var clientId = menuItemXmlNode.getAttr(DvtContextMenuHandler._ATTR_ITEM_CLIENT_ID);
              var destination = menuItemXmlNode.getAttr(DvtContextMenuHandler._ATTR_ITEM_DESTINATION);
              var targetFrame = menuItemXmlNode.getAttr(DvtContextMenuHandler._ATTR_ITEM_TARGET_FRAME);
              var event = new DvtGraphContextMenuSelectEvent(menuType, clientId, destination, targetFrame);
              
              // Add the menu item
              targetMenuItems.push(new DvtContextMenuItem(event, text, disabled, sepBefore));
            }
            
            return targetMenuItems;
          }
        }
      }
    }
  }
  return null;
}

/**
 * Returns the DvtContext associated with this handler.
 * @return {DvtContext}
 */
DvtContextMenuHandler.prototype.getCtx = function() {
  return this._context;
}

/**
 * Dispatches an event to the external interface
 * @param {DvtContextmenuSelectEvent} event The event to dispatch
 * @protected
 */
DvtContextMenuHandler.prototype.DispatchContextMenuSelectEvent = function(event) {
  this.getCtx().getJsExt().dispatchEvent(null, null, null, event);
}
/**
 * Interactivity manager for popup support.
 * @param {DvtContext} context The platform specific context object.
 * @param {function} callback A function that responds to delayed popup events
 * @param {object} callbackObj The optional object instance that the callback function is defined on.
 * @class DvtPopupBehaviorHandler
 * @constructor
 */
var  DvtPopupBehaviorHandler = function(context, callback, callbackObj) {
  this._context = context;
  this._callback = callback;
  this._callbackObj = callbackObj;
  
  // Initialize the hover params
  this._popupPosition = null;   // The most recent position, useful for placing the popup
  this._hoverTarget = null;      // The current hover target
  this._hoverBehavior = null;    // The current showPopupBehavior for the hover
  this._hoverPopupShown = false; // true if a hover popup is being shown
  this._hoverTimer = new DvtTimer(context, 500, this._onHoverDelay, this, 1);
};

DvtObj.createSubclass(DvtPopupBehaviorHandler, DvtObj, "DvtPopupBehaviorHandler");

// TODO define an interface for the targets of the popup handler functions
// 1. getPopupBounds()
// 2. getId()

/**
 * Processes a click event and fires a DvtGraphShowPopupEvent if a popup should be shown.
 * @param {object} target The target of the mouse event.
 * @param {array} behaviors The array of applicable show popup behaviors.
 * @param {DvtPoint} position The position for placing the popup.
 */
DvtPopupBehaviorHandler.prototype.processClick = function(target, behaviors, position) {
  var consumed = false;
  if(target && behaviors && behaviors.length>0) 
  {
    for(var i=0; i<behaviors.length; i++) {
      var behavior = behaviors[i];
      if(behavior && (behavior.getTriggerType() == DvtGraphShowPopupBehavior.TRIGGER_TYPE_ACTION ||
                      behavior.getTriggerType() == DvtGraphShowPopupBehavior.TRIGGER_TYPE_CLICK)) {
        //BUG FIX 15851577: hide an existing popup before showing a new one
        this.HidePopup();
        // Create and fire the popup event
        var popupEvent = this._createShowPopupEvent(target, behavior, position);
        DvtEventDispatcher.dispatchEvent(this._callback, this._callbackObj, this, popupEvent);
        consumed = true;
      }
    }
  }

  return consumed;
};

/**
 * Processes a mouseOver event.
 * @param {object} target The target of the mouse event.
 * @param {array} behaviors The array of applicable show popup behaviors.
 * @param {DvtPoint} position The position for the popup.
 * @param <DvtDisplayable> displayable The DvtDisplayable associated with the logical target of the event.
 */
DvtPopupBehaviorHandler.prototype.processMouseOver = function(target, behaviors, position, displayable) {
  // Same target, no action needed
  if(target === this._hoverTarget)
    return true;
  
  // Different target, look for applicable showPopupBehavior
  if(target && behaviors && behaviors.length>0) 
  {
    for(var i=0; i<behaviors.length; i++) {
      var behavior = behaviors[i];
      if(behavior && behavior.getTriggerType() == DvtGraphShowPopupBehavior.TRIGGER_TYPE_MOUSE_HOVER) {
        //BUG FIX 15851577: hide an existing popup before showing a new one
        this.HidePopup();
        // Found hover behavior, store the info for use in callback and rollout
        this._hoverTarget = target;
        this._hoverBehavior = behavior;
        //BUG FIX 15851577: save the displayable so we know when to hide a mousehover popup
        this._hoverDisplayable = displayable;
        
        this._popupPosition = position;
        // Start the hover timer
        this._hoverTimer.start();
        return true;
      }
    }
  }
  return false;
};

/**
 * Processes a mouseOver event and fires a DvtGraphHidePopupEvent if a popup is to be hidden.
 * @param {object} target The target of the mouse event.
 * @param {array} displayableHierarchy The hierarchy of DvtDisplayables associated with the relatedTarget of the
 * mouseout event
 */
DvtPopupBehaviorHandler.prototype.processMouseOut = function(target, displayableHierarchy) {
  //BUG FIX 15851577: if we're mousing out of the whole component, hide the popup
  var bLeavingComponent = false;
  if (displayableHierarchy) {
    bLeavingComponent = (displayableHierarchy.length < 1);
  }
  // Continue only if a hover popup was processed for this target
  if(!this._hoverTarget || !this._hoverBehavior || (target !== this._hoverTarget && !bLeavingComponent))
    return;
  
  //BUG FIX 15851577: don't hide the popup if the DvtDisplayable that launched it is still in the
  //hierarchy of DvtDisplayables for the event's relatedTarget, in other words, if we haven't moused out
  //of the launching displayable entirely (for example, when mousing between children of the
  //launching displayable)
  if (displayableHierarchy && this._hoverDisplayable) {
    if (DvtArrayUtils.getIndex(displayableHierarchy, this._hoverDisplayable) > -1) {
      return;
    }
  }
  
  this.HidePopup();
};

/**
 * Hides a popup
 * @protected
 */
DvtPopupBehaviorHandler.prototype.HidePopup = function() {
  if(this._hoverPopupShown) {
    // Popup already shown, hide it
    var popupEvent = new DvtGraphHidePopupEvent(this._hoverBehavior.getPopupId());
    DvtEventDispatcher.dispatchEvent(this._callback, this._callbackObj, this, popupEvent);
  }
  else {
    // Popup not shown yet, just stop the timer
    this._hoverTimer.stop();
  }
  
  // Clear out the fields
  this._hoverTarget = null;
  this._hoverBehavior = null;
  this._hoverPopupShown = false;
  this._hoverDisplayable = null;
};

/**
 * Processes a mouseMove event.  The most recent position is stored to help position
 * hover popups relative to the mouse.
 * @param {DvtPoint} position The position for the popup.
 */
DvtPopupBehaviorHandler.prototype.processMouseMove = function(position) {
  this._popupPosition = position;
}

/**
 * Helper function used to create the DvtGraphShowPopupEvent.
 * @param {object} target The target of the mouse event.
 * @param {DvtGraphShowPopupBehavior} behavior The show popup behavior that is being fired.
 * @param {DvtPoint} position The position for placing the popup.
 * @return {DvtGraphShowPopupEvent} The showPopupEvent, if a popup should be shown.
 * @private
 */
DvtPopupBehaviorHandler.prototype._createShowPopupEvent = function(target, behavior, position) {
  //FIX BUG 14498642: only align to the moue when align information is not specified if this._bForceAlignToMouse is true
  
  // If align and alignId are not defined, position the popup with alignId="endAfter".  The original
  // showPopupBehavior is not modified.
  var spb = behavior;
  var behaviorForBounds = behavior;
  var canAlignToMouse = spb.getAlign() && !spb.getAlignId();
  if (canAlignToMouse) {
    behaviorForBounds = new DvtGraphShowPopupBehavior(spb.getPopupId(), spb.getTriggerType(), null, null);
  }
  
  // Find the bounds that the popup should align to
  var launcherBounds = target.getPopupBounds ? target.getPopupBounds(behaviorForBounds) : null;
  if(canAlignToMouse && !launcherBounds) {
    // If the object doesn't specify a bounds, then align to the mouse
    var pos = this._context.getDocumentUtils().getStageRelativePosition(this._context.getStage(), position.x, position.y);
    launcherBounds = new DvtRectangle(pos.x, pos.y-5, 1, 5);
  }
  else if (launcherBounds) {
    //BUG FIX 13971862: if we have launcher bounds, then create a new behavior object and set the alignId
    //to null so that the JS peer will recognize that the popup should be aligned to a component
    //rendered by the toolkit and position the helper div accordingly
    spb = new DvtGraphShowPopupBehavior(spb.getPopupId(), spb.getTriggerType(), null, spb.getAlign());
  }

  var event = new DvtGraphShowPopupEvent(spb, launcherBounds, null);
  // add the component specific context
  DvtEventManager._addContextInfo(target, event);

  return event;
}

/**
 * Callback function that is called by the timer when a hover popup should be shown.
 * @private
 */
DvtPopupBehaviorHandler.prototype._onHoverDelay = function() {
  if(!this._hoverTarget || !this._hoverBehavior)
    return;
  // Show the popup: Create the event and pass to the hover callback
  this._hoverPopupShown = true;
  var event = this._createShowPopupEvent(this._hoverTarget, this._hoverBehavior, this._popupPosition);
  DvtEventDispatcher.dispatchEvent(this._callback, this._callbackObj, this, event);
}
/**
 * Interactivity manager for dvt component client behaviors
 * @param {DvtContext} context The platform specific context object.
 * @param {function} callback A function that responds to client behavior events
 * @param {object} callbackObj The optional object instance that the callback function is defined on.
 * @class DvtClientBehaviorHandler
 * @constructor
 */
var DvtClientBehaviorHandler = function(context, callback, callbackObj) {
  this.Init(context, callback, callbackObj);
};

DvtObj.createSubclass(DvtClientBehaviorHandler, DvtObj, "DvtClientBehaviorHandler");

DvtClientBehaviorHandler.prototype.Init = function(context, callback, callbackObj) {
  this._context = context;
  this._callback = callback;
  this._callbackObj = callbackObj;      
};

/**
 * Processes a click event and fires a DvtClientBehaviorEvent if a client behavior was triggered.
 * @param {object} target The target of the mouse event.
 * @param {array} behaviors The array of applicable client behaviors.
 * @return {boolean} true if a client behavior was triggered, false otherwise
 */
DvtClientBehaviorHandler.prototype.processClick = function(target, behaviors) {
  var consumed = false;
  if(target && behaviors && behaviors.length>0) 
  {
    for(var i=0; i<behaviors.length; i++) {
      var behavior = behaviors[i];
      if(behavior && (behavior.getTriggerType() == DvtClientBehavior.TRIGGER_TYPE_ACTION ||
                      behavior.getTriggerType() == DvtClientBehavior.TRIGGER_TYPE_CLICK)) {
        // Create and fire the popup event
        var behaviorEvent = this.CreateClientBehaviorEvent(target, behavior);        
        DvtEventDispatcher.dispatchEvent(this._callback, this._callbackObj, null, behaviorEvent);
        consumed = true;
      }
    }
  }

  return consumed;
};

DvtClientBehaviorHandler.prototype.CreateClientBehaviorEvent = function(target, behavior) {
  var behaviorEvent = new DvtClientBehaviorEvent(behavior);
  DvtEventManager._addContextInfo(target, behaviorEvent);
  return behaviorEvent;
}
/**
  * Interactivity manager for selection.
  * @class DvtSelectionHandler
  * @constructor
  */
var  DvtSelectionHandler = function(type) {
  this.Init(type);
};

DvtObj.createSubclass(DvtSelectionHandler, DvtObj, "DvtSelectionHandler");

DvtSelectionHandler.TYPE_SINGLE   = "s";
DvtSelectionHandler.TYPE_MULTIPLE = "m";

DvtSelectionHandler.prototype.Init = function(type) {
  this._selection = [];
  this._type = type ? type : DvtSelectionHandler.TYPE_SINGLE;
  
  // Some selected id may not correspond to a drawn target because it's outside the viewport.
  this._hiddenSelectedIds = [];
} ;

DvtSelectionHandler.prototype.getType = function() {
  return this._type;
};

/**
 * Returns the number of currently selected objects.
 * @return {number}
 */
DvtSelectionHandler.prototype.getSelectedCount = function() {
  return this._selection.length;
};

/**
 * Returns the current selection.
 * @return {array} The current selection.
 */
DvtSelectionHandler.prototype.getSelection = function() {
  return this._selection.slice(0);
}

/**
 * Returns the ids for the currently selected objects. Includes the ids of the hidden selected objects.
 * @return {array} The ids for the currently selected objects.
 */
DvtSelectionHandler.prototype.getSelectedIds = function() {
  var selectedIds = [];
  for(var i=0; i<this._selection.length; i++) {
    selectedIds.push(this._selection[i].getId());
  }
  return selectedIds.concat(this._hiddenSelectedIds);
}

/**
 * Processes the initially selected objects, updating the state of this handler.
 * @param {array} selectedIds The array of ids for the selected objects.
 * @param {array} targets The array of selectable objects.
 */
DvtSelectionHandler.prototype.processInitialSelections = function(selectedIds, targets) {
  // Clear current selection state
  this.clearSelection();

  // If nothing selected, we are done
  if(!selectedIds || !targets)
    return;
    
  // Loop through all the selected ids, matching them to the targets  
  for(var i=0; i<selectedIds.length; i++) {
    var hidden = true;
    for(var j=0; j<targets.length; j++) {
      var targetId = targets[j].getId();
      if(targetId != null && DvtSelectionHandler._isEquals(selectedIds[i], targetId) && targets[j].isSelectable && targets[j].isSelectable()) {
        // Found a match, continue to next selected id
        this._addToSelection(targets[j], true);
        hidden = false;
        break;
      }
    }
    if (hidden)
      this._hiddenSelectedIds.push(selectedIds[i]);
  }
}

/**
 * Processes a click event.
 * @param {DvtSelectable} target
 * @param {boolean} addToExisting true if a key indicating multi-select should be performed was pressed during the click.
 * @return {boolean} true if the selection has changed.
 */
DvtSelectionHandler.prototype.processClick = function(target, addToExisting) {
  //BUG FIX 13381888: if this click is unrelated to selection, then
  //don't change selection at all
  if (target && target.isUnrelatedToSelection && target.isUnrelatedToSelection()) {
    return false;
  }
  
  // Check whether we are in multi-select mode
  var bMulti = (addToExisting && this._type == DvtSelectionHandler.TYPE_MULTIPLE);
  
  //*************************************************
  // Possible cases:
  // 1. Multi-select of selectable target
  // 2. Multi-select of non-selectable target (noop)
  // 3. Single select of selectable target
  // 4. Single select of non-selectable target
  //*************************************************
  
  var bChanged = false; // Keep track of whether the selection is changed
  if(bMulti) 
  {
    if(target && target.isSelectable && target.isSelectable()) {
      // 1. Multi-select of selectable target
      if(target.isSelected()) {
        bChanged = this.removeFromSelection(target);
      }
      else {
        bChanged = this._addToSelection(target, true);
      }
    }
    // Otherwise 2. Multi-select of non-selectable target (noop)
  }
  else // Single Select
  {
    if(target && target.isSelectable && target.isSelectable()) {
      // 3. Single select of selectable target
      bChanged = this._addToSelection(target, false);
    }
    else {
      // 4. Single select of non-selectable target
      bChanged = this.clearSelection();
    }
  }
  
  // Return whether the selection has changed.
  return bChanged;
};

/**
 * Processes selection event involving a group of target items.
 * @param {array} targets Array of DvtSelectable objects
 * @param {boolean} addToExisting true if a key indicating multi-select should be performed was pressed during the click.
 */
DvtSelectionHandler.prototype.processGroupSelection = function(targets, addToExisting) {
  if (!addToExisting)
    this.clearSelection();
  
  var target;
  for (var i = 0; i < targets.length; i++) {
    target = targets[i];
    
    //BUG FIX 13381888: if this click is unrelated to selection, then
    //don't change selection at all
    if (target && target.isUnrelatedToSelection && target.isUnrelatedToSelection()) {
      continue;
    }
    
    this._addToSelection(target, true);
  }
};

/**
 * Processes a mouseOver event.
 * @param {DvtSelectable} target
 */
DvtSelectionHandler.prototype.processMouseOver = function(target) {
  if(target && target.isSelectable && target.isSelectable() && target.showHoverEffect) {
    target.showHoverEffect();
  }
};

/**
 * Processes a mouseOut event.
 * @param {DvtSelectable} target
 */
DvtSelectionHandler.prototype.processMouseOut = function(target) {
  if(target && target.isSelectable && target.isSelectable() && target.hideHoverEffect) {
    target.hideHoverEffect();
  }
};

/**
 * Selects a single object.
 * @param {DvtSelectable} target the object to select
 * @param {boolean} bAddToExisting true if the object should be added to the current selection.
 * @return {boolean} true if the selection has changed.
 */
DvtSelectionHandler.prototype._addToSelection = function(target, bAddToExisting) {
  // If already selected, return.  This intentionally ignores bAddToExisting.
  if(target.isSelected()) {
    return false;
  }
  
  // If not adding to the current selection, deselect all
  if(!bAddToExisting) {
    this.clearSelection();
  }
  
  // Finally, select the object
  target.setSelected(true);
  this._selection.push(target);
  return true;
};

/**
 * Deselects a single object in the selection.
 * @param {DvtSelectable} target the object to deselect
 * @return {boolean} true if the selection has changed.
 */
DvtSelectionHandler.prototype.removeFromSelection = function(target) {
  if(!target.isSelected())
    return false;

  // First deselect the object, then remove it from the selected array
  target.setSelected(false);
  for(var i = 0; i < this._selection.length; i++) {
    if(this._selection[i] == target) {
      this._selection.splice(i, 1);
      break; 
    }
  }
  return true;
};

/**
 * Clears the current selection.
 * @return {boolean} true if the selection has changed.
 */
DvtSelectionHandler.prototype.clearSelection = function() {
  if(this._selection.length <= 0 && this._hiddenSelectedIds.length <= 0)
    return false;

  while(this._selection.length > 0) {
    var obj = this._selection.pop();
    obj.setSelected(false);
  }
  this._hiddenSelectedIds.length = 0;
  return true;
};

/**
 * Utility function used to determine if two id objects are equal.
 * @param {object} a
 * @param {object} b
 * @private
 * @return {boolean}
 */
DvtSelectionHandler._isEquals = function(a, b) {
  if(a == b)
    return true;
  else if (a instanceof String)       // Flash doesn't have a String.equals()
    return false ;
  else if(a && a.equals)
    return a.equals(b);
  else
    return false;
}
/**
  * Interactivity manager for keyboard events.
  * @param {DvtEventManager} manager The owning DvtEventManager
  * @class DvtKeyboardHandler
  * @constructor
  */
var  DvtKeyboardHandler = function(manager) 
{ 
  this.Init(manager);
};

DvtObj.createSubclass(DvtKeyboardHandler, DvtObj, "DvtKeyboardHandler");

// Constants used for calculating penalties when calculating distances between two DvtKeyboardNavigables
// in DvtKeyboardHandler._calcDistanceAngleWeighted
DvtKeyboardHandler._OPTIMAL_ANGLE1 = 15/180*Math.PI;
DvtKeyboardHandler._OPTIMAL_ANGLE2 = 40/180*Math.PI;
DvtKeyboardHandler._SUBOPTIMAL_ANGLE_PENALTY1 = 2; // multiplier to the distance
DvtKeyboardHandler._SUBOPTIMAL_ANGLE_PENALTY2 = 6; // multiplier to the distance

DvtKeyboardHandler.prototype.Init = function(manager)
{
  this._eventManager = manager;
}


/**
 * Processes key down events.
 * @param {DvtKeyboardEvent} event
 * @return {DvtKeyboardNavigable} The object that has keyboard focus as a result of the keyboard event. Null if the event
 *                                does not affect which DvtKeyboardNavigable has focus.
 */
DvtKeyboardHandler.prototype.processKeyDown = function(event) 
{ 
  var currentNavigable = this._eventManager.getFocus();
  
  if(currentNavigable && (this.isNavigationEvent(event) || this.isMultiSelectEvent(event)))
  {
    event.preventDefault();
    var next = currentNavigable.getNextNavigable(event);
    this._eventManager.setFocus(next);
    return next;
  }

  return null;
} ;


/**
 * Simple implementation to return a navigable item based on direction and bounding box of current focused item
 * @param {DvtKeyboardNavigable} currentNavigable The DvtKeyboardNavigable item with current focus
 * @param {DvtKeyboardEvent} event
 * @param {Array} navigableItems An array of items that could receive focus next
 */
DvtKeyboardHandler.getNextNavigable = function(currentNavigable, event, navigableItems) 
{
  var nextNavigable = null;
  var nextNavigableDelta = 0;
  var delta = 0;
  
  var direction = event.keyCode;

  if(!currentNavigable)
  {
    if(!navigableItems || navigableItems.length < 1)
      return null;
    else
      return navigableItems[0];
  }
    
  // get the bounds of the current navigable
  var currentBounds = currentNavigable.getKeyboardBoundingBox();
  var candidateBounds;

  for(var i=0; i < navigableItems.length; i++)
  {
    var navigable = navigableItems[i];
    
    if(currentNavigable === navigable)  
      continue;

    candidateBounds = navigable.getKeyboardBoundingBox();  

    if(DvtKeyboardHandler._isInBounds(currentBounds, candidateBounds, direction))
    {
      delta = DvtKeyboardHandler._computeDelta(currentBounds, candidateBounds, direction);

      if( (((direction == DvtKeyboardEvent.UP_ARROW) || (direction == DvtKeyboardEvent.LEFT_ARROW)) && (delta < 0) && (!nextNavigable || (delta > nextNavigableDelta))) ||
          (((direction == DvtKeyboardEvent.DOWN_ARROW) || (direction == DvtKeyboardEvent.RIGHT_ARROW)) && (delta > 0) && (!nextNavigable || (delta < nextNavigableDelta)))
        )
      {
        nextNavigable = navigable;
        nextNavigableDelta = delta;
      }
    }  
  }
  
  return nextNavigable ? nextNavigable : currentNavigable;
}

 
/**
 * Returns a default keyboard navigable by selecting the upper left or lower right-most item in the navigableItems
 * array.  Utility method that can be called by classes that implement DvtKeyboardNavigable
 * @param {Array} An array of DvtKeyboardNavigables from which to choose the default one to receive focus
 */
DvtKeyboardHandler.prototype.getDefaultNavigable = function(navigableItems)
{ 
  if(!navigableItems || navigableItems.length <= 0)
    return null;
 
  var defaultNavigable = navigableItems[0];
  var defaultLocation = defaultNavigable.getKeyboardBoundingBox();
  var navigable;
  var navigableLocation;
  
  for(var i=1; i<navigableItems.length; i++)
  {
    navigable = navigableItems[i];
    navigableLocation = navigable.getKeyboardBoundingBox();
    // return the top left-most item in non-bidi and top right-most item in bidi
    if( (((navigableLocation.x == defaultLocation.x && navigableLocation.y < defaultLocation.y) ||
        navigableLocation.x < defaultLocation.x) && !DvtAgent.isRightToLeft(this._eventManager.getCtx())) ||
        (((navigableLocation.x + navigableLocation.w == defaultLocation.x + defaultLocation.w && navigableLocation.y < defaultLocation.y) ||
        navigableLocation.x + navigableLocation.w < defaultLocation.x + defaultLocation.w) && DvtAgent.isRightToLeft(this._eventManager.getCtx())))
    {
      defaultNavigable = navigable;
      defaultLocation = defaultNavigable.getKeyboardBoundingBox();
    } 
  }
  
  return defaultNavigable;
} 


/**
 * Returns true if the event requires us to update the DvtKeyboardNavigable with keyboard focus.  In the base 
 * implementation, we return true if the given event is an arrow keystroke. 
 * @param {DvtKeybaordEvent} event
 * @return {Boolean}
 */
DvtKeyboardHandler.prototype.isNavigationEvent = function(event)
{
  var keyCode = event.keyCode;

  switch(keyCode)
  {
    case DvtKeyboardEvent.UP_ARROW:
    case DvtKeyboardEvent.DOWN_ARROW:
    case DvtKeyboardEvent.LEFT_ARROW:
    case DvtKeyboardEvent.RIGHT_ARROW:
      return true;
    default:
      break;
  }
  return false;
}


/**
 * Returns true if the event requires us to perform a single select
 * @param {DvtKeyboardEvent} event
 * @return {Boolean}
 */
DvtKeyboardHandler.prototype.isSelectionEvent = function(event)
{
  return false; // subclasses should override
}

/**
 * Returns true if the event requires us to perform a multi select
 * @param {DvtKeybaordEvent} event
 * @return {Boolean}
 */ 
DvtKeyboardHandler.prototype.isMultiSelectEvent = function(event)
{
  return false; // subclasses should override
}

/**
 * Returns true if the keyboard event is the standard keystroke for opening context menus
 * (Ctrl + Alt + M)
 * @param {DvtKeyboardEvent} event
 * @return {Boolean} true if the event is Ctrl+Alt+M, false otherwise
 */
DvtKeyboardHandler.prototype.isContextMenuEvent = function(event)
{
  return  event.keyCode == DvtKeyboardEvent.M &&
          event.altKey && event.ctrlKey;
};


/**
 * Determines if the candidate bounds line up with the current bounds in the given direction
 * For example, if the direction is up, then the candidate's x-bounds should overlap with the 
 * current's x-bounds
 * 
 * @param {DvtRectangle} currentBounds
 * @param {DvtRectangle} candidateBounds
 * @param {Number} direction  One of DvtKeyboardEvent.UP_ARROW, DvtKeyboardEvent.DOWN_ARROW, 
 *                            DvtKeyboardEvent.LEFT_ARROW, or DvtKeyboardEvent.RIGHT_ARROW
 * @return {Boolean} True if the candidate bounds line up with the current bounds, in the given direction
 * @private
 */
DvtKeyboardHandler._isInBounds = function(currentBounds, candidateBounds, direction) 
{
  if(direction == DvtKeyboardEvent.UP_ARROW || direction == DvtKeyboardEvent.DOWN_ARROW)
  {
    // if up/down, check that the current x-bounds overlap with the candidate's x-bounds.
    // by making sure that the left edge of the current is not to the right of the candidate
    // and that the right edge of the current is not to the left of the candidate
    
    var currentX1 = currentBounds.x;
    var currentX2 = currentX1 + currentBounds.w;
    var candidateX1 = candidateBounds.x;
    var candidateX2 = candidateX1 + candidateBounds.w;
    
    return !((currentX1 >= candidateX2) || (currentX2 <= candidateX1));
  }
  else if(direction == DvtKeyboardEvent.LEFT_ARROW || direction == DvtKeyboardEvent.RIGHT_ARROW)
  {
    // if left/right, check that the current y-bounds overlap with the candidate's y-bounds.
    // by making sure that the top edge of the current is not below the candidate
    // and that the bottom edge of the current is not above the candidate
    
    var currentY1 = currentBounds.y;
    var currentY2 = currentY1 + currentBounds.h;
    var candidateY1 = candidateBounds.y;
    var candidateY2 = candidateY1 + candidateBounds.h;
    
    return !((currentY1 >= candidateY2) || (currentY2 <= candidateY1));    
  }
}


/**
 * Determines the diffeerence between the centers of the currentBounds and the candidatBounds,
 * in the given direction.  The difference is negative if the candidate is above or to the left
 * of the current, positive if the candidate is below or to the right
 * 
 * @param {DvtRectangle} currentBounds
 * @param {DvtRectangle} candidateBounds
 * @param {Number} direction  One of DvtKeyboardEvent.UP_ARROW, DvtKeyboardEvent.DOWN_ARROW, 
 *                            DvtKeyboardEvent.LEFT_ARROW, or DvtKeyboardEvent.RIGHT_ARROW
 * @return {Number} The difference between the centers of the currentBounds and candidateBounds, in
 *                  the given direction
 * @private                          
 */

DvtKeyboardHandler._computeDelta = function(currentBounds, candidateBounds, direction)
{
  var delta = 0;
  var currentX = currentBounds.getCenter().x;
  var currentY = currentBounds.getCenter().y;
  var candidateX = candidateBounds.getCenter().x;
  var candidateY = candidateBounds.getCenter().y;
  
  if((direction == DvtKeyboardEvent.UP_ARROW) || (direction == DvtKeyboardEvent.DOWN_ARROW))
    delta = candidateY - currentY;
  else if((direction == DvtKeyboardEvent.LEFT_ARROW) || (direction == DvtKeyboardEvent.RIGHT_ARROW))
    delta = candidateX - currentX;
    
  return delta;
}



/**
 * Returns the next navigable based on the arrow key that was pressed. This method will return the next navigable that
 * is adjacent to the current navigable, in the direction of the arrow key. If there are no adjacent navigables, the
 * closest navigable in the direction of the arrow key is returned.  Distance to the nearest navigable is based on
 * straight line distance between the midpoints of the navigables' keyboard bounding box, multiplied by a penalty
 * factor if the midpoints are too far off the vertical (in the case of up and down) or horizontal (for left and right)
 * 
 * @param {DvtKeyboardNavigable} current
 * @param {DvtKeyboardEvent} event
 * @param {Array} listOfObjects Array of DvtKeyboardNavigable objects
 * @return {DvtKeyboardNavigable}
 */
DvtKeyboardHandler.getNextAdjacentNavigable = function(current, event, listOfObjects)
{
  var keycode = event.keyCode;
  
  if (!listOfObjects)
    return null;			
			
  if (!current)
    return listOfObjects[0];
			
  var nextObject = current; //init to current object
  var nextDistance = Number.MAX_VALUE;
			
  // If an object is in contact it overrules all other attributes
  // Only another in contact object with better attributes will have higher precedence
  var nextInContact = false; 

  for(var i=0; i<listOfObjects.length; i++)
  {
    var object = listOfObjects[i];
    			
    if (object === current)
      continue;
            				
    if (!DvtKeyboardHandler._isValidDestination(object, current, keycode))
      continue;

    var inContact = DvtKeyboardHandler._calcInContact(object, current, keycode);
    
    if (nextInContact && !inContact)
      continue;

    var distance = DvtKeyboardHandler._calcDistanceAngleWeighted(object, current, keycode);
    // Make sure incontact flag have highest precedence
    if( (!nextInContact && inContact) ||
        (distance < nextDistance && ((nextInContact && inContact) || !nextInContact)) ) 
    {
      nextDistance = distance;
      nextObject = object;
      nextInContact = inContact;
    }
  }
  return nextObject;
}	



/**
 * Determine if two objects are in contact in the specified direction 
 * 
 * @param {DvtKeyboardNavigable} object
 * @param {DvtKeyboardNavigable} current
 * @param {Number} keycode
 * @return {Boolean}
 * @private
 */
DvtKeyboardHandler._calcInContact = function(object, current, keycode)
{
  var objRect = object.getKeyboardBoundingBox();
  var curRect = current.getKeyboardBoundingBox();

  switch (keycode) 
  {
    case DvtKeyboardEvent.UP_ARROW: 
      return  DvtKeyboardHandler._isVerticallyAligned(objRect, curRect) && 
              (curRect.y <= (objRect.y+objRect.h) || DvtKeyboardHandler._areEqualWithinTolerance(curRect.y, objRect.y+objRect.h));  
    case DvtKeyboardEvent.DOWN_ARROW: 
      return  DvtKeyboardHandler._isVerticallyAligned(objRect, curRect) && 
              (objRect.y <= (curRect.y+curRect.h) || DvtKeyboardHandler._areEqualWithinTolerance(objRect.y, curRect.y+curRect.h));
    case DvtKeyboardEvent.RIGHT_ARROW: 
      return  DvtKeyboardHandler._isHorizontallyAligned(objRect, curRect) && 
              (objRect.x <= (curRect.x+curRect.w) || DvtKeyboardHandler._areEqualWithinTolerance(objRect.x, curRect.x+curRect.w));
    case DvtKeyboardEvent.LEFT_ARROW: 
      return  DvtKeyboardHandler._isHorizontallyAligned(objRect, curRect) && 
              (curRect.x <= (objRect.x+objRect.w) || DvtKeyboardHandler._areEqualWithinTolerance(curRect.x, objRect.x+objRect.w));
    default:
      break;
  }
            
  return false;
}

/**
 * Returns true if the two input rectangles are lined up vertically
 * 
 * @param {DvtRectangle} rect1
 * @param {DvtRectangle} rect2
 * @return {Boolean}
 * @private
 */
DvtKeyboardHandler._isVerticallyAligned = function(rect1, rect2)
{
  return ((rect1.x >= rect2.x && rect1.x <= (rect2.x+rect2.w)) ||
          (rect2.x >= rect1.x && rect2.x <= (rect1.x+rect1.w)));
}

/**
 * Returns true if the two input rectangles are lined up horizontally
 * 
 * @param {DvtRectangle} rect1
 * @param {DvtRectangle} rect2
 * @return {Boolean}
 * @private
 */
DvtKeyboardHandler._isHorizontallyAligned = function(rect1, rect2)
{
  return ((rect1.y >= rect2.y && rect1.y <= (rect2.y+rect2.h)) ||
          (rect2.y >= rect1.y && rect2.y <= (rect1.y+rect1.h)));
}



/**
 * Returns the distance between the centers of the keyboard bounding boxes of the input DvtKeyboardNavigables.
 * Distance is multiplied by a penalty factor if the centers are too far off the vertical (in the case of up and down) 
 * or horizontal (for left and right)
 * 
 * @param {DvtKeyboardNavigable} object
 * @param {DvtKeyboardNavigable} current
 * @param {Number} keycode
 * @return {Number} 
 * @private
 */
DvtKeyboardHandler._calcDistanceAngleWeighted = function(object, current, keycode)
{
  var objectBB = object.getKeyboardBoundingBox();
  var objCenterX = objectBB.x + objectBB.w/2;
  var objCenterY = objectBB.y + objectBB.h/2;

  var currentBB = current.getKeyboardBoundingBox();
  var curCenterX = currentBB.x + currentBB.w/2;
  var curCenterY = currentBB.y + currentBB.h/2;

  var x_dist = Math.abs(objCenterX - curCenterX);
  var y_dist = Math.abs(objCenterY - curCenterY);
            
  var angle = Math.atan2(y_dist, x_dist);
            
  var distance = Math.sqrt(x_dist * x_dist + y_dist * y_dist);

  // Angle penalty based on direction   
  if( (angle > DvtKeyboardHandler._OPTIMAL_ANGLE1 && (keycode == DvtKeyboardEvent.RIGHT_ARROW || keycode == DvtKeyboardEvent.LEFT_ARROW)) ||
      (angle < DvtMath.HALF_PI - DvtKeyboardHandler._OPTIMAL_ANGLE1 && (keycode == DvtKeyboardEvent.UP_ARROW || keycode == DvtKeyboardEvent.DOWN_ARROW)) ) 
  {
    if( (angle > DvtKeyboardHandler._OPTIMAL_ANGLE2 && (keycode == DvtKeyboardEvent.RIGHT_ARROW || keycode == DvtKeyboardEvent.LEFT_ARROW)) ||
        (angle < DvtMath.HALF_PI - DvtKeyboardHandler._OPTIMAL_ANGLE2 && (keycode == DvtKeyboardEvent.UP_ARROW || keycode == DvtKeyboardEvent.DOWN_ARROW)) ) 
    {
      distance *= DvtKeyboardHandler._SUBOPTIMAL_ANGLE_PENALTY2;
    }
    else 
    {
      distance *= DvtKeyboardHandler._SUBOPTIMAL_ANGLE_PENALTY1;
    }
  }
                
  return distance;
}


/**
  * Determine if a point is valid based on the direction
  * @param {DvtKeyboardNavigable} object
  * @param {DvtKeyboardNavigable} current
  * @param {Number} keycode
  * @return {Boolean}
  * @private
  */
DvtKeyboardHandler._isValidDestination = function(object, current, keycode)
{
  var objBB = object.getKeyboardBoundingBox();
  var curBB = current.getKeyboardBoundingBox();
  
  switch (keycode) 
  {
    case DvtKeyboardEvent.UP_ARROW: 
      return (objBB.y<curBB.y) || DvtKeyboardHandler._areEqualWithinTolerance(objBB.y, curBB.y);
    case DvtKeyboardEvent.DOWN_ARROW: 
      return objBB.y>curBB.y || DvtKeyboardHandler._areEqualWithinTolerance(objBB.y, curBB.y);
    case DvtKeyboardEvent.RIGHT_ARROW: 
      return objBB.x>curBB.x || DvtKeyboardHandler._areEqualWithinTolerance(objBB.x, curBB.x);
    case DvtKeyboardEvent.LEFT_ARROW: 
      return objBB.x<curBB.x || DvtKeyboardHandler._areEqualWithinTolerance(objBB.x, curBB.x);
    default:
      break;
  }
  return true;
}

/**
 * Utility method to check if two numbers are equal, within a small tolerance. Used to account for small rounding
 * errors
 * 
 * @param {Number} a
 * @param {Number} b
 * @return {Boolean} true if the numbers are within 0.0000001 of each other
 * @private
 */
DvtKeyboardHandler._areEqualWithinTolerance = function(a,b)
{
  return Math.abs(a-b) <= 0.0000001;
}

/**
 * Handler used for marquee operations.
 * @constructor
 * @param {DvtContainer} container Marquee container.
 * @param {DvtRectangle} marqueeBounds The area in which the marquee can be initiated and drawn.
 * @param {DvtRectangle} glassPaneBounds The area that will be covered by the glass pane when the marquee is active.
 * @param {DvtFill} fill The marquee rect fill
 * @param {DvtStroke} stroke The marquee rect stroke
 * @param {boolean} [allowHorizResize] Whether horizontal resize is allowed. Defaults to true.
 * @param {boolean} [allowVertResize] Whether vertical resize is allowed. Defaults to true.
 * @param {DvtRectangle} [horizResizeBounds] The bounds for initiating a marquee that only resizes horizontally.
 * @param {DvtRectangle} [vertResizeBounds] The bounds for initiating a marquee that only resizes vertically.
 */
var DvtMarqueeHandler = function(container, marqueeBounds, glassPaneBounds, fill, stroke, 
    allowHorizResize, allowVertResize, horizResizeBounds, vertResizeBounds) {
  this.Init(container, marqueeBounds, glassPaneBounds, fill, stroke, allowHorizResize, allowVertResize, 
      horizResizeBounds, vertResizeBounds);
}  


DvtObj.createSubclass(DvtMarqueeHandler, DvtObj, "DvtMarqueeHandler");


/**
 * @param {DvtContainer} container Marquee container.
 * @param {DvtRectangle} marqueeBounds The area in which the marquee can be initiated and drawn.
 * @param {DvtRectangle} glassPaneBounds The area that will be covered by the glass pane when the marquee is active.
 * @param {DvtFill} fill The marquee rect fill
 * @param {DvtStroke} stroke The marquee rect stroke
 * @param {boolean} [allowHorizResize] Whether horizontal resize is allowed. Defaults to true.
 * @param {boolean} [allowVertResize] Whether vertical resize is allowed. Defaults to true.
 * @param {DvtRectangle} [horizResizeBounds] The bounds for initiating a marquee that only resizes horizontally.
 * @param {DvtRectangle} [vertResizeBounds] The bounds for initiating a marquee that only resizes vertically.
 */
DvtMarqueeHandler.prototype.Init = function(container, marqueeBounds, glassPaneBounds, fill, stroke, 
    allowHorizResize, allowVertResize, horizResizeBounds, vertResizeBounds) {
  this._context = container.getCtx();
  this._container = container;
  this._bounds = marqueeBounds;
  this._fill = fill;
  this._stroke = stroke;  
  this._allowHoriz = allowHorizResize == null ? true : allowHorizResize;
  this._allowVert = allowVertResize == null ? true : allowVertResize;
  this._horizBounds = horizResizeBounds;
  this._vertBounds = vertResizeBounds;
  
  // Set up the glass pane
  this._glassPane = new DvtRect(this._context, glassPaneBounds.x, glassPaneBounds.y, glassPaneBounds.w, glassPaneBounds.h);
  this._glassPane.setInvisibleFill();
  
  // Flags
  this._marqueeOn = false;     // whether the marquee has been activated
  this._marqueeDrawn = false;  // whether the marquee has been drawn
  this._resizeHoriz = false;   // whether the marquee is resizeable horizontally
  this._resizeVert = false;    // whether the marquee is resizeable vertically
}


/**
 * Processes drag start.
 * @param {DvtPoint} relPos The event position relative to the stage.
 * @param {boolean} ctrlKey Whether the ctrl key is pressed during the event.
 * @return {DvtMarqueeEvent}
 */
DvtMarqueeHandler.prototype.processDragStart = function(relPos, ctrlKey) {
  if (!this._marqueeOn) {
    this._origPt = this._container.stageToLocal(relPos); // marquee origin
    
    // Determine if the marquee is resizeable horizontally and/or vertically:
    // - if origin is inside marqueeBounds, then allow resize in both directions if possible;
    // - if origin is inside vertResizeBounds, then only vert resize is possible;
    // - if origin is inside horizResizeBounds, then only horiz resize is possible;
    // - otherwise, marquee shouldn't be initiated.
    this._resizeHoriz = this._allowHoriz;
    this._resizeVert = this._allowVert;
    if (!this._bounds.containsPoint(this._origPt.x, this._origPt.y)) {
      if (this._vertBounds && this._vertBounds.containsPoint(this._origPt.x, this._origPt.y))
        this._resizeHoriz = false;
      else if (this._horizBounds && this._horizBounds.containsPoint(this._origPt.x, this._origPt.y))
        this._resizeVert = false;
      else
        return null;
    }
    
    this._marqueeOn = true;
    this._marquee = null;
    return this._createMarqueeEvent(DvtMarqueeEvent.SUBTYPE_START, ctrlKey);
  }
  
  return null;
}


/**
 * Processes drag move.
 * @param {DvtPoint} relPos The event position relative to the stage.
 * @param {boolean} ctrlKey Whether the ctrl key is pressed during the event.
 * @return {DvtMarqueeEvent}
 */
DvtMarqueeHandler.prototype.processDragMove = function(relPos, ctrlKey) {
  if (!this._marqueeOn)
    return null;
  
  if (!this._marqueeDrawn) {
    this._container.addChild(this._glassPane);
    
    // Initiate the marquee
    this._marquee = new DvtRect(this._context, this._bounds.x, this._bounds.y, this._bounds.w, this._bounds.h);
    if (this._resizeHoriz) {
      this._marquee.setX(this._origPt.x);
      this._marquee.setWidth(0);
    }
    if (this._resizeVert) {
      this._marquee.setY(this._origPt.y);
      this._marquee.setHeight(0);
    }
    
    this._marquee.setStroke(this._stroke);
    this._marquee.setFill(this._fill);
    this._marquee.setPixelHinting(true);
    this._glassPane.addChild(this._marquee);
    
    this._marqueeDrawn = true;
  }
    
  var newPt = this._container.stageToLocal(relPos);
 
  // Bound the newPt within the marquee bounds
  newPt.x = Math.max(newPt.x, this._bounds.x);
  newPt.x = Math.min(newPt.x, this._bounds.x + this._bounds.w);
  newPt.y = Math.max(newPt.y, this._bounds.y);
  newPt.y = Math.min(newPt.y, this._bounds.y + this._bounds.h);
  
  // Update marquee x and w
  if (this._resizeHoriz) {
    this._marquee.setWidth(Math.abs(newPt.x - this._origPt.x));
    if (newPt.x < this._origPt.x)
      this._marquee.setX(newPt.x);
    else
      this._marquee.setX(this._origPt.x);
  }
  
  // Update marquee y and h
  if (this._resizeVert) {
    this._marquee.setHeight(Math.abs(newPt.y - this._origPt.y));
    if (newPt.y < this._origPt.y)
      this._marquee.setY(newPt.y);
    else
      this._marquee.setY(this._origPt.y);
  }
  
  return this._createMarqueeEvent(DvtMarqueeEvent.SUBTYPE_MOVE, ctrlKey);
}


/**
 * Processes drag end.
 * @param {DvtPoint} relPos The event position relative to the stage.
 * @param {boolean} ctrlKey Whether the ctrl key is pressed during the event.
 * @return {DvtMarqueeEvent}
 */
DvtMarqueeHandler.prototype.processDragEnd = function(relPos, ctrlKey) {
  if (this._marqueeOn) {
    this._marqueeOn = false;
    
    if (this._marqueeDrawn) {
      // Remove the marquee and glass pane
      this._glassPane.removeChild(this._marquee);
      this._container.removeChild(this._glassPane);
      this._marqueeDrawn = false;
      
      return this._createMarqueeEvent(DvtMarqueeEvent.SUBTYPE_END, ctrlKey);
    }
  }
  
  return null;
}

/**
 * Processes drag end.
 * @param {DvtPoint} relPos The event position relative to the stage.
 * @param {boolean} ctrlKey Whether the ctrl key is pressed during the event.
 * @return {DvtMarqueeEvent}
 */
DvtMarqueeHandler.prototype.cancelMarquee = function() {
  if (this._marqueeOn) {
    this._marqueeOn = false;
    
    if (this._marqueeDrawn) {
      // Remove the marquee and glass pane
      this._glassPane.removeChild(this._marquee);
      this._container.removeChild(this._glassPane);
      this._marqueeDrawn = false;
      
      return true;
    }
  }
  
  return false;
}


/**
 * Creates a marquee event.
 * @param {string} type Event type.
 * @param {boolean} ctrlKey Whether the ctrl key is pressed during the event.
 * @return {DvtMarqueeEvent} The event.
 */
DvtMarqueeHandler.prototype._createMarqueeEvent = function(type, ctrlKey) {
  var x = null;
  var y = null;
  var w = null;
  var h = null;
  
  if (this._resizeHoriz) {
    x = this._marquee ? this._marquee.getX() : this._origPt.x;
    w = this._marquee ? this._marquee.getWidth() : 0;
  }
  if (this._resizeVert) {
    y = this._marquee ? this._marquee.getY() : this._origPt.y;
    h = this._marquee ? this._marquee.getHeight() : 0;
  }
  
  return new DvtMarqueeEvent(type, x, y, w, h, ctrlKey);
}

/**
 * Returns the appropriate cursor type.
 * @param {DvtPoint} relPos The current cursor position relative to the stage.
 * @return {string} The cursor type.
 */
DvtMarqueeHandler.prototype.getCursor = function(relPos) {
  var pos = this._container.stageToLocal(relPos);
  var withinBounds = this._bounds.containsPoint(pos.x, pos.y);
  var withinVertBounds = this._vertBounds && this._vertBounds.containsPoint(pos.x, pos.y);
  var withinHorizBounds = this._horizBounds && this._horizBounds.containsPoint(pos.x, pos.y);
  
  if (withinBounds || withinVertBounds || withinHorizBounds)
    return "crosshair";
  else
    return "inherit";
}
/**
 * Handler used for pan and zoom operations.
 * @constructor
 * @param {DvtContainer} container Glass pane container.
 * @param {DvtRectangle} panZoomBounds The area in which pan/zoom can be initiated.
 * @param {DvtRectangle} glassPaneBounds The area that will be covered by the glass pane during pan.
 * @param {number} zoomRate The rate of mouse wheel zoom.
 */
var DvtPanZoomHandler = function(container, panZoomBounds, glassPaneBounds, zoomRate) {
  this.Init(container, panZoomBounds, glassPaneBounds, zoomRate);
}  

DvtObj.createSubclass(DvtPanZoomHandler, DvtObj, "DvtPanZoomHandler");


/**
 * @param {DvtContainer} container Glass pane container.
 * @param {DvtRectangle} panZoomBounds The area in which the pan/zoom can be initiated.
 * @param {DvtRectangle} glassPaneBounds The area that will be covered by the glass pane when the marquee is active.
 * @param {number} zoomRate The rate of mouse wheel zoom.
 */
DvtPanZoomHandler.prototype.Init = function(container, panZoomBounds, glassPaneBounds, zoomRate) {
  this._context = container.getCtx();
  this._container = container;
  this._bounds = panZoomBounds;
  this._zoomRate = zoomRate;
  
  // Set up the glass pane
  this._glassPane = new DvtRect(this._context, glassPaneBounds.x, glassPaneBounds.y, glassPaneBounds.w, glassPaneBounds.h);
  this._glassPane.setInvisibleFill();
  
  // Flags
  this._panOn = false;           // whether the pan has been initiated
  this._glassPaneDrawn = false;  // whether the glass pane has been drawn
  this._pinchOn = false;         // whether the pinch has been initiated
}


/**
 * Processes drag start.
 * @param {DvtPoint} relPos The event position relative to the stage.
 * @return {DvtPanZoomEvent}
 */
DvtPanZoomHandler.prototype.processDragStart = function(relPos) {
  if (!this._panOn) {
    this._origPt = this._container.stageToLocal(relPos); // drag origin
    this._lastPt = this._origPt;
    
    // Ignore if the start point is outside the pan/zoom bounds
    if (!this._bounds.containsPoint(this._origPt.x, this._origPt.y))
      return null;
    
    this._panOn = true;
    
    return new DvtPanZoomEvent(DvtPanZoomEvent.SUBTYPE_PAN_START, 0, 0, 0, 0, 0, 0, 0, 0);
  }
  
  return null;
}


/**
 * Processes drag move.
 * @param {DvtPoint} relPos The event position relative to the stage.
 * @return {DvtPanZoomEvent}
 */
DvtPanZoomHandler.prototype.processDragMove = function(relPos) {
  if (!this._panOn)
    return null;
  
  if (!this._glassPaneDrawn) {
    this._container.addChild(this._glassPane);
    this._glassPaneDrawn = true;
  }
    
  var newPt = this._container.stageToLocal(relPos);
  var deltaX = newPt.x - this._lastPt.x;
  var deltaY = newPt.y - this._lastPt.y;
  var totalDeltaX = newPt.x - this._origPt.x;
  var totalDeltaY = newPt.y - this._origPt.y;
  this._lastPt = newPt;
  
  return new DvtPanZoomEvent(DvtPanZoomEvent.SUBTYPE_PAN_MOVE, -deltaX, -deltaX, -deltaY, -deltaY,
      -totalDeltaX, -totalDeltaX, -totalDeltaY, -totalDeltaY);
}


/**
 * Processes drag end.
 * @param {DvtPoint} relPos The event position relative to the stage.
 * @return {DvtPanZoomEvent}
 */
DvtPanZoomHandler.prototype.processDragEnd = function(relPos) {
  if (this._panOn) {
    this._panOn = false;
    
    if (this._glassPaneDrawn) {
      // Remove the glass pane
      this._container.removeChild(this._glassPane);
      this._glassPaneDrawn = false;
      
      var newPt = relPos ? this._container.stageToLocal(relPos) : this._lastPt;
      var deltaX = newPt.x - this._lastPt.x;
      var deltaY = newPt.y - this._lastPt.y;
      var totalDeltaX = newPt.x - this._origPt.x;
      var totalDeltaY = newPt.y - this._origPt.y;
      this._lastPt = null;
  
      return new DvtPanZoomEvent(DvtPanZoomEvent.SUBTYPE_PAN_END, -deltaX, -deltaX, -deltaY, -deltaY,
          -totalDeltaX, -totalDeltaX, -totalDeltaY, -totalDeltaY);
    }
  }
  
  return null;
}

/**
 * Processes mouse wheel.
 * @param {DvtPoint} relPos The event position relative to the stage.
 * @param {number} delta The mouse wheel delta.
 * @return {DvtPanZoomEvent}
 */
DvtPanZoomHandler.prototype.processMouseWheel = function(relPos, delta) {
  // Ignore if the cursor is outside the pan/zoom bounds
  var startPt = this._container.stageToLocal(relPos);
  if (!this._bounds.containsPoint(startPt.x, startPt.y))
    return null;
      
  delta *= this._zoomRate;
  
  // Compute the deltas. It should maintain the cursor pointing at the same item.
  var deltaXMin = delta * (startPt.x - this._bounds.x);
  var deltaXMax = -delta * (this._bounds.x + this._bounds.w - startPt.x);
  var deltaYMin = delta * (startPt.y - this._bounds.y);
  var deltaYMax = -delta * (this._bounds.y + this._bounds.h - startPt.y);
  
  return new DvtPanZoomEvent(DvtPanZoomEvent.SUBTYPE_ZOOM, deltaXMin, deltaXMax, deltaYMin, deltaYMax,
      deltaXMin, deltaXMax, deltaYMin, deltaYMax);
}

/**
 * Processes pinch start.
 * @return {DvtPanZoomEvent}
 */
DvtPanZoomHandler.prototype.processPinchStart = function(relPos1, relPos2) {
  if (!this._pinchOn) {    
    // Save pinch origin
    this._origPt1 = this._container.stageToLocal(relPos1);
    this._origPt2 = this._container.stageToLocal(relPos2);
    this._lastPt1 = this._origPt1;
    this._lastPt2 = this._origPt2;
    
    // Ignore if the start points are outside the pan/zoom bounds
    if (!this._bounds.containsPoint(this._origPt1.x, this._origPt1.y) || !this._bounds.containsPoint(this._origPt2.x, this._origPt2.y))
      return null;
    
    this._pinchOn = true;
    
    return new DvtPanZoomEvent(DvtPanZoomEvent.SUBTYPE_PINCH_START, 0, 0, 0, 0, 0, 0, 0, 0);
  }
  
  return null;
}


/**
 * Processes pinch move.
 * @param {number} cx The x location of the center of the two fingers.
 * @param {number} cy The y location of the center of the two fingers.
 * @param {number} dcx How much the center of the two fingers moved in the x direction.
 * @param {number} dcy How much the center of the two fingers moved in the y direction.
 * @param {number} rx The ratio of the current intra-finger x distance to previous distance.
 * @param {number} ry The ratio of the current intra-finger y distance to previous distance.
 * @return {DvtPanZoomEvent}
 */
DvtPanZoomHandler.prototype.processPinchMove = function(relPos1, relPos2) {
  if (!this._pinchOn)
    return null;
  
  var newPt1 = this._container.stageToLocal(relPos1);
  var newPt2 = this._container.stageToLocal(relPos2);
  
  var deltas = this._computePinchDeltas(newPt1, newPt2, this._lastPt1, this._lastPt2);
  var totalDeltas = this._computePinchDeltas(newPt1, newPt2, this._origPt1, this._origPt2);
  
  this._lastPt1 = newPt1;
  this._lastPt2 = newPt2;

  return new DvtPanZoomEvent(DvtPanZoomEvent.SUBTYPE_PINCH_MOVE, deltas.dxMin, deltas.dxMax, deltas.dyMin, deltas.dyMax, 
      totalDeltas.dxMin, totalDeltas.dxMax, totalDeltas.dyMin, totalDeltas.dyMax);
}


/**
 * Processes pinch end.
 * @return {DvtPanZoomEvent}
 */
DvtPanZoomHandler.prototype.processPinchEnd = function() {
  if (this._pinchOn) {
    this._pinchOn = false;
    var totalDeltas = this._computePinchDeltas(this._lastPt1, this._lastPt2, this._origPt1, this._origPt2);    
    
    this._lastPt1 = null;
    this._lastPt2 = null;
    
    return new DvtPanZoomEvent(DvtPanZoomEvent.SUBTYPE_PINCH_END, 0, 0, 0, 0, 
        totalDeltas.dxMin, totalDeltas.dxMax, totalDeltas.dyMin, totalDeltas.dyMax);
  }
  
  return null;
}

/**
 * Computes how much the bounds should change based on the two finger movements.
 * @param {DvtPoint} pos1 Current position of the first finger.
 * @param {DvtPoint} pos2 Current position of the second finger.
 * @param {DvtPoint} prevPos1 Previous position of the first finger.
 * @param {DvtPoint} prevPos2 Previous position of the second finger.
 * @return {object} An object containing deltas for the bounds: dxMin, dxMax, dyMin, and dyMax.
 */
DvtPanZoomHandler.prototype._computePinchDeltas = function(pos1, pos2, prevPos1, prevPos2) {
  // Calculate the ratio of the current inter-finger distance to the previous
  var dx = Math.abs(pos1.x - pos2.x);
  var dy = Math.abs(pos1.y - pos2.y);
  var prevDx = Math.abs(prevPos1.x - prevPos2.x);
  var prevDy = Math.abs(prevPos1.y - prevPos2.y);
  // The check dx > dy/2 is to make it easier to zoom in y-direction alone. Otherwise, one might pinch in y-direction,
  // but the slight finger movement in the x-direction will cause the x-axis to zoom as well.
  var zoomX = dx > dy/2 ? prevDx/dx : 1;
  var zoomY = dy > dx/2 ? prevDy/dy : 1;
  
  // Calculate the distance the center of the two fingers has moved
  var cx = (pos1.x + pos2.x) / 2;
  var cy = (pos1.y + pos2.y) / 2;
  var prevCx = (prevPos1.x + prevPos2.x) / 2;
  var prevCy = (prevPos1.y + prevPos2.y) / 2;
  var dcx = cx - prevCx;
  var dcy = cy - prevCy;
    
  // Perform a zoom
  var dxMin = (1-zoomX) * (cx - this._bounds.x);
  var dxMax = (zoomX-1) * (this._bounds.x + this._bounds.w - cx);
  var dyMin = (1-zoomY) * (cy - this._bounds.y);
  var dyMax = (zoomY-1) * (this._bounds.y + this._bounds.h - cy);
  
  // Perform a scroll
  dxMin -= dcx;
  dxMax -= dcx;
  dyMin -= dcy;
  dyMax -= dcy;
  
  return {dxMin: dxMin, dxMax: dxMax, dyMin: dyMin, dyMax: dyMax};
}

/**
 * Sets the pan cursor.
 * @param {string} panUpCursor The URI of the cursor image for the non-dragged state.
 * @param {string} panDownCursor The URI of the cursor image for the dragged state. 
 */
DvtPanZoomHandler.prototype.setPanCursor = function(panUpCursor, panDownCursor) {
  // IE doesn't support cursor image with custom positioning
  if (DvtAgent.isPlatformIE())
    return;
    
  if (panUpCursor)
    this._panUpCursor = "url(" + panUpCursor + ") 8 8, auto";
  if (panDownCursor)
    this._panDownCursor = "url(" + panDownCursor + ") 8 8, auto";
}

/**
 * Returns the appropriate cursor type.
 * @param {DvtPoint} [relPos] The current cursor position relative to the stage.
 * @return {string} The cursor type.
 */
DvtPanZoomHandler.prototype.getCursor = function(relPos) {
  var withinBounds = true;
  if (relPos) {
    var pos = this._container.stageToLocal(relPos);
    withinBounds = this._bounds.containsPoint(pos.x, pos.y);
  }
  
  if (withinBounds) {
    if (this._panOn)
      return this._panDownCursor ? this._panDownCursor : "move";
    else
      return this._panUpCursor ? this._panUpCursor: "move";
  }
  else
    return "inherit";
}
/**
 * Animation handler for black box animations.
 * @class DvtBlackBoxAnimationHandler
 */
var DvtBlackBoxAnimationHandler = function() {};

DvtObj.createSubclass(DvtBlackBoxAnimationHandler, DvtObj, "DvtBlackBoxAnimationHandler");

// Black Box Animation Types
DvtBlackBoxAnimationHandler.ALPHA_FADE            = "alphaFade";   
DvtBlackBoxAnimationHandler.CONVEYOR_FROM_RIGHT   = "conveyorFromRight";   
DvtBlackBoxAnimationHandler.CONVEYOR_FROM_LEFT    = "conveyorFromLeft";
DvtBlackBoxAnimationHandler.CUBE_TO_RIGHT         = "cubeToRight";
DvtBlackBoxAnimationHandler.CUBE_TO_LEFT          = "cubeToLeft";
DvtBlackBoxAnimationHandler.FLIP_RIGHT            = "flipRight";
DvtBlackBoxAnimationHandler.FLIP_LEFT             = "flipLeft";   
DvtBlackBoxAnimationHandler.TRANSITION_TO_RIGHT   = "transitionToRight";
DvtBlackBoxAnimationHandler.TRANSITION_TO_LEFT    = "transitionToLeft"; 
DvtBlackBoxAnimationHandler.SLIDE_TO_RIGHT        = "slideToRight";
DvtBlackBoxAnimationHandler.SLIDE_TO_LEFT         = "slideToLeft"; 
DvtBlackBoxAnimationHandler.ZOOM                  = "zoom";

/**
 * Returns true if the specified animation type should be handled by a black box animation.
 * @param {string} type The animation type.
 * @return {boolean}
 */
DvtBlackBoxAnimationHandler.isSupported = function(type) {
  return (type == DvtBlackBoxAnimationHandler.ALPHA_FADE || 
          type == DvtBlackBoxAnimationHandler.CONVEYOR_FROM_RIGHT || 
          type == DvtBlackBoxAnimationHandler.CONVEYOR_FROM_LEFT || 
          type == DvtBlackBoxAnimationHandler.CUBE_TO_RIGHT || 
          type == DvtBlackBoxAnimationHandler.CUBE_TO_LEFT || 
          type == DvtBlackBoxAnimationHandler.FLIP_RIGHT || 
          type == DvtBlackBoxAnimationHandler.FLIP_LEFT || 
          type == DvtBlackBoxAnimationHandler.TRANSITION_TO_RIGHT || 
          type == DvtBlackBoxAnimationHandler.TRANSITION_TO_LEFT || 
          type == DvtBlackBoxAnimationHandler.SLIDE_TO_RIGHT || 
          type == DvtBlackBoxAnimationHandler.SLIDE_TO_LEFT || 
          type == DvtBlackBoxAnimationHandler.ZOOM);
}

/**
 * Creates and returns the specified black box animation for the displayables.
 * @param {DvtContext} context The platform specific context object.
 * @param {string} type The animation type.
 * @param {object} objs The displayable or array of displayables.
 * @param {DvtRectangle} bounds The bounds of the objects to animate.
 * @param {number} duration The duration of the animation
 * @return {DvtPlayable} The animation from the old object to the new object.
 */
DvtBlackBoxAnimationHandler.getInAnimation = function(context, type, objs, bounds, duration) {
  if(type == DvtBlackBoxAnimationHandler.ALPHA_FADE) 
    return new DvtAnimFadeIn(context, objs, duration);
  else if(type == DvtBlackBoxAnimationHandler.CONVEYOR_FROM_RIGHT)
    return new DvtAnimConveyorIn(context, objs, DvtBaseAnimation.DIR_E, duration); 
  else if(type == DvtBlackBoxAnimationHandler.CONVEYOR_FROM_LEFT)
    return new DvtAnimConveyorIn(context, objs, DvtBaseAnimation.DIR_W, duration); 
  else if(type == DvtBlackBoxAnimationHandler.CUBE_TO_RIGHT)
    return new DvtAnimCubeIn(context, objs, DvtBaseAnimation.AXIS_Y, DvtBaseAnimation.ROT_DIR_CLOCKWISE, duration);  
  else if(type == DvtBlackBoxAnimationHandler.CUBE_TO_LEFT)
    return new DvtAnimCubeIn(context, objs, DvtBaseAnimation.AXIS_Y, DvtBaseAnimation.ROT_DIR_COUNTERCLOCKWISE, duration); 
  else if(type == DvtBlackBoxAnimationHandler.FLIP_RIGHT)
    return new DvtAnimFlipIn(context, objs, DvtBaseAnimation.AXIS_Y, DvtBaseAnimation.ROT_DIR_CLOCKWISE, duration);  
  else if(type == DvtBlackBoxAnimationHandler.FLIP_LEFT)
    return new DvtAnimFlipIn(context, objs, DvtBaseAnimation.AXIS_Y, DvtBaseAnimation.ROT_DIR_COUNTERCLOCKWISE, duration); 
  else if(type == DvtBlackBoxAnimationHandler.TRANSITION_TO_RIGHT)
    return new DvtAnimScaleFadeIn(context, objs, bounds, DvtBaseAnimation.DIR_NW, 0.5, duration); 
  else if(type == DvtBlackBoxAnimationHandler.TRANSITION_TO_LEFT) 
    return new DvtAnimScaleFadeIn(context, objs, bounds, DvtBaseAnimation.DIR_NE, 0.5, duration); 
  else if(type == DvtBlackBoxAnimationHandler.SLIDE_TO_RIGHT) {
    DvtBlackBoxAnimationHandler._offsetObjects(objs, -bounds.w, 0);
    return new DvtAnimMoveBy(context, objs, new DvtPoint(bounds.w, 0), duration); 
  }
  else if(type == DvtBlackBoxAnimationHandler.SLIDE_TO_LEFT) {
    DvtBlackBoxAnimationHandler._offsetObjects(objs, bounds.w, 0);
    return new DvtAnimMoveBy(context, objs, new DvtPoint(-bounds.w, 0), duration); 
  }
  else if(type == DvtBlackBoxAnimationHandler.ZOOM)
    return new DvtAnimScaleFadeIn(context, objs, bounds, DvtBaseAnimation.DIR_C, 0.5, duration); 
  else 
    return null; 
}

/**
 * Creates and returns the specified black box animation between the old displayable and
 * the new displayable.
 * @param {DvtContext} context The platform specific context object.
 * @param {string} type The animation type.
 * @param {object} outObjs The displayable or array of displayables to animate out.
 * @param {object} inObjs The displayable or array of displayables to animate in.
 * @param {DvtRectangle} bounds The bounds of the objects to animate.
 * @param {number} duration The duration of the animation (in seconds).
 * @return {DvtPlayable} The animation from the old object to the new object.
 */
DvtBlackBoxAnimationHandler.getCombinedAnimation = function(context, type, outObjs, inObjs, bounds, duration) {
  if(type == DvtBlackBoxAnimationHandler.ALPHA_FADE)
    return new DvtCombinedAnimFade(context, outObjs, inObjs, duration); 
  else if(type == DvtBlackBoxAnimationHandler.CONVEYOR_FROM_RIGHT)
    return new DvtCombinedAnimConveyor(context, outObjs, inObjs, DvtBaseAnimation.DIR_W, DvtBaseAnimation.DIR_E, duration); 
  else if(type == DvtBlackBoxAnimationHandler.CONVEYOR_FROM_LEFT)
    return new DvtCombinedAnimConveyor(context, outObjs, inObjs, DvtBaseAnimation.DIR_E, DvtBaseAnimation.DIR_W, duration); 
  else if(type == DvtBlackBoxAnimationHandler.CUBE_TO_RIGHT)
    return new DvtCombinedAnimCube(context, outObjs, inObjs, DvtBaseAnimation.AXIS_Y, DvtBaseAnimation.ROT_DIR_CLOCKWISE, duration); 
  else if(type == DvtBlackBoxAnimationHandler.CUBE_TO_LEFT)
    return new DvtCombinedAnimCube(context, outObjs, inObjs, DvtBaseAnimation.AXIS_Y, DvtBaseAnimation.ROT_DIR_COUNTERCLOCKWISE, duration); 
  else if(type == DvtBlackBoxAnimationHandler.FLIP_RIGHT)
    return new DvtCombinedAnimFlip(context, outObjs, inObjs, DvtBaseAnimation.AXIS_Y, DvtBaseAnimation.ROT_DIR_CLOCKWISE, duration); 
  else if(type == DvtBlackBoxAnimationHandler.FLIP_LEFT)
    return new DvtCombinedAnimFlip(context, outObjs, inObjs, DvtBaseAnimation.AXIS_Y, DvtBaseAnimation.ROT_DIR_COUNTERCLOCKWISE, duration); 
  else if(type == DvtBlackBoxAnimationHandler.TRANSITION_TO_RIGHT) 
    return new DvtCombinedAnimScaleFade(context, outObjs, inObjs, bounds, DvtBaseAnimation.DIR_SE, DvtBaseAnimation.DIR_NW, 0.5, duration); 
  else if(type == DvtBlackBoxAnimationHandler.TRANSITION_TO_LEFT)
    return new DvtCombinedAnimScaleFade(context, outObjs, inObjs, bounds, DvtBaseAnimation.DIR_SW, DvtBaseAnimation.DIR_NE, 0.5, duration); 
  else if(type == DvtBlackBoxAnimationHandler.SLIDE_TO_RIGHT) {
    DvtBlackBoxAnimationHandler._offsetObjects(inObjs, -bounds.w, 0);
    return new DvtCombinedAnimMoveBy(context, outObjs, inObjs, new DvtPoint(bounds.w, 0), new DvtPoint(bounds.w, 0), duration); 
  }
  else if(type == DvtBlackBoxAnimationHandler.SLIDE_TO_LEFT) {
    DvtBlackBoxAnimationHandler._offsetObjects(inObjs, bounds.w, 0);
    return new DvtCombinedAnimMoveBy(context, outObjs, inObjs, new DvtPoint(-bounds.w, 0), new DvtPoint(-bounds.w, 0), duration); 
  }
  else if(type == DvtBlackBoxAnimationHandler.ZOOM) 
    return new DvtCombinedAnimScaleFade(context, outObjs, inObjs, bounds, DvtBaseAnimation.DIR_C, DvtBaseAnimation.DIR_C, 0.5, duration); 
  else 
    return null; 
}

/**
 * Adjusts the objects by the specified offset.
 * @param {object} objs The displayable or array of displayables.
 * @param {number} offsetX The x offset to add.
 * @param {number} offsetY The y offset to add.
 @private
 */
DvtBlackBoxAnimationHandler._offsetObjects = function(objs, offsetX, offsetY) {
  if(objs.length) {
    for(var i=0; i<objs.length; i++)
      DvtBlackBoxAnimationHandler._offsetObjects(objs[i], offsetX, offsetY);
  }
  else if(objs) {
    // Adjust the displayable
    objs.setTranslateX(objs.getTranslateX() + offsetX);
    objs.setTranslateY(objs.getTranslateY() + offsetY);
  }
}
/**
 * Animation handler for data objects.
 * @param {DvtContext} context The platform specific context object.
 * @param {DvtContainer} deleteContainer The container where deletes should be moved for animation.
 * @class DvtDataAnimationHandler
 * @constructor
 */
var DvtDataAnimationHandler = function(context, deleteContainer) {
  this.Init(context, deleteContainer);
};

DvtObj.createSubclass(DvtDataAnimationHandler, DvtObj, "DvtDataAnimationHandler");

// TODO Document the expected interface for animatable objects:
// getId()
// animateUpdate(oldObj)
// animateDelete()
// animateInsert()

/**
 * Initializes the handler.
 * @param {DvtContext} context The platform specific context object.
 * @param {DvtContainer} deleteContainer The container where deletes should be moved for animation.
 * @protected
 */
DvtDataAnimationHandler.prototype.Init = function(context, deleteContainer) {
  this._context         = context;
  this._deleteContainer = deleteContainer;
  this._playables       = new Array();
  this._newChart        = null; // TODO this should be removed, doesn't belong in a generic animation handler
}

/**
 * Constructs an animation between two lists of logical objects.  This function 
 * delegates the specific animation behavior to the logical objects.  The animation
 * can be retrieved using getAnimation().
 * @param {array} oldList The list to animate from.
 * @param {array} newList The list to animate to.
 */
DvtDataAnimationHandler.prototype.constructAnimation = function(oldList, newList) {
  if (!newList)
    return;

  // Copy the new objects list, since we will modify it
  newList = newList.slice(0);

  // Loop through the two lists and diff the changes.
  // Note: This implementation considers changes in order to be updates.
  if (oldList) {
    for(var oldIndex=0; oldIndex<oldList.length; oldIndex++) {
      var oldItem = oldList[oldIndex];
      if(!oldItem) // oldItem must exist for update or delete
        continue;
      
      // Loop through the new list looking for a match
      var oldId = oldItem.getId();
      var bMatchFound = false;
      for(var newIndex=0; newIndex<newList.length; newIndex++) {
        var newItem = newList[newIndex];
        if(!newItem) // newItem must exist for update
          continue;
        
        var newId = newItem.getId();
        if((oldId === newId) || (oldId && oldId instanceof DvtObj && oldId.equals && oldId.equals(newId))) {
          // Match found, remove the item from the new list since it's handled
          newItem.animateUpdate(this, oldItem);
          newList.splice(newIndex, 1);
          bMatchFound = true;
          break;
        }
      }
      
      // If no match found, it was a delete.  Pass in the delete container so that
      // the object can choose whether to move to the new container.
      if(!bMatchFound) {
        if (newList[0]) {
          if (newList[0].getChart)
	          oldItem._newChart = newList[0].getChart() ;        // new chart needed for
        }                                                    // some delete animation
        oldItem.animateDelete(this, this._deleteContainer);
      }
    }
  }

  // All remaining objects in newList are inserts
  for(var i=0; i<newList.length; i++) {
    if(newList[i]) // must be valid object for insert
      newList[i].animateInsert(this);
  }
}

/**
 * Adds the specified playable to this handler's animation.
 * @param {DvtPlayable} The playable to add to this animation.
 * @param {number} index The relative ordering of the animation, beginning at 0.
 */
DvtDataAnimationHandler.prototype.add = function(playable, index) {
  if(!playable)
    return;
    
  if(!index)
    index = 0;

  // Make sure the playables array is large enough
  while(this._playables.length <= index)
    this._playables.push(new Array());
  
  // Add the playable to the array
  this._playables[index].push(playable);
}

/**
 * Returns the animation constructed by this handler.
 * @return {DvtPlayable} The animation constructed by this handler.
 */
DvtDataAnimationHandler.prototype.getAnimation = function() {
  // Construct the sequential playable
  var masterPlayable = new Array();
  for(var i=0; i<this._playables.length; i++) {
    // Construct and add the playable for this index in the sequence
    if(this._playables[i].length > 0) {
      var detailPlayable = new DvtParallelPlayable(this._context, this._playables[i]);
      masterPlayable.push(detailPlayable);
    }
  }

  return new DvtSequentialPlayable(this._context, masterPlayable);
}


/**
 * Returns the number of playables in this animation.
 * @returns {Number} the number of playables in this animation.
 */
DvtDataAnimationHandler.prototype.getNumPlayables = function()
{
    return this._playables.length ;
};
/**
 * Base class object for tooltip services.
 * @class DvtTooltipManager
 * @extends DvtObj
 * @constructor
 */
var DvtTooltipManager = function() {};

DvtObj.createSubclass(DvtTooltipManager, DvtObj, "DvtTooltipManager");

/**
 * Displays a tooltip with the given parameters.
 * @param {int} x the x position as relative to coordinate space of implementation
 * @param {int} y the y position as relative to coordinate space of implementation
 * @param {string} text the text to show within the tooltip
 * @param {string} borderColor the border color of the tooltip
 */
DvtTooltipManager.prototype.showDatatip = function(x, y, text, borderColor)
{
  // TODO subclasses should override
};


/**
 * Displays a tooltip.
 * @param {int} x the x position as relative to coordinate space of implementation
 * @param {int} y the y position as relative to coordinate space of implementation
 * @param (string) text The text to display in the tooltip. 
 * @param (DvtDisplayable) component The display object to use for tracking mouse movements.
 * @param (boolean) bTrackMouse Optional boolean to specify whether mouse movement should
 *                  also reposition the tooltip.  Default is true.
 * @param {string} borderColor Optional the border color of the tooltip
 *
 * @see hideTooltip()
 * @see showDatatip()
 */
DvtTooltipManager.prototype.showTooltip = function(x, y, text, component, bTrackMouse, borderColor)
{
  // TODO subclasses should override
};


/**
 * Hides the tooltip.
 */
DvtTooltipManager.prototype.hideTooltip = function()
{
  // TODO subclasses should override
};


/**
 * Interactivity handle for category rollover effects support.  
 * Objects must implement DvtLogicalObject and DvtCategoricalObject to be supported by this handler.
 * @class DvtCategoryRolloverHandler
 * @extends DvtObj
 * @constructor
 */
var DvtCategoryRolloverHandler = function() {};

DvtObj.createSubclass(DvtCategoryRolloverHandler, DvtObj, "DvtCategoryRolloverHandler");

/**
 * Processes the specified DvtCategoryRolloverEvent for the array of objects.
 * @param {DvtCategoryRolloverEvent} event The event that was triggered.
 * @param {array} objs The array of objects containing hide and show targets.
 * @deprecated We should update all components to use the highlight API instead.
 */
DvtCategoryRolloverHandler.processEvent = function(event, objs, customAlpha)
{
  if(!event || !objs)
    return;
  
  var dimmedAlpha = !customAlpha ? 0.35 : customAlpha;
  var eventCategories = event.getCategory();
  var alpha = (event.getType() === DvtCategoryRolloverEvent.TYPE_OVER) ? dimmedAlpha : 1;
  
  // Loop through the objects and update objects not belonging to the specified
  // category.
  for(var i=0; i<objs.length; i++) {
    var obj = objs[i];
    if(obj && obj.getCategories && !DvtCategoryRolloverHandler._hasCategory(obj, eventCategories)) {
      // Found a match, update the displayables
      var displayables = obj.getDisplayables(displayables);
      DvtCategoryRolloverHandler._updateAlpha(displayables, alpha);
    }
  }
}

/**
 * Highlights the objects corresponding to the specified objects.  If no categories are specified, all highlight will
 * be removed.
 * @param {array} categories The array of categories whose data items will be highlighted.
 * @param {array} objs The array of objects containing hide and show targets.
 * @param {number} customAlpha A custom alpha for the dimmed objects.
 */
DvtCategoryRolloverHandler.highlight = function(categories, objs, customAlpha) {
  if(!categories || !objs)
    return;
  
  var dimmedAlpha = !customAlpha ? 0.35 : customAlpha;
  
  // Loop through the objects and update objects not belonging to the specified category.
  for(var i=0; i<objs.length; i++) {
    var obj = objs[i];
    if(obj && obj.getCategories) {
      var alpha = categories && categories.length > 0 && !DvtCategoryRolloverHandler._hasCategory(obj, categories) ? dimmedAlpha : 1;
      var displayables = obj.getDisplayables(displayables);
      DvtCategoryRolloverHandler._updateAlpha(displayables, alpha);
    }
  }
}

/**
 * Updates the alpha of the displayables to the specified value.
 * @param {array} displayables The array of displayables.
 * @param {number} alpha The new alpha value.
 * @private
 */
DvtCategoryRolloverHandler._updateAlpha = function(displayables, alpha) {
  if(!displayables)
    return;

  for(var i=0; i<displayables.length; i++) {
    displayables[i].setAlpha(alpha);
  }
}

/**
 * Returns true if the specified object belongs to one of the specified categories.
 * @param {DvtCategoricalObject} obj
 * @param {array} categories
 * @private
 */
DvtCategoryRolloverHandler._hasCategory = function(obj, categories) {
  if(!obj || !obj.getCategories)
    return false;
  
  var objCategories = obj.getCategories();
  if(categories instanceof Array) {
    for(var categoryIndex=0; categoryIndex<categories.length; categoryIndex++) {
      // Return true if any category matches
      if(DvtArrayUtils.getIndex(objCategories, categories[categoryIndex]) >= 0)
        return true;
    }
    
    // No match found
    return false;
  }
  else // categories is just a single object
    return DvtArrayUtils.getIndex(objCategories, categories) >= 0;
}
/**
 * Interactivity handle for category hide and show support.  
 * Objects must implement DvtLogicalObject and DvtCategoricalObject to be supported by this handler.
 * @class DvtHideShowCategoryHandler
 * @extends DvtObj
 * @constructor
 */
var DvtHideShowCategoryHandler = function() {};

DvtObj.createSubclass(DvtHideShowCategoryHandler, DvtObj, "DvtHideShowCategoryHandler");

/**
 * Processes the specified DvtCategoryHideShowEvent for the array of objects.
 * @param {DvtCategoryHideShowEvent} event The event that was triggered.
 * @param {array} objs The array of objects containing hide and show targets.
 */
DvtHideShowCategoryHandler.processEvent = function(event, objs)
{
  if(!event || !objs)
    return;
    
  var category = event.getCategory();
  var bHide = (event.getType() === DvtCategoryHideShowEvent.TYPE_HIDE);
  
  // Loop through the objects and find the categorical matches
  for(var i=0; i<objs.length; i++) {
    var obj = objs[i];
    if(obj && obj.getCategories && DvtArrayUtils.getIndex(obj.getCategories(), category) >= 0) {
      // Found a match, update the displayables
      var displayables = obj.getDisplayables(displayables);
      DvtHideShowCategoryHandler._updateVisible(displayables, !bHide);
    }
  }
};

/**
 * Updates the visibility of the displayables to the specified value.
 * @param {array} displayables The array of displayables.
 * @param {boolean} bVisible
 */
DvtHideShowCategoryHandler._updateVisible = function(displayables, bVisible) {
  if(!displayables)
    return;

  for(var i=0; i<displayables.length; i++) {
    displayables[i].setVisible(bVisible);
  }
}
/**
 * Event manager that processes low level events and sends them to the appropriate handlers.
 * @param {DvtContext} context The platform specific context object.
 * @param {function} callback A function that responds to component events.
 * @param {object} callbackObj The optional object instance that the callback function is defined on.
 * @class
 * @implements {DvtComponentKeyboardHandler}
 * @constructor
 */
var DvtEventManager = function(context, callback, callbackObj) {
  this.Init(context, callback, callbackObj);
}

DvtObj.createSubclass(DvtEventManager, DvtObj, "DvtEventManager");

DvtEventManager.CLEAR_SELECTION_ACTION_TYPE = "clearSelectionActionType";

/**
 * Method called by the constructor to initialize this object
 * @param {DvtContext} context The platform specific context object.
 * @param {function} callback A function that responds to component events.
 * @param {object} callbackObj The optional object instance that the callback function is defined on.
 * @protected
 */
DvtEventManager.prototype.Init = function(context, callback, callbackObj) {
  this._context = context;
  this._callback = callback;
  this._callbackObj = callbackObj;
  
  // Initialize the higher level event handlers.  These handlers are exposed as protected fields 
  // so that subclasses can fully customize the behavior of this event manager.     
  this._selectionHandler = null; 
  this.ContextMenuHandler = null;
  this._marqueeHandler = null;
  this.PopupHandler = new DvtPopupBehaviorHandler(context, callback, callbackObj);
  this.ClientBehaviorHandler = this.CreateClientBehaviorHandler(context, callback, callbackObj);
  this.KeyboardHandler = null;
  
  // Tooltips enabled by default
  this.setTooltipsEnabled(true);
  
  var id = context.getStage().getId();
  if (!id)
    id = "undefinedId";
  this.CustomTooltipManager = this._context.getCustomTooltipManager();
  this.CustomTooltipManager.addTooltipEventListener(DvtActionTooltipEvent.TOOLTIP_CLOSED_TYPE, this.OnActionTooltipClosed, this);
  this.CustomTooltipManager.addTooltipEventListener(DvtActionTooltipEvent.TOOLTIP_STARTED_TYPE, this.OnActionTooltipStarted, this);
  
  this.TouchManager = null;
  if (DvtAgent.isTouchDevice())    
    this.TouchManager = new DvtTouchManager("touchmanager", this._context);
  this._isMagnifyShown = false;
  
  // The DvtKeyboardNavigable item that currently has keyboard focus
  this._focusedObj = null;
  
  // flag to indicate if the component should display keyboard focus
  this._shouldDisplayKeyboardFocus = false;
  
  //BUG FIX #13376229: support for ignoring the next click after a mouse drag, 
  //so that panning Diagram doesn't change selection, for example
  this._bIgnoreClickAfterDrag = false;
  this._bMouseDown = false;
  this._bMouseDrag = false;
  this._bIgnoreNextClick = false;
  this._ignoreNextClickOffTimer = null;
  
  // Map associating an event with relevant info during its processing lifecycle
  this._eventInfo = {};
  
  // List of object types that should receive rollover/rollout events
  this._rolloverTypes = [];
  
  // An array of event managers that can process keyboard events sent to this
  // event manager, arranged in tab order. This event manager should be in this
  // array, but there may be other event managers as well, such as an event
  // manager for breadcrumbs
  this._keyboardHandlers = [this];
  
  // index to the _keyboardHandlers array indicating the current handler
  // that receives keyboard events
  this._currentKeyboardHandlerIdx = -1;
  
  this._bundle = new DvtUtilBundle();
}

/**
 * Returns the DvtContext associated with this event manager.
 * @return {DvtContext}
 */
DvtEventManager.prototype.getCtx = function() {
  return this._context;
}

/**
 * Associates the specified displayable with the specified object for this event manager.  This is used by the default
 * implementation of GetLogicalObject.
 * @param {DvtDisplayable} displayable The displayable to associate.
 * @param {object} obj The object to associate with.
 */
DvtEventManager.prototype.associate = function(displayable, obj) {
  if(displayable) {
    // Create the logical objects array if not already present
    if(!displayable._logicalObjects)
      displayable._logicalObjects = [];
    
    // Add this logical object and event manager mapping
    displayable._logicalObjects.push({logicalObject:obj, eventManager: this});
    
    // Tooltip support for XML renderer
    if (!DvtAgent.isEnvironmentBrowser() && displayable.getImpl().setLogicalObj)
      displayable.getImpl().setLogicalObj(obj);
  }
}

/**
 * Adds event listeners to the specified displayable.
 * @param {DvtDisplayable} displayable The object on which to add the listeners.
 */
DvtEventManager.prototype.addListeners = function(displayable) {
  if(!displayable)
    return;

  if (DvtAgent.isTouchDevice()) {
       // Hide any tooltips previously shown
       this.hideTooltip();       
       
       // Hide any magnify lens previously shown
       this._hideMagnify();

       displayable.addEvtListener(DvtTouchEvent.TOUCHSTART, this.OnTouchStartBubble, false, this);
       displayable.addEvtListener(DvtTouchEvent.TOUCHMOVE, this.OnTouchMoveBubble, false, this);
       displayable.addEvtListener(DvtTouchEvent.TOUCHEND, this.OnTouchEndBubble, false, this);
     
       this.TouchManager.addTouchEventListener(DvtComponentTouchEvent.TOUCH_HOVER_START_TYPE, this._preOnComponentTouchHoverStart, this);
       this.TouchManager.addTouchEventListener(DvtComponentTouchEvent.TOUCH_HOVER_MOVE_TYPE, this._preOnComponentTouchHoverMove, this);
       this.TouchManager.addTouchEventListener(DvtComponentTouchEvent.TOUCH_HOVER_END_TYPE, this._preOnComponentTouchHoverEnd, this);
       this.TouchManager.addTouchEventListener(DvtComponentTouchEvent.TOUCH_HOVER_OUT_TYPE, this._preOnComponentTouchHoverOut, this);
       this.TouchManager.addTouchEventListener(DvtComponentTouchEvent.TOUCH_HOVER_OVER_TYPE, this._preOnComponentTouchHoverOver, this);

       this.TouchManager.addTouchEventListener(DvtComponentTouchEvent.TOUCH_CLICK_TYPE, this._preOnComponentTouchClick, this);
       this.TouchManager.addTouchEventListener(DvtComponentTouchEvent.TOUCH_DOUBLE_CLICK_TYPE, this._preOnComponentTouchDblClick, this);
       
  } else {
      displayable.addEvtListener(DvtMouseEvent.CLICK, this.PreOnClick, false, this);
      displayable.addEvtListener(DvtMouseEvent.DBLCLICK, this.PreOnDblClick, false, this);
      displayable.addEvtListener('contextmenu', this.PreOnContextMenu, false, this);
      displayable.addEvtListener(DvtMouseEvent.MOUSEMOVE,   this.PreOnMouseMove, false, this);
      displayable.addEvtListener(DvtMouseEvent.MOUSEOVER,   this.PreOnMouseOver, false, this);
      displayable.addEvtListener(DvtMouseEvent.MOUSEOUT,    this.PreOnMouseOut, false, this);
      displayable.addEvtListener(DvtMouseEvent.MOUSEDOWN,   this.PreOnMouseDown, false, this);
      displayable.addEvtListener(DvtMouseEvent.MOUSEUP,     this.PreOnMouseUp, false, this);
      displayable.addEvtListener(DvtKeyboardEvent.KEYDOWN,     this.OnKeyDown, false, this);
      displayable.addEvtListener(DvtKeyboardEvent.KEYUP,       this.OnKeyUp, false, this);
  }
}


/**
 * Removes event listeners from the specified displayable.
 * @param {DvtDisplayable} displayable The object on which to remove the listeners.
 */
DvtEventManager.prototype.removeListeners = function(displayable)
{
  if(!displayable)
    return;
    
  // Hide any tooltips previously shown
  this.hideTooltip();

  if (DvtAgent.isTouchDevice()) {
     // Hide any magnify lens previously shown
     this._hideMagnify(); 

     displayable.removeEvtListener(DvtTouchEvent.TOUCHSTART, this.OnTouchStartBubble, false, this);
     displayable.removeEvtListener(DvtTouchEvent.TOUCHMOVE, this.OnTouchMoveBubble, false, this);
     displayable.removeEvtListener(DvtTouchEvent.TOUCHEND, this.OnTouchEndBubble, false, this);
     
     this.TouchManager.removeTouchEventListener(DvtComponentTouchEvent.TOUCH_HOVER_START_TYPE, this._preOnComponentTouchHoverStart, this);
     this.TouchManager.removeTouchEventListener(DvtComponentTouchEvent.TOUCH_HOVER_MOVE_TYPE, this._preOnComponentTouchHoverMove, this);
     this.TouchManager.removeTouchEventListener(DvtComponentTouchEvent.TOUCH_HOVER_END_TYPE, this._preOnComponentTouchHoverEnd, this);
     this.TouchManager.removeTouchEventListener(DvtComponentTouchEvent.TOUCH_HOVER_OUT_TYPE, this._preOnComponentTouchHoverOut, this);
     this.TouchManager.removeTouchEventListener(DvtComponentTouchEvent.TOUCH_HOVER_OVER_TYPE, this._preOnComponentTouchHoverOver, this);

     this.TouchManager.removeTouchEventListener(DvtComponentTouchEvent.TOUCH_CLICK_TYPE, this._preOnComponentTouchClick, this);
     this.TouchManager.removeTouchEventListener(DvtComponentTouchEvent.TOUCH_DOUBLE_CLICK_TYPE, this._preOnComponentTouchDblClick, this);     
   
  } else {
    displayable.removeEvtListener(DvtMouseEvent.CLICK, this.PreOnClick, false, this);
    displayable.removeEvtListener(DvtMouseEvent.DBLCLICK, this.PreOnDblClick, false, this);
    displayable.removeEvtListener('contextmenu', this.PreOnContextMenu, false, this);
    displayable.removeEvtListener(DvtMouseEvent.MOUSEMOVE,   this.PreOnMouseMove, false, this);
    displayable.removeEvtListener(DvtMouseEvent.MOUSEOVER,   this.PreOnMouseOver, false, this);
    displayable.removeEvtListener(DvtMouseEvent.MOUSEOUT,    this.PreOnMouseOut, false, this);
    displayable.removeEvtListener(DvtMouseEvent.MOUSEDOWN,   this.PreOnMouseDown, false, this);
    displayable.removeEvtListener(DvtMouseEvent.MOUSEUP,     this.PreOnMouseUp, false, this);
    displayable.removeEvtListener(DvtKeyboardEvent.KEYDOWN,     this.OnKeyDown, false, this);
    displayable.removeEvtListener(DvtKeyboardEvent.KEYUP,       this.OnKeyUp, false, this);
  }
  
}

/**
 * Releases all resources owned to prevent memory leaks.
 */
DvtEventManager.prototype.destroy = function()
{   
  if(this.CustomTooltipManager) 
  {
    this.CustomTooltipManager.removeTooltipEventListener(DvtActionTooltipEvent.TOOLTIP_CLOSED_TYPE, this.OnActionTooltipClosed, this);
    this.CustomTooltipManager.removeTooltipEventListener(DvtActionTooltipEvent.TOOLTIP_STARTED_TYPE, this.OnActionTooltipStarted, this);  
  }
}

/**
 * A method called before an event is processed. Sets a touch manager for the event and calls preEventBuble on touch manager object.
 * @param {DvtTouchEvent} event
 * @protected
 */
DvtEventManager.prototype.PreEventBubble = function(event) {
  this.TouchManager.preEventBubble(event);
}

/**
 * Sets the selection handler to use with this event manager.
 * @param {DvtSelectionHandler} handler The selection handler to use.
 */
DvtEventManager.prototype.setSelectionHandler = function(handler) {
  this._selectionHandler = handler;
}

/**
 * Gets the selection handler to use with this event manager.
 * @param {object} logicalObj Logical object used to retrieve the correct selection handler. Optional parameter used for
 * override subclasses like DvtThematicMapEventManager.
 */
DvtEventManager.prototype.getSelectionHandler = function(logicalObj) {
  return this._selectionHandler;
}

/**
 * Sets the marquee handler to use with this event manager.
 * @param {DvtMarqueeHandler} handler The marquee handler to use.
 */
DvtEventManager.prototype.setMarqueeHandler = function(handler) {
  this._marqueeHandler = handler;
}

/**
 * Sets the drag source to use with this event manager.
 * @param {DvtDragSource} dragSource The drag source to use
 */
DvtEventManager.prototype.setDragSource = function(dragSource) {
  this.DragSource = dragSource;
  if (DvtAgent.isTouchDevice())
    this.DragSource.setTouchManager(this.TouchManager);
}

/**
 * Sets the context menu handler to use with this event manager.
 * @param {DvtContextMenuHandler} handler The context menu handler to use.
 */
DvtEventManager.prototype.setContextMenuHandler = function(handler) {
  this.ContextMenuHandler = handler;
}

/**
 * Sets the keyboard handler to use with this event manager. This method is
 * a no-op if we are rendering the component on a touch device.
 * @param {DvtKeyboardHandler} handler The keyboard handler to use.
 */
DvtEventManager.prototype.setKeyboardHandler = function(handler) 
{
  if(!DvtAgent.isTouchDevice())
    this.KeyboardHandler = handler;
}

/**
 * Returns the keyboard handler used by this event manager
 * @return {DvtKeyboardHandler}
 */
DvtEventManager.prototype.getKeyboardHandler = function()
{
  return this.KeyboardHandler;  
}

/**
 * Sets the event manager's keyboard focus on the given DvtKeyboardNavigable,
 * and update the keyboard focus visual feedback.
 * @param {DvtKeyboardNavigable} navigable The DvtKeyboardNavigable to receive keyboard focus
 */
DvtEventManager.prototype.setFocusObj = function(navigable) {
  var curFocus = this.getFocus();
  if (navigable !=  curFocus) {
    this.setFocus(navigable);
    if (curFocus) {
      curFocus.hideKeyboardFocusEffect();
    }
    if (this.ShowKeyboardFocusByDefault()) {
      navigable.showKeyboardFocusEffect();
    }
    
    // set this event manager as the one to receive keyboard events
    if(this._currentKeyboardHandlerIdx > -1 && 
       this._currentKeyboardHandlerIdx < this._keyboardHandlers.length) {
      var handler = this._keyboardHandlers[this._currentKeyboardHandlerIdx];
      if (handler != this) { // Only hide focus effect if the keyboard handler has changed
        handler.hideKeyboardFocusEffect();
      }
    }
    this._updateKeyboardHandlerIdx(this);
  }
};

/**
 * Sets the event manager's keyboard focus on the given DvtKeyboardNavigable
 * @param {DvtKeyboardNavigable} navigable The DvtKeyboardNavigable to receive keyboard focus
 */
DvtEventManager.prototype.setFocus = function(navigable)
{
  if(this.KeyboardHandler)
    this._focusedObj = navigable;
}

/**
 * Returns the DvtKeyboardNavigable item with the current keyboard focus
 * @return {DvtKeyboardNavigable} The DvtKeyboardNavigable with the current keyboard focus
 */
DvtEventManager.prototype.getFocus = function()
{
  return this.KeyboardHandler ? this._focusedObj : null;
}


/**
 * Updates the view when the owning component receives focus 
 */
DvtEventManager.prototype.setFocused = function(isFocused)
{
  var navigable = this.getFocus();
  // don't show keyboard focus effect on touch devices
  if(navigable && this.KeyboardHandler)
  {
    if(!isFocused)
    {
      navigable.hideKeyboardFocusEffect();
    }
    else if(this.getCtx() instanceof DvtFlashContext)
    {
      // TODO: When Flash components get focus, they don't seem to consume
      // tab events. Instead, when the Flash component has keyboard focus
      // and receives a tab, the tab causes the component to lose focus
      // So rather than wait for an additional keystroke before showing
      // the keyboard focus effect, show the focus effect as soon as focus
      // is received. This doesn't fix the tab issue, but it does restore
      // keyboard functionality in the Flash case.
      navigable.showKeyboardFocusEffect();
    }
    else if( this._shouldDisplayKeyboardFocus &&
             this._currentKeyboardHandlerIdx >=0 &&
             this._currentKeyboardHandlerIdx < this._keyboardHandlers.length &&
             ((this._keyboardHandlers[this._currentKeyboardHandlerIdx] === this))
           )
    {
      // show keyboard focus effect if we receive focus after completing an animation
      // and we had keyboard focus before the animation began
      navigable.showKeyboardFocusEffect();
    }
  }    
  
  if(!isFocused)
    this._shouldDisplayKeyboardFocus = false;
}

/**
 * Returns true if the keyboard focus should be shown by default (when the component has focus), even if the user 
 * has not used the keyboard to intearact with the component
 * 
 * @return {Boolean}
 */
DvtEventManager.prototype.ShowKeyboardFocusByDefault = function() 
{
  return false;
}

/**
 * Specifies whether tooltips are enabled.
 * @param {boolean} tooltipsEnabled
 */
DvtEventManager.prototype.setTooltipsEnabled = function(tooltipsEnabled) {
  this._tooltipsEnabled = tooltipsEnabled;
}

/**
 * Returns true if tooltips are enabled.
 * @return {boolean}
 */
DvtEventManager.prototype.getTooltipsEnabled = function() {
  return this._tooltipsEnabled;
}

/**
 * Returns the logical object corresponding to the specified DvtDisplayable.  All high level event handlers,
 * such as the selection and popup handlers, are designed to react to the logical objects.
 * @param {DvtDisplayable} target The displayable.
 * @param {boolean} ignoreParents (optional) true indicates that parent displayables should not be searched if the 
 *                  target doesn't have its own logical object
 * @return {object} The logical object corresponding to the target.
 * @protected
 */
DvtEventManager.prototype.GetLogicalObject = function(target, ignoreParents) {
  var retObj = this.GetLogicalObjectAndDisplayable(target, ignoreParents);
  if (retObj) {
    return retObj.logicalObject;
  }
  return null;
};

/**
 * Returns the logical object and associated DvtDisplayable corresponding to the specified DvtDisplayable.  
 * All high level event handlers, such as the selection and popup handlers, are designed to react to the 
 * logical objects.
 * @param {DvtDisplayable} target The displayable.
 * @param {boolean} ignoreParents (optional) true indicates that parent displayables should not be searched if the 
 *                  target doesn't have its own logical object
 * @return {object} The logical object and associated DvtDisplayable corresponding to the target.
 * @protected
 */
DvtEventManager.prototype.GetLogicalObjectAndDisplayable = function(target, ignoreParents) {
  //BUG FIX 15851577: the popup handler needs to keep track of the displayable associated with the logical
  //object that launched a mousehover popup in order to dismiss the popup at the right time
  var displayable = target;
  while (displayable) {
    if(displayable._logicalObjects) {
      for(var i=0; i<displayable._logicalObjects.length; i++) {
        var mapping = displayable._logicalObjects[i];
        if(mapping.eventManager == this) {
          var retObj = {};
          retObj.logicalObject = mapping.logicalObject;
          retObj.displayable = displayable;
          return retObj;
        }
      }
    }
    displayable = ignoreParents ? null : displayable.getParent();
  }
  return null;
}

/**
 * Returns the array of showPopupBehaviors for a specified logical object.
 * @param {object} obj The logical target object.
 * @return {array} The array of applicable DvtShowPopupBehaviors
 * @private
 */
DvtEventManager.prototype._getShowPopupBehaviors = function(obj) {
  if(obj && obj.getShowPopupBehaviors)
    return obj.getShowPopupBehaviors();
}

/**
 * Returns the array of clientBehaviors for a specified logical object.
 * @param {object} obj The logical target object.
 * @return {array} The array of applicable DvtClientBehaviors
 * @private
 */
DvtEventManager.prototype._getClientBehaviors = function(obj) {
  if(obj && obj.getClientBehaviors)
    return obj.getClientBehaviors();
}

/**
 * Returns the menu type to display.
 * @param {object} logicalObj Logical object used to retrieve the correct selection handler. 
 * @return {object} The menu type to pass into the context menu handler.
 * @protected
 */
DvtEventManager.prototype.GetContextMenuType = function(logicalObj) {
  // Figure out the selection size, which determines which menu to display
  var selectionHandler = this.getSelectionHandler(logicalObj);
  var selectionSize = selectionHandler ? selectionHandler.getSelection().length : 0;
  if(selectionSize <= 0)
    return DvtContextMenuHandler.TYPE_BODY_CONTEXT_MENU;
  else if(selectionSize == 1)
    return DvtContextMenuHandler.TYPE_CONTEXT_MENU;
  else
    return DvtContextMenuHandler.TYPE_MULTI_SELECT_CONTEXT_MENU;
}

/**
 * Returns the id of the specific context menu to display for the specified logical object.
 * @param {object} obj The logical object
 * @return {object} The menu id to pass into the context menu handler.
 * @private
 */
DvtEventManager.prototype._getContextMenuId = function(obj) {
  if(obj && obj.getContextMenuId)
    return obj.getContextMenuId();
}

/**
 * Returns the tooltip color for the specified object.
 * @param {object} obj The logical object.
 * @param {number} x The relative x coordinate of the event
 * @param {number} y The relative y coordinate of the event
 */
DvtEventManager.prototype.GetTooltipColor = function(obj, x, y) {
  if(obj && obj.getDatatipColor)
    return obj.getDatatipColor();
}

/**
 * Fires the specified event through the callback.
 * @param {object} event
 * @param {object} source The component that is the source of the event, if available.
 */
DvtEventManager.prototype.FireEvent = function(event, source) {
  if(this._callback)
    this._callback.call(this._callbackObj, event, source);
}

/**
 * Creates and fires an instance of DvtComponentUIEvent for the specified object.
 * @param {string} type The type of DvtComponentUIEvent.
 * @param {object} logicalObj The logical object corresponding to the target of the event.
 * @protected
 */
DvtEventManager.prototype.FireUIEvent = function(type, logicalObj) {
  // subclasses must override if the event is to be fired
}

/**
 * Fires a selection event with the current selection state.
 * @param {object} logicalObj Logical object used to retrieve the correct selection handler. 
 * @private
 */
DvtEventManager.prototype.fireSelectionEvent = function(logicalObj) {
  var selectionHandler = this.getSelectionHandler(logicalObj);
  if(!selectionHandler)
    return;
    
  // Get the array of selected ids
  var selectedIds = selectionHandler.getSelectedIds();

  // Create and fire the event
  var selectionEvent = new DvtGraphSelectionEvent(selectedIds);
  this._callback.call(this._callbackObj, selectionEvent);
}

//*******************************************************************************//
//*********************** Begin Event Listeners *********************************//
//*******************************************************************************//

/**
 * Click event handler that accounts for double clicks
 * @protected
 */
DvtEventManager.prototype.PreOnClick = function(event) {
  //BUG FIX #13376229: if ignoring the next click, toggle the flag and return
  if (this._bIgnoreClickAfterDrag && this.IsIgnoreNextClick()) {
    //turn off the flag
    this.SetIgnoreNextClick(false);
    return;
  }
  
  if (this.IsDoubleClickable(event.target)) {
    if (this._clickTimer && this._clickTimer.isRunning()) {
      var clickEvent = this._savedClickEvent;
      if (event.pageX == clickEvent.pageX && event.pageY == clickEvent.pageY) {
        // Same coords, this is a double click, so ignore second click event
        return;
      }
      else {
        // Different coords, so need to process first event
        this._clickTimer.stop();
        this._onClickTimerEnd();
      }
    }
    this._savedClickEvent = event;
    if (!this._clickTimer) {
      this._clickTimer = new DvtTimer(this._context, 250, this._onClickTimerEnd, this, 1);
    }
    this._clickTimer.reset();
    this._clickTimer.start()
  }
  else {
    this._propagateEvent(DvtMouseEvent.CLICK, event, this.OnClick);
  }
}

DvtEventManager.prototype._onClickTimerEnd = function() {
  var clickEvent = this._savedClickEvent;
  this._savedClickEvent = null;
  if (clickEvent) {
    this._propagateEvent(DvtMouseEvent.CLICK, clickEvent, this.OnClick);
  }  
}


/**
 * Click event handler.
 * @param {DvtObj} event The platform specific mouse event
 * @protected
 */
DvtEventManager.prototype.OnClick = function(event) {
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
  this.OnClickInternal(event);
  
  this.ProcessSelectionEventHelper(obj, event.ctrlKey);
  
  // Stop displaying keyboard focus after the user has clicked
  this._shouldDisplayKeyboardFocus = false;

  // Done if there is no object
  if(! obj)
  {
    return;
  }
  
  // BUGFIX 13263165 and 13377513 - update the keyboard focus on mouse click
  if(this.KeyboardHandler && obj.getNextNavigable)  
  {
    var nextFocus = obj.getNextNavigable(event);
    this.setFocusObj(nextFocus);
  }

  // Popup Support for triggerType="click"
  var consumed = null;
  if(this._eventInfo[event]) {
    consumed = this._eventInfo[event]['popupDisplayed'];
  }

  if (!consumed) {
    var behaviors = this._getShowPopupBehaviors(obj);
    if(behaviors) {
      var position = new DvtPoint(event.pageX, event.pageY);
      consumed = this.PopupHandler.processClick(obj, behaviors, position);
    }
  
    if (consumed) {
      this._eventInfo[event]['popupDisplayed'] = true;
    }
  }

  // Client Behavior Support for triggerType="click"
  if (!consumed) {
    var clientBehaviors = this._getClientBehaviors(obj);
    if (clientBehaviors) {
      consumed = this.ClientBehaviorHandler.processClick(obj, clientBehaviors);
    }  
  }

  // fire an Action event if logical obj is a command component
  if (! consumed && obj.isCommandComponent && obj.isCommandComponent()) {
    // Create the action event
    var event = new DvtGraphActionEvent(DvtGraphActionEvent.SUBTYPE_ACTION, obj.getId());

    // add the component specific context
    DvtEventManager._addContextInfo(obj, event);

    // fire the action event
    this._callback.call(this._callbackObj, event);
  }
    
  // DvtComponentUIEvent Support
  this.FireUIEvent(DvtComponentUIEvent.TYPE_CLICK, obj);
}

DvtEventManager.prototype.IsDoubleClickable = function(leafTarget) {
  var displayable = leafTarget;
  while (displayable) {
    var logicalObject = this.GetLogicalObject(displayable, true);
    if (logicalObject) {
      if (logicalObject.isDoubleClickable && logicalObject.isDoubleClickable()) {
        return true;
      }
      if (!this.IsPropagationEnabled(DvtMouseEvent.DBLCLICK, null, displayable)) {
        return false;
      }
    }
    displayable = displayable.getParent();
  }
  return false;
}

/**
 * Helper function to process selection events
 * @param {DvtLogicalObject} logicalObj The logical object to process selection on
 * @param {Boolean} isMultiSelect True if we are performing multi-select
 * @private 
 */
DvtEventManager.prototype.ProcessSelectionEventHelper = function(logicalObj, isMultiSelect)
{
  // Selection Support
  var selectionHandler = this.getSelectionHandler(logicalObj);
  if(selectionHandler) {
    var bSelectionChanged = selectionHandler.processClick(logicalObj, isMultiSelect);
    // If the selection has changed, fire an event
    if(bSelectionChanged) 
      this.fireSelectionEvent(logicalObj);
  }  
}

DvtEventManager.prototype.PreOnDblClick = function(event) {
  this._propagateEvent(DvtMouseEvent.DBLCLICK, event, this.OnDblClick);
}

/**
 * Double click event handler.
 * @protected
 */
DvtEventManager.prototype.OnDblClick = function(event) {
  if (this._clickTimer && this._clickTimer.isRunning()) {
    this._clickTimer.stop();
    this._savedClickEvent = null;
  }
  else {
    if (DvtAgent.isPlatformIE()) {
      var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
      if (!obj) {
        return;
      }
      
      if (!obj.isDoubleClickable || !obj.isDoubleClickable()) {
        // Need to turn this into a single click for IE which only sends out click,dblclick upon a double-click rather
        // than the click,click,dblclick sent out by other browsers
        this.OnClick(event);
        return;
      }
    }
  }
}

DvtEventManager.prototype.PreOnContextMenu = function(event) {
  this._propagateEvent('contextmenu', event, this.OnContextMenu);
}

/**
 * Context Menu event handler.
 * @protected
 */
DvtEventManager.prototype.OnContextMenu = function(event) {
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
  
  // set keyboard focus on the object we are firing the context menu on
  if(obj && obj.getNextNavigable)
    this.setFocus(obj);
  
  this._onContextMenuHelper(event, obj);
}

/**
 * Helper method that does the bulk of the context menu handling
 * 
 * @param {DvtMouseEvent} event
 * @param {Object} obj  The logical object on which the mouse event was fired
 */
DvtEventManager.prototype._onContextMenuHelper = function(event, obj)
{
  this.OnContextMenuInternal(event);
  var popupLaunched = this._processActionPopup(this.GetCurrentTargetForEvent(event),  new DvtPoint(event.pageX, event.pageY));
  if (popupLaunched)
    event.preventDefault();
    
  this.ProcessSelectionEventHelper(obj, event.ctrlKey);
  
  // Context Menu Support
  if(this.ContextMenuHandler) {
    var menuType = this.GetContextMenuType(obj);
    var menuId = this._getContextMenuId(obj);
    this.ContextMenuHandler.prepareMenuItems(event, menuType, menuId);
    var contextMenuEvent = this.ContextMenuHandler.show(event, menuType, menuId); 
    if(contextMenuEvent) {
      // Hide the tooltip before displaying the menu
      this._context.getTooltipManager().hideTooltip();
      
      //BUG FIX 16018606: stop a mouseHover popup when a context menu is displayed
      this.PopupHandler.HidePopup();
      
      // Request the context menu be displayed
      this._callback.call(this._callbackObj, contextMenuEvent);
    }
  }  
}

/**
 * Keypress down event handler.  Delegates to DvtComponentKeyboardHandlers 
 * until event is consumed.
 * @param {DvtKeyboardEvent} event
 * @return {Boolean} true if this event manager has consumed the event
 * @protected
 */
DvtEventManager.prototype.OnKeyDown = function(event) 
{
  var keyCode = event.keyCode;
  var eventConsumed = false;  

  var i;
  var increment;
  
  // if no current event manager is set to receive keyboard focus, 
  // start at one end of the array and pass event to each event manager until
  // one event manager consumes the event.  that will be the current event manager
  if(this._currentKeyboardHandlerIdx < 0 || 
     this._currentKeyboardHandlerIdx > this._keyboardHandlers.length)
  {    
    // if we get a shift+tab, start from the back of the array
    if(keyCode == DvtKeyboardEvent.TAB && event.shiftKey)
      i = this._keyboardHandlers.length - 1;
    else
      i = 0;
  }
  else
  {
    i = this._currentKeyboardHandlerIdx;
  }

  // if we get a shift+tab, start from the back of the array and move backward
  if(keyCode == DvtKeyboardEvent.TAB && event.shiftKey)
    increment = -1; 
  else
    increment = 1;
 
  for(i; (i >=0 && i<this._keyboardHandlers.length && !eventConsumed); i=i+increment)
  {
    var handler = this._keyboardHandlers[i];
    if(handler=== this)
      eventConsumed = this.ProcessKeyboardEvent(event);
    else
      eventConsumed = handler.handleKeyboardEvent(event);

    if(eventConsumed)
      this._currentKeyboardHandlerIdx = i;    
  }
  
  return eventConsumed;
}


/**
 * @override
 */
DvtEventManager.prototype.handleKeyboardEvent = function(event)
{
  return this.OnKeyDown(event);
}

/**
 * @override
 */
DvtEventManager.prototype.hideKeyboardFocusEffect = function()
{
  var currentNavigable = this.getFocus();

  if(currentNavigable && currentNavigable.isShowingKeyboardFocusEffect())
  {    
    currentNavigable.hideKeyboardFocusEffect();
  }
}

/**
 * Keypress down event handler.  Provides basic keyboard navigation and 
 * triggering of context menus.
 * @param {DvtKeyboardEvent} event
 * @return {Boolean} true if this event manager has consumed the event
 * @protected
 */
DvtEventManager.prototype.ProcessKeyboardEvent = function(event) 
{  
  if(!this.KeyboardHandler)
    return false;

  // clear tooltip if one is shown; tooltip can be shown if mouse is used to make a selection
  // and then we use the keyboard to navigate from the mouse-selected item
  this.hideTooltip();
  
  
  var currentNavigable = this.getFocus();
  var nextNavigable = null;
  
  if(event.keyCode == DvtKeyboardEvent.TAB && currentNavigable &&
     currentNavigable.isShowingKeyboardFocusEffect())
  {
    // handle the case where we tab out of a component
    // don't cancel the event and propoagate it onwards
    currentNavigable.hideKeyboardFocusEffect();
    return false;
  }
  
  if(currentNavigable && this.KeyboardHandler.isContextMenuEvent(event))
  {
    var contextMenuLocation;
    
    if(currentNavigable.getContextMenuLocation)
    {
      contextMenuLocation = currentNavigable.getContextMenuLocation();      
    }
    else
    {
      var bounds = currentNavigable.getKeyboardBoundingBox();
      // adjust location for bidi; use top left corner for bidi, top right corner for non-bidi, 
      if(DvtAgent.isRightToLeft(this._context))
        contextMenuLocation = new DvtPoint(bounds.x, bounds.y);        
      else
        contextMenuLocation = new DvtPoint(bounds.x+bounds.w, bounds.y);
    }
  
    var mouseEvent = this.GenerateMouseEventFromKeyboardEvent(event, this._context.getStage(), contextMenuLocation.x, contextMenuLocation.y);
    // OnContextMenu checks for ctrlKey to perform multi-select before showing context menu
    // However, the keystroke combination for showing the context menu is Ctrl+Alt+M
    // CLEAR the ctrlKey field so that multi-select isn't performed, and send OnContextMenu
    // a mouse event that is equivalent to right-clicking on the navigable with keyboard focus
    // With the mouse, a user could Ctrl+right click a navigbale to multi-select it and then bring up the
    // context menu.  In the keyboard case, the user would first have to multi-select and then bring up the 
    // context menu, in two separate keystrokes
    mouseEvent.ctrlKey = false; 
    this._onContextMenuHelper(mouseEvent, currentNavigable);
    return true;
  }
  

  nextNavigable = this.KeyboardHandler.processKeyDown(event);

  if(nextNavigable)
  {
    // the user has transferred focus via the keyboard, start showing keyboard focus effect
    this._shouldDisplayKeyboardFocus = true;
    if (currentNavigable)
      currentNavigable.hideKeyboardFocusEffect();
    if(this.KeyboardHandler.isSelectionEvent(event))
      this.ProcessSelectionEventHelper(nextNavigable, event.shiftKey);
    else if(this.KeyboardHandler.isMultiSelectEvent(event))
      this.ProcessSelectionEventHelper(nextNavigable, event.ctrlKey);

    nextNavigable.showKeyboardFocusEffect();
    
    // Accessibility Support
    this._fireActiveElementChangeEvent(nextNavigable);
    
    return true;
  }
  
  return false;
}

/**
 * Returns a DvtMouseEvent that wraps the given keyboard event.  The given stageX and stageY coordinates are used to
 * compute the DvtMouseEvent's pageX and pageY fields
 * 
 * @param {DvtKeyboardEvent} event
 * @param {DvtStage} stage
 * @param {Number} stageX
 * @param {Number} stageY
 * @return {DvtMouseEvent}
 * @protected
 */
DvtEventManager.prototype.GenerateMouseEventFromKeyboardEvent = function(event, stage, stageX, stageY)
{
  var dvtMouseEvent = null;
  
  if(this._context instanceof DvtSvgContext)
  {    
    dvtMouseEvent = DvtSvgEventFactory.generateMouseEventFromKeyboardEvent(event, this._context, DvtMouseEvent.CLICK, stage, stageX, stageY);
  }
  
  // TODO: support other platforms
  return dvtMouseEvent;
}


/**
 * Keypress up event handler.  Empty implementation; subclasses will need to add their own implementation
 * @param {Object} event
 * @protected
 */
DvtEventManager.prototype.OnKeyUp = function(event) {  }

DvtEventManager.prototype.PreOnMouseMove = function(event) {
  this._propagateEvent(DvtMouseEvent.MOUSEMOVE, event, this.OnMouseMove);
}

/**
 * Mouse Move event handler
 * @protected
 */
DvtEventManager.prototype.OnMouseMove = function(event) {
  //BUG FIX #13376229: maintain flag indicating if the mouse is dragged
  if (this._bIgnoreClickAfterDrag && this._bMouseDown) {
    this._bMouseDrag = true;
  }
    
  var pageX = event.pageX;
  var pageY = event.pageY;
  var relPos = this._context.getRelativePosition(pageX, pageY);
  
  if (this._marqueeHandler) {
    var marqueeEvent = this._marqueeHandler.processDragMove(relPos, event.ctrlKey);
    if (marqueeEvent)
      this._callback.call(this._callbackObj, marqueeEvent);
  }
  
  var target = this.GetCurrentTargetForEvent(event);
  var obj = this.GetLogicalObject(target);
  
  // Return if no object is found
  if(!obj)
    return;
  
  if (!this._eventInfo[event]['tooltipDisplayed']) {
    if (this.IsTooltipShowable(obj, relPos)) {
        // Show and move the tooltip
        if (this._processShowTooltip(target, obj, pageX, pageY)) {
          this._eventInfo[event]['tooltipDisplayed'] = true;
        };
    } else {
        this.hideTooltip();
    }
  }

  var position = new DvtPoint(pageX, pageY);
  // Notify the popup handler of the mouse move
  this.PopupHandler.processMouseMove(position);
  
  // Drag and Drop Support
  if(this.DragSource && !event.dragSourceSet && this.IsDragCandidate(obj)) {
    this.DragSource.setDragCandidate(obj);
    event.dragSourceSet = true;
  }

}

DvtEventManager.prototype.PreOnMouseOver = function(event) {
  this._propagateEvent(DvtMouseEvent.MOUSEOVER, event, this.OnMouseOver);
  this._handlePreOverEvent(event);
}

DvtEventManager.prototype._getDisplayableHierarchy = function(target, filterFunc, filterFuncObj) {
  var hierarchy = [];
  var currentTarget = target;
  while (currentTarget) {
    var bInclude = true;
    if (filterFunc) {
      bInclude = filterFunc.call(filterFuncObj, currentTarget);
    }
    if (bInclude) {
      hierarchy.unshift(currentTarget);
    }
    currentTarget = currentTarget.getParent();
  }
  return hierarchy;
}

DvtEventManager.prototype._getRolloverHierarchy = function(target) {
  return this._getDisplayableHierarchy(target, this._isRolloverType, this);
}
 
 
DvtEventManager.prototype._isRolloverType = function(target) {
  var logicalObject = this.GetLogicalObject(target, true);
  if (logicalObject) {
    for (var i = 0; i < this._rolloverTypes.length; i++) {
      if (logicalObject instanceof this._rolloverTypes[i]) {
        return true;
      }
    }
  }
  return false;
}

DvtEventManager.prototype.addRolloverType = function(type) {
  this._rolloverTypes.push(type);
}

DvtEventManager.prototype.removeRolloverType = function(type) {
  var index = this._rolloverTypes.indexOf(type);
  if (index != -1) {
    this._rolloverTypes.splice(index, 1);
  }
}

DvtEventManager.prototype.OnRollOver = function(event) {
  
}

/**
 * Mouse Over event handler
 * @protected
 */
DvtEventManager.prototype.OnMouseOver = function(event) {
  var currTargetForEvent = this.GetCurrentTargetForEvent(event);
  var logObjAndDisp = this.GetLogicalObjectAndDisplayable(currTargetForEvent);
  var obj = null;
  if (logObjAndDisp) {
    obj = logObjAndDisp.logicalObject;
  }
  var position = new DvtPoint(event.pageX, event.pageY);
  
  // Return if no object is found
  if(!obj)
    return;
    
  // Selection Support
  var selectionHandler = this.getSelectionHandler(obj);
  if(selectionHandler)
    selectionHandler.processMouseOver(obj);
  
  // DvtComponentUIEvent Support
  this.FireUIEvent(DvtComponentUIEvent.TYPE_ROLL_OVER, obj);

  if (!this._eventInfo[event]['popupDisplayed']) {
    // Popup Support for triggerType="hover"
    var behaviors = this._getShowPopupBehaviors(obj);
    if(behaviors) {
      //BUG FIX 15851577: pass the displayable corresponding to the logical object to the popup handler
      //so that it can keep track of when to dismiss a mousehover popup
      if (this.PopupHandler.processMouseOver(obj, behaviors, position, logObjAndDisp.displayable)) {
        this._eventInfo[event]['popupDisplayed'] = true;
      }
    }
  }
  
  // Accessibility Support
  this._fireActiveElementChangeEvent(currTargetForEvent);
}

DvtEventManager.prototype.PreOnMouseOut = function(event) {
  this._handlePreOutEvent(DvtMouseEvent.MOUSEOUT, event, this.OnMouseOut);
}

DvtEventManager.prototype.OnRollOut = function(event) {
  
}

/**
 * Mouse Out event handler
 * @protected
 */
DvtEventManager.prototype.OnMouseOut = function(event) {
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
    
  // Return if no object is found
  if(!obj)
    return;
  
  var relatedObj = this.GetLogicalObject(event.relatedTarget);
  if(obj == relatedObj)
    return; // not a mouse out if they correspond to the same object
  
  // Selection Support
  var selectionHandler = this.getSelectionHandler(obj);
  if(selectionHandler)
    selectionHandler.processMouseOut(obj);
  
  // DvtComponentUIEvent Support
  this.FireUIEvent(DvtComponentUIEvent.TYPE_ROLL_OUT, obj);
      
  // BUGFIX 13604291 - only show keyboard focus effect if the component has received a keystroke
  //   if the component has not received a keystroke, hide the keyboard focus effect
  var navigable = this.getFocus();
  if(navigable)
  {
    // some components may use the hover effect to indicate keyboard focus as well.
    // in these cases, do not remove the visual effect when we mouse out of the target
    // with keyboard focus    
    if(obj === navigable)
    {      
      if(this._shouldDisplayKeyboardFocus && !navigable.isShowingKeyboardFocusEffect())
        navigable.showKeyboardFocusEffect();
    }
      
  }
  
  // Bug #12957574: If the mouse has moved off the component, hide the tooltip  
  this.hideTooltip();
  
  // Clear drag source when moving out of component
  if(this.DragSource && !event.dragSourceSet) {
    this.DragSource.setDragCandidate(null);
    event.dragSourceSet = true;
  }
}

DvtEventManager.prototype.PreOnMouseDown = function(event) {
  this._propagateEvent(DvtMouseEvent.MOUSEDOWN, event, this.OnMouseDown);
}


/**
 * Mouse Down event handler
 * @protected
 */
DvtEventManager.prototype.OnMouseDown = function(event) {
  //BUG FIX #13376229: maintain flag indicating if the mouse down
  if (this._bIgnoreClickAfterDrag) {
    this._bMouseDown = true;
  }
  
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
  var relPos = this._context.getRelativePosition(event.pageX, event.pageY);

  var isPopup = this.isInActionPopup();
  if (isPopup) {
    this.CustomTooltipManager.closeActionTooltip();
  }
  
  if (this._marqueeHandler) {
    var marqueeEvent = this._marqueeHandler.processDragStart(relPos, event.ctrlKey);
    if (marqueeEvent)
      this._callback.call(this._callbackObj, marqueeEvent);
  }
  
   // Drag and Drop Support
  if(this.DragSource && !event.dragSourceSet && this.IsDragCandidate(obj)) {
    this.DragSource.setDragCandidate(obj);
    event.dragSourceSet = true;
  }
}

DvtEventManager.prototype.PreOnMouseUp = function(event) {
  this._propagateEvent(DvtMouseEvent.MOUSEUP, event, this.OnMouseUp);
}

/**
 * Mouse Up event handler
 * @protected
 */
DvtEventManager.prototype.OnMouseUp = function(event) {
  //BUG FIX #13376229: maintain flag indicating if we should ignore the
  //next click event because the mouse was dragged
  if (this._bIgnoreClickAfterDrag && this._bMouseDown && this._bMouseDrag) {
    //turn on the flag
    this.SetIgnoreNextClick(true);
  }
  this._bMouseDown = false;
  this._bMouseDrag = false;
  
  var relPos = this._context.getRelativePosition(event.pageX, event.pageY);
  
  if (this._marqueeHandler) {
    var marqueeEvent = this._marqueeHandler.processDragEnd(relPos, event.ctrlKey);
    if (marqueeEvent)
      this._callback.call(this._callbackObj, marqueeEvent);
  }

  // no default behavior, subclasses can override
}

// Shows multi-touch tooltips.
DvtEventManager.prototype._processMultiTouchTooltip = function() {
    var tooltipInfoObj = this.TouchManager.getTooltipInfo();
    var touchIds = tooltipInfoObj.touchIds;
    var tooltipTarget = tooltipInfoObj.tooltipTarget;
    
    // If no hints are there, hide the tooltip
    if (touchIds.length == 0 || !tooltipTarget) {
        this.hideTooltip();
        return;
    }
    
    var avgPos = this.TouchManager.calcAveragePosition(touchIds);
    var obj = this.GetLogicalObject(tooltipTarget);
    this._processObjectTooltip(avgPos.x, avgPos.y, obj, null);
}

/**
 * Get the timer to turn off the flag to ignore the next mouse click.
 * @protected
 */
DvtEventManager.prototype.GetIgnoreNextClickOffTimer = function () {
  if (!this._ignoreNextClickOffTimer) {
    this._ignoreNextClickOffTimer = new DvtTimer(this._context, 500, this.HandleIgnoreNextClickOffTimer, this, 1);
  }
  
  return this._ignoreNextClickOffTimer;
};

/**
 * Handle an event from the timer to turn off the flag to ignore the
 * next mouse click.  
 * @protected
 */
DvtEventManager.prototype.HandleIgnoreNextClickOffTimer = function (event) {
  //turn off the flag
  this.SetIgnoreNextClick(false);
};

/**
 * Set the flag to ignore the next mouse click.
 * @protected
 */
DvtEventManager.prototype.SetIgnoreNextClick = function(bIgnoreNextClick) {
  this._bIgnoreNextClick = bIgnoreNextClick;
  
  //if turning on the flag, start the timer to shut it off
  //automatically in case a mouse click is not registered
  //immediately
  if (bIgnoreNextClick)
  {
    this.GetIgnoreNextClickOffTimer().reset();
    this.GetIgnoreNextClickOffTimer().start();
  }
  //if turning off the flag, stop the auto-off timer
  else
  {
    if (this.GetIgnoreNextClickOffTimer().isRunning())
      this.GetIgnoreNextClickOffTimer().stop();
  }
};

/**
 * Determine if the flag to ignore the next mouse click is set.
 * @protected
 */
DvtEventManager.prototype.IsIgnoreNextClick = function() {
  return this._bIgnoreNextClick;
};

/**
 * Touch Start event handler
 * @param {DvtTouchEvent} event
 * @protected
 */
DvtEventManager.prototype.OnTouchStartBubble = function(event) {
    this.PreEventBubble(event);

    if (event.isInitialTouch()) {
      this._popupJustClosed = false;
    }

    // Drag and Drop Support
    if(this.DragSource) {
        var obj = this.GetLogicalObject(event.target);
        if (!event.dragSourceSet && this.IsDragCandidate(obj)) {
            this.DragSource.setDragCandidate(obj);
            event.dragSourceSet = true;
        }
    }

    var isPopup = this.isInActionPopup();
    // Action popups block other types of interactions
    if (isPopup) {
       this.CustomTooltipManager.closeActionTooltip();
       this.TouchManager.resetTouchHold();
    }
    this.HandleImmediateTouchStartInternal(event);
    this.TouchManager.fireLogicalEvents(event);
    this.TouchManager.postEventBubble(event);
    
    this._processMultiTouchTooltip();
}
/**
 * Touch Move event handler
 * @param {DvtTouchEvent} event
 * @protected
 */
DvtEventManager.prototype.OnTouchMoveBubble = function(event) {
  this.PreEventBubble(event);

  if (this.TouchManager) {
        this._prevActionClear = false;
    
        this.HandleImmediateTouchMoveInternal(event);
        this.TouchManager.fireLogicalEvents(event);
        this.TouchManager.postEventBubble(event);

        this._processMultiTouchTooltip();
    }
}

/*
 * Timeout needed to put touch end after an attempt to touch & hold
 */
DvtEventManager.prototype._handleTouchEndTimer = function() {    
    for (var i=0;i<this._touchEndTimer.length;i++) {
        var timerObj = this._touchEndTimer[i];
        timerObj["timer"].stop();
        var event = timerObj["event"];
        if (this.TouchManager) {
            this.HandleImmediateTouchEndInternal(event);
            this.TouchManager.fireLogicalEvents(event);
            this.TouchManager.postEventBubble(event);
        }
    }
    // Clear drag source on touch end timer
    if(this.DragSource && !event.dragSourceSet) {
        this.DragSource.setDragCandidate(null);
        event.dragSourceSet = true;
    }    
    this._touchEndTimer = new Array();
}
/**
 * Touch End event handler
 * @param {DvtTouchEvent} event
 * @protected
 */
DvtEventManager.prototype.OnTouchEndBubble = function(event) {
   this.PreEventBubble(event);
   var timer = new DvtTimer(this._context, 0, this._handleTouchEndTimer, this, 1);
   if (!this._touchEndTimer)
    this._touchEndTimer = new Array();
   this._touchEndTimer.push({"event": event, "timer": timer});
   timer.start();
}

DvtEventManager.prototype.HandleImmediateTouchStartInternal = function(event) {
}

DvtEventManager.prototype.HandleImmediateTouchMoveInternal = function(event) {
}

DvtEventManager.prototype.HandleImmediateTouchEndInternal = function(event) {
}

/**
 * Touch hover start event handler
 * @private
 * @param {DvtComponentTouchEvent} event
 */
DvtEventManager.prototype._preOnComponentTouchHoverStart = function(event) {
  this._propagateEvent(DvtComponentTouchEvent.TOUCH_HOVER_START_TYPE, event, this.OnComponentTouchHoverStart);
}

/**
 * Touch hover start event handler. Subclasses can override.
 * @param {DvtComponentTouchEvent} event
 * @protected
 */
DvtEventManager.prototype.OnComponentTouchHoverStart = function(event) {
  var targetObj = this.GetCurrentTargetForEvent(event);
  var obj = this.GetLogicalObject(targetObj);
    
  var touch = event.touch;
  var touchX = touch.pageX;
  var touchY = touch.pageY;
  var relPos = this._context.getRelativePosition(touchX, touchY);
    
  if (this._marqueeHandler) {
    var marqueeEvent = this._marqueeHandler.processDragStart(relPos, event.ctrlKey);
    if (marqueeEvent) {
      event.preventDefault();
      this._callback.call(this._callbackObj, marqueeEvent);  
    }
  }   
      
  this.UpdateTouchHoverFeedback(targetObj);

  this.ProcessMagnifyStart(new DvtPoint(touchX, touchY), obj);

  this.HandleTouchHoverStartInternal(event);
        
  this._processMultiTouchTooltip();
  
  this._prevActionClear = false;
}

/**
 * Touch hover move event handler
 * @private
 * @param {DvtComponentTouchEvent} event
 */
DvtEventManager.prototype._preOnComponentTouchHoverMove = function(event) {
  this._propagateEvent(DvtComponentTouchEvent.TOUCH_HOVER_MOVE_TYPE, event, this.OnComponentTouchHoverMove);   
}

/**
 * Touch hover move event handler. Subclasses can override.
 * @param {DvtComponentTouchEvent} event
 * @protected
 */
DvtEventManager.prototype.OnComponentTouchHoverMove = function(event) {
  var targetObj = this.GetCurrentTargetForEvent(event);
  var touch = event.touch;
  var touchX = touch.pageX;
  var touchY = touch.pageY;
  var relPos = this._context.getRelativePosition(touchX, touchY);
  var obj = this.GetLogicalObject(targetObj);
    
  if (this._marqueeHandler) {
    var marqueeEvent = this._marqueeHandler.processDragMove(relPos, event.ctrlKey);
    if (marqueeEvent) {
      event.preventDefault();
      this._callback.call(this._callbackObj, marqueeEvent);  
    }
  }
    
  this.UpdateTouchHoverFeedback(targetObj);

  this.ProcessMagnify(new DvtPoint(touchX, touchY), obj);
 
  this.HandleTouchHoverMoveInternal(event);
    
  var position = new DvtPoint(touch.pageX, touch.pageY);
  // Notify the popup handler of the mouse move
  this.PopupHandler.processMouseMove(position);
}

/**
 * Touch hover end event handler
 * @private
 * @param {DvtComponentTouchEvent} event
 */
DvtEventManager.prototype._preOnComponentTouchHoverEnd = function(event) {
  this._propagateEvent(DvtComponentTouchEvent.TOUCH_HOVER_END_TYPE, event, this.OnComponentTouchHoverEnd);   
}

/**
 * Touch hover end event handler. Subclasses can override.
 * @param {DvtComponentTouchEvent} event
 * @protected
 */
DvtEventManager.prototype.OnComponentTouchHoverEnd = function(event) {
  var targetObj = this.GetCurrentTargetForEvent(event);
  var obj = this.GetLogicalObject(targetObj);

  var touch = event.touch;
  var touchX = touch.pageX;
  var touchY = touch.pageY;
  var relPos = this._context.getRelativePosition(touchX, touchY);
    
  if (this._marqueeHandler) {
    var marqueeEvent = this._marqueeHandler.processDragEnd(relPos, event.ctrlKey);
    if (marqueeEvent) {
      event.preventDefault();
      this._callback.call(this._callbackObj, marqueeEvent);  
    }
  }

  this._hideMagnify();

  this.HandleTouchHoverEndInternal(event);

  if (obj && obj.isSelected && obj.isSelected()) {
    // Don't de-select on touch hover 
  } else {
    this._processTouchSelection(obj, event._isCancelEvent, false);
  }

  this.ProcessTouchContextMenu(event, obj);

  this._processActionPopup(targetObj, new DvtPoint(touchX, touchY));
    
  // DvtComponentUIEvent Support
  this.FireUIEvent(DvtComponentUIEvent.TYPE_ROLL_OUT, obj); 
}

/**
 * Touch hover out event handler
 * @private
 * @param {DvtComponentTouchEvent} event
 */
DvtEventManager.prototype._preOnComponentTouchHoverOut = function(event) {
  this._handlePreOutEvent(DvtComponentTouchEvent.TOUCH_HOVER_OUT_TYPE, event, this.OnComponentTouchHoverOut);
}

/**
 * Touch hover out event handler. Subclasses can override.
 * @param {DvtComponentTouchEvent} event
 * @protected
 */
DvtEventManager.prototype.OnComponentTouchHoverOut = function(event) {
  var targetObj = this.GetCurrentTargetForEvent(event);
  var obj = this.GetLogicalObject(targetObj);

  this.HandleTouchHoverOutInternal(event);

  var selectionHandler = this.getSelectionHandler(obj);
  if(selectionHandler)
    selectionHandler.processMouseOut(obj);
    
  // DvtComponentUIEvent Support
  this.FireUIEvent(DvtComponentUIEvent.TYPE_ROLL_OUT, obj);   
}

/**
 * Touch hover over event handler
 * @private
 * @param {DvtComponentTouchEvent} event
 */
DvtEventManager.prototype._preOnComponentTouchHoverOver = function(event) {
  this._propagateEvent(DvtComponentTouchEvent.TOUCH_HOVER_OVER_TYPE, event, this.OnComponentTouchHoverOver);
  this._handlePreOverEvent(event);
}

/**
 * Touch hover over event handler. Subclasses can override.
 * @param {DvtComponentTouchEvent} event
 * @protected
 */
DvtEventManager.prototype.OnComponentTouchHoverOver = function(event) {
  var targetObj = this.GetCurrentTargetForEvent(event);
  var obj = this.GetLogicalObject(targetObj);

  var touch = event.touch;
  var position = new DvtPoint(touch.pageX, touch.pageY);
  
  this.HandleTouchHoverOverInternal(event);

  var selectionHandler = this.getSelectionHandler(obj);
  if(selectionHandler)
    selectionHandler.processMouseOver(obj);
    
  // DvtComponentUIEvent Support
  this.FireUIEvent(DvtComponentUIEvent.TYPE_ROLL_OVER, obj);    
    
  // Popup Support for triggerType="hover"
  var behaviors = this._getShowPopupBehaviors(obj);
  if(behaviors)
    this.PopupHandler.processMouseOver(obj, behaviors, position);
}

/**
 * Touch double click event handler.
 * @private
 * @param {DvtComponentTouchEvent} event
 */
DvtEventManager.prototype._preOnComponentTouchDblClick = function(event) {
  this._propagateEvent(DvtComponentTouchEvent.TOUCH_DOUBLE_CLICK_TYPE, event, this.OnComponentTouchDblClick); 
}

/**
 * Touch double click event handler. Subclasses must override.
 * @param {DvtComponentTouchEvent} event
 * @protected
 */
DvtEventManager.prototype.OnComponentTouchDblClick = function(event) {
}

/**
 * Touch click event handler
 * @private
 * @param {DvtComponentTouchEvent} event
 */
DvtEventManager.prototype._preOnComponentTouchClick = function(event) {
  this._propagateEvent(DvtComponentTouchEvent.TOUCH_CLICK_TYPE, event, this.OnComponentTouchClick); 
}

/**
 * Touch click event handler. Subclasses can override.
 * @param {DvtComponentTouchEvent} event Component Touch event
 * @protected
 */
DvtEventManager.prototype.OnComponentTouchClick = function(event) {
  var targetObj = this.GetCurrentTargetForEvent(event);
  var touch = event.touch;
  var dlo = this.GetLogicalObject(targetObj);
  var position = new DvtPoint(touch.pageX, touch.pageY);
  // Popup Support for triggerType="click"
  var consumed = false;
  if(this._eventInfo[event]) {
    consumed = this._eventInfo[event]['popupDisplayed'];
  }
  
  if (!consumed) {
    var behaviors = this._getShowPopupBehaviors(dlo);
    if(behaviors) {
      consumed = this.PopupHandler.processClick(dlo, behaviors, position);
    }
  
    if (consumed) {
      this._eventInfo[event]['popupDisplayed'] = true;
    }
  }
  
  if (!consumed) {
    var clientBehaviors = this._getClientBehaviors(dlo);
    if (clientBehaviors) { 
      this.ProcessClientBehaviorForTouch(dlo, clientBehaviors);
    }
  }
  
  // DvtComponentUIEvent Support
  this.FireUIEvent(DvtComponentUIEvent.TYPE_CLICK, dlo);
  
  var done = this.HandleTouchClickInternal(event);
  if (done)
      return;
      
  this._processTouchSelection(dlo, this._popupJustClosed, true);
  
  if (this.CustomTooltipManager.displayActionPopup()) {
      this.UpdateActionTooltipLaunchedFeedback(targetObj, new DvtPoint(touch.pageX, touch.pageY));
      this.CustomTooltipManager.startActionPopupAtPosition(touch.pageX, touch.pageY, targetObj);   
  }
    
  // fire an Action event if logical obj is a command component
  if (dlo && !consumed && dlo.isCommandComponent && dlo.isCommandComponent()) {
    // Create the action event
    var event = new DvtGraphActionEvent(DvtGraphActionEvent.SUBTYPE_ACTION, dlo.getId());

    // add the component specific context
    DvtEventManager._addContextInfo(dlo, event);

    // fire the action event
    this._callback.call(this._callbackObj, event);
  }
     
}

DvtEventManager.prototype._processTouchSelection = function(dlo, preventClear, isTap) {
    var bSelectionChanged = false;
    var selectionHandler = this.getSelectionHandler(dlo);
    if (selectionHandler) {
        if (!dlo || !dlo.isSelectable || !dlo.isSelectable()) {
            if (!preventClear && !this._prevActionClear) {
                // Don't ask for clear selection if popup just closed  
                var showClearDialog = false;
                var allowClear = false;
                // There are two different policies for the clear selection dialog
                // 1) If a tap, show clear selection only for multiple selection when more than 1 is selected
                // 2) If a touch and hold + release, don't attempt a clear or perform a  clear
                if (isTap) {
                    allowClear = true;
                    if (this.isClearMenuAllowed()) {
                        var selType = selectionHandler.getType();
                        if (selType == DvtSelectionHandler.TYPE_MULTIPLE) {
                            if (selectionHandler.getSelectedCount() > 1) {
                                showClearDialog = true;
                            }
                        }
                    }
                }
                if (allowClear) {
                  if (showClearDialog) {
                    this.addClearAllActionItem();
                  } else {
                    bSelectionChanged = selectionHandler.processClick(null, false);
                    // If the selection has changed, fire an event
                    if(bSelectionChanged) 
                      this.fireSelectionEvent(dlo);
                }
              }
            }
        } else {
            // Process click immediately
            bSelectionChanged = selectionHandler.processClick(dlo, true);
            // If the selection has changed, fire an event
            if(bSelectionChanged) 
              this.fireSelectionEvent(dlo);
        }
    }
    this._prevActionClear = false;
}

DvtEventManager.prototype.OnActionTooltipClosed = function(event) {
    var targetObj = event.target;
    var dlo = this.GetLogicalObject(targetObj);
    
    this._popupJustClosed = true;
    this._isInActionPopup = false;     
    var actionPopup =  this.CustomTooltipManager.getActionTooltip();

    if (actionPopup.containsMenuId(DvtEventManager.CLEAR_SELECTION_ACTION_TYPE)) {
        this._prevActionClear = true;
    }
    this.CustomTooltipManager.clearActionTooltip();

    // end start
    this.UpdateActionTooltipClosedFeedback(targetObj);
}

DvtEventManager.prototype.OnActionTooltipStarted = function(event) {
    this._isInActionPopup = true;
}

//*******************************************************************************//
//************************* End Event Listeners *********************************//
//*******************************************************************************//
/*
 * Touch-related actions
 */

DvtEventManager.prototype._processObjectTooltip = function(touchX, touchY, obj, targetObj) {
    this._processShowTooltip(targetObj, obj, touchX, touchY);
}

DvtEventManager.prototype._getTooltipInfo = function(target, obj, x, y) {
    var text = null;
    var isDatatip = false;
    if (obj && obj.getDatatip) {
        text = obj.getDatatip(target, x, y);
        if (text)
            isDatatip = true;
    }

    if(!text && obj && obj.getTooltip) {
       text = obj.getTooltip(target, x, y);

    }
    return {"text" : text, "isDatatip": isDatatip};
}

DvtEventManager.prototype._processShowTooltip = function(target, obj, pageX, pageY) {
  if (this.getTooltipsEnabled()) {
      var relPos = this._context.getRelativePosition(pageX, pageY);
      var x = relPos.x;
      var y = relPos.y;
      
      var tooltipInfo = this._getTooltipInfo(target, obj, x, y);
      var isDatatip = tooltipInfo["isDatatip"];
      var text = tooltipInfo["text"];
      if(text) {
        this.CustomTooltipManager.clearActionTooltip();
        var borderColor = this.GetTooltipColor(obj, x, y);
        if (this._isMagnifyShown) {
            this.CustomTooltipManager.showMagnifyDatatip(pageX, pageY, text, borderColor);
        } else {
            var ttm = this._context.getTooltipManager();
            if (isDatatip) {
                ttm.showDatatip(pageX, pageY, text, borderColor);
            } else {
                ttm.showTooltip(pageX, pageY, text, null, true, borderColor);
            }
        }
        this.TooltipLaunched(text, borderColor);
        return true;
      } else {
        this.hideTooltip();
      }
  } else {
      this.hideTooltip();
  }
  return false;
}

DvtEventManager.prototype.hideTooltip = function() {
    var tooltipManager = this._context.getTooltipManager();
    if(tooltipManager)
      tooltipManager.hideTooltip();
    
    if (this._isMagnifyShown)
        this.CustomTooltipManager.hideTooltip(); 
    
    this.TooltipHidden();
}


DvtEventManager.prototype.ProcessTouchContextMenu = function(event, dlo) {

    if (!this.ContextMenuHandler)
        return;
    var menuType = this.GetContextMenuType(dlo);
    var menuId = this._getContextMenuId(dlo);

    var allowContextMenu = false;
    if (dlo && dlo.isSelected && dlo.isSelected()) {
        allowContextMenu = true;
    } else {
        if (!dlo || !dlo.isSelectable || !dlo.isSelectable()) {
            menuType = DvtContextMenuHandler.TYPE_BODY_CONTEXT_MENU;
        }
        if (menuType == DvtContextMenuHandler.TYPE_BODY_CONTEXT_MENU) {
                allowContextMenu = true;
            }
        }
    if (allowContextMenu) {
        // Call the context menu handler to show the specified menu
        var menuItems = this.ContextMenuHandler.getMenuItems(menuType, menuId);
        if (menuItems) {
          for (var i=0; i<menuItems.length; i++) {
            var menuItem = menuItems[i];
            var actionTooltip = this.CustomTooltipManager.getActionTooltip();
            // Set to default border when there are context menu items launched for multple/body cases and there is no tooltip
            actionTooltip.setTooltipBorderColor(DvtCustomTooltip.DEFAULT_BORDER_COLOR);
            var listener = this.ContextMenuItemListener;
            var ttipItem = new DvtContextMenuTooltipItem(this._context, "item"+i, listener, this, menuItem);
            actionTooltip.addMenuItem(ttipItem);
          }
          return menuItems.length > 0;
        }
    }

    return false;
}

DvtEventManager.prototype._processActionPopup = function(targetObj, position) {
    // Only if action tooltip shown
    if (this.CustomTooltipManager.displayActionPopup()) {    
        this.SetupTouchTooltip(targetObj);     
        this.UpdateActionPopupShownFeedback(targetObj);
        this.CustomTooltipManager.startActionPopupAtPosition(position.x, position.y, targetObj);  
        return true;
    } else {
        this.UpdateActionPopupHiddenFeedback(targetObj);
        return false;
    }
}
/*
 * General  hooks
 */

DvtEventManager.prototype.TooltipLaunched = function(tooltip, borderColor) {
}

DvtEventManager.prototype.TooltipHidden = function() {
}

DvtEventManager.prototype.SetupTouchTooltip = function(targetObj)
{
    var obj = this.GetLogicalObject(targetObj);
    var actionTooltip = this.CustomTooltipManager.getActionTooltip();
    var tooltipBorderColor = actionTooltip.getTooltipBorderColor(); 
    // If border color wasn't overridden, get it here
    if (tooltipBorderColor == null) {
        var borderColor = this.GetTooltipColor(obj);
        actionTooltip.setTooltipBorderColor(borderColor);  
    }
}

/*
 * Mouse-related hooks
 */
DvtEventManager.prototype.OnContextMenuInternal = function(event) {
}

DvtEventManager.prototype.OnClickInternal = function(event) {
}

/*
 * Touch-related hooks
 */
DvtEventManager.prototype.HandleTouchHoverStartInternal = function(event) {
}

DvtEventManager.prototype.HandleTouchHoverMoveInternal = function(event) {
}

DvtEventManager.prototype.HandleTouchHoverEndInternal = function(event) {
} 

DvtEventManager.prototype.HandleTouchHoverOverInternal = function(event) {
}

DvtEventManager.prototype.HandleTouchHoverOutInternal = function(event) {
}

DvtEventManager.prototype.HandleTouchClickInternal = function(event) {
    return false;
}

// Action popup shown for the given target object
DvtEventManager.prototype.UpdateActionPopupShownFeedback = function(targetObj) {
}

// Action popup hidden for the given target object
DvtEventManager.prototype.UpdateActionPopupHiddenFeedback = function(targetObj) {
}

DvtEventManager.prototype.UpdateTouchHoverFeedback = function(targetObj) {
}

DvtEventManager.prototype.UpdateActionTooltipClosedFeedback = function(targetObj) {
}

DvtEventManager.prototype.UpdateActionTooltipLaunchedFeedback = function(targetObj, position) {
}

DvtEventManager.prototype.isInActionPopup = function()
{
    return this._isInActionPopup;
}

DvtEventManager.prototype.GetMagnifyContent = function(pos) {
    return null;
}


DvtEventManager.prototype.ProcessMagnifyStart = function(pos, logicalObj) {
    if (this.IsMagnifyLensEnabled()) {
        var magLens = this.GetMagnifyLens();
        magLens.setDirty(true);
        
        this.ProcessMagnify(pos, logicalObj);
    }
}    

/** @protected */
DvtEventManager.prototype.ProcessMagnify = function(pos, logicalObj) {
    if (this.IsMagnifyLensEnabled()) {
        var magLens = this.GetMagnifyLens();
        var relPos = this._context.getRelativePosition(pos.x, pos.y);
        var region = this.GetMagnifyLensRegion(relPos, logicalObj);
        if (region) {
           if (magLens.isDirty()) {
               var content = this.GetMagnifyContent(pos);
               if (content)
                 magLens.updateMagnifyContent(content);
               magLens.setDirty(false);
           }
            
           magLens.UpdatePosition(relPos.x, relPos.y);        
           this.CustomTooltipManager.showMagnifyLens(pos.x, pos.y, magLens);
           this._isMagnifyShown = true;
        } else {
           this._hideMagnify();
           this._isMagnifyShown = false;
        }
    }
    return false;
}

DvtEventManager.prototype._hideMagnify = function()
{
    this.CustomTooltipManager.hideMagnifyLens(); 
    this.CustomTooltipManager.hideTooltip(); 
    this._isMagnifyShown = false;
}

DvtEventManager.prototype.IsMagnifyLensEnabled = function()
{
    return false;
}

DvtEventManager.prototype.GetMagnifyLensRegion = function(pos, logicalObj)
{
    return null;
}

DvtEventManager.prototype.GetMagnifyLens = function()
{
    return this.CustomTooltipManager.getMagnifyLens();
}

DvtEventManager.prototype.IsTooltipShowable = function(obj, pos) {
    if (!obj)
        return true;
    var tooltipRegion = this.GetTooltipsAllowedRegion(obj);
    var allowTooltips = true;
    if (tooltipRegion && !tooltipRegion.containsPoint(pos.x, pos.y)) {
        allowTooltips = false;
    }
    return allowTooltips && this.IsTooltipShowableInternal && 
        this.IsTooltipShowableInternal(obj, pos);
}

DvtEventManager.prototype.IsTooltipShowableInternal = function(obj, pos) {
    return obj.isTooltipShowable ? obj.isTooltipShowable() : true;
}

DvtEventManager.prototype.GetTooltipsAllowedRegion = function(obj) {
    return null;
}

/*
 * Useful functions
 */

/**
 * Returns a localized "Clear Selection" text.
 * @return {string}
 */
DvtEventManager.prototype.GetClearSelectionText = function() {
  return this._bundle.getTranslatedString('CLEAR_SELECTION', null);
}

// Add a clear selection popup menu item
DvtEventManager.prototype.addClearAllActionItem = function() {
    this.addActionTooltipMenuItem(DvtEventManager.CLEAR_SELECTION_ACTION_TYPE, this.GetClearSelectionText(), this._actionTooltipClearListener, this);
    var actionTooltip = this.CustomTooltipManager.getActionTooltip();
    // Override color to always be gray
    if (actionTooltip)
        actionTooltip.setTooltipBorderColor(DvtCustomTooltip.DEFAULT_BORDER_COLOR);
}

DvtEventManager.prototype.isClearMenuAllowed = function()
{
    return true;
}

// Add a menu item to the action tooltip
DvtEventManager.prototype.addActionTooltipMenuItem = function(id, text, listener, obj)
{
    var actionTooltip = this.CustomTooltipManager.getActionTooltip();
    actionTooltip.addMenuItem(new DvtCustomTooltipItem(this._context, id, text, listener, obj));
}

/*
 * Touch-related listeners
 */
// Performed after tapping a context menu item
DvtEventManager.prototype.ContextMenuItemListener = function(evt) {
    var menuItem = evt.menuItem;
    var contextMenuEvent = menuItem.getEvent();
    this._callback.call(this._callbackObj, contextMenuEvent);
}

// Performed after tapping the clear selection option in an action popup
DvtEventManager.prototype._actionTooltipClearListener = function(evt) {
    var targetObj = evt.target; 
    var dlo = this.GetLogicalObject(targetObj);
    var bSelectionChanged = this.getSelectionHandler(dlo).processClick(null, false);
    // If the selection has changed, fire an event
    if(bSelectionChanged) 
      this.fireSelectionEvent(dlo);
}

// Performed after tapping the select option in an action popup
DvtEventManager.prototype._actionTooltipSelectListener = function(evt) {
    var targetObj = evt.target; 
    var dlo = this.GetLogicalObject(targetObj);
    // Always control click for touch
    var bSelectionChanged = this.getSelectionHandler(dlo).processClick(dlo, true);
    
    // If the selection has changed, fire an event
    if(bSelectionChanged) 
      this.fireSelectionEvent(dlo);
}

DvtEventManager.prototype.setTouchRegionBounds = function(bounds) {
    this.TouchManager.setTouchRegionBounds(bounds);
}

/**
 * Stop event propagation
 */
DvtEventManager.consumeEvent = function(event) {
  if (event){
    event.preventDefault();
    //BUG FIX 13381888: stop event propagation when the event is consumed
    event.stopPropagation();
  }
};


// add the component specific context to this event, such as the clientRowKey.
DvtEventManager._addContextInfo = function(target, event) {
  // diagram
  if (target.getContextCallback && target.getContextCallbackObj) {
    var ccb = target.getContextCallback();
    var ccbo = target.getContextCallbackObj();
    if (ccb && ccbo) {
      ccb.call(ccbo, event);
    }
  }
  // graph
  else {
    // store clientRowKey
    event.addParam(DvtBaseComponentEvent.CLIENT_ROW_KEY, target.getId());
  }
}

DvtEventManager.prototype.IsPropagationEnabled = function(eventType, event, displayable) {
  return false;
}

DvtEventManager.prototype.GetCurrentTargetForEvent = function(event) {
  return this._eventInfo[event] ? this._eventInfo[event]['currentTarget'] : event.target;
}

DvtEventManager.prototype._propagateEvent = function(eventType, event, handler) {
  var obj = this.GetLogicalObject(event.target);
  if (!obj) {
    // No logical objects anywhere in the hierarchy, call handler directly
    handler.call(this, event);
  }
  else {
    var displayable = event.target;
    this._eventInfo[event] = {};
    try {
      while (displayable) {
        var logicalObject = this.GetLogicalObject(displayable, true);
        if (logicalObject) {
          this._eventInfo[event]['currentTarget'] = displayable;
          handler.call(this, event);
          if (!this.IsPropagationEnabled(eventType, event, displayable)) {
            return;
          }
        }
        displayable = displayable.getParent();
      }  
    }
    finally {
      delete this._eventInfo[event];
    }
  }
}

DvtEventManager.prototype.ConsumesClick = function(logicalObject) {
  return (logicalObject.isSelectable && logicalObject.isSelectable()) ||
         (logicalObject.isClickEventPropagationStopped && logicalObject.isClickEventPropagationStopped()) ||
         this._hasClickPopup(logicalObject) || this._hasClickClientBehavior(logicalObject);
}

DvtEventManager.prototype._hasClickPopup = function(logicalObject) {
  var behaviors = this._getShowPopupBehaviors(logicalObject);
  if(behaviors) {
    for(var i=0; i<behaviors.length; i++) {
      var behavior = behaviors[i];
      if (behavior && (behavior.getTriggerType() == DvtGraphShowPopupBehavior.TRIGGER_TYPE_ACTION ||
                       behavior.getTriggerType() == DvtGraphShowPopupBehavior.TRIGGER_TYPE_CLICK)) {
        return true;
      }
    }
  }
  return false;
}

/**
 * Adds a DvtComponentKeyboardHandler to process keyboard events initially 
 * received by this event manager.  Handlers are stored in tab index order and 
 * a keyboard event will be passed from one handler to the next until some 
 * handler (which might be this event manager itself) consumes the event or
 * all handlers after or before the current handler in the list have
 * had a chance to process the keyboard event.
 * 
 * @param {DvtComponentKeyboardHandler} handler 
 */
DvtEventManager.prototype.addComponentKeyboardHandler = function(handler)
{
  if(handler) {
    this._keyboardHandlers.push(handler) ;
  }
}

/**
 * Adds a DvtComponentKeyboardHandler to process keyboard events initially 
 * received by this event manager.  Handlers are stored in tab index order and 
 * a keyboard event will be passed from one handler to the next until some 
 * handler (which might be this event manager itself) consumes the event or
 * all handlers after or before the current handler in the list have
 * had a chance to process the keyboard event.
 * 
 * @param {DvtComponentKeyboardHandler} handler
 * @param {Number} index The tab index at which to add this handler
 *                 
 */
DvtEventManager.prototype.addComponentKeyboardHandlerAt = function(handler, index)
{
  // Return immediately if the current index doesn't exist
  if(index > this._keyboardHandlers.length)
    return;

  if(handler) {
    this._keyboardHandlers.splice(index, 0, handler) ;
    // maintain the pointer to the current keyboard handler
    if(index <= this._currentKeyboardHandlerIdx)
      this._currentKeyboardHandlerIdx++;
  }
}

/**
 * Removes the given DvtComponentKeyboardHandler 
 * 
 * @param {DvtComponentKeyboardHandler} handler
 *                 
 */
DvtEventManager.prototype.removeComponentKeyboardHandler = function(handler)
{
  if(handler) {
    var i = this._findHandler(handler);
    
    if(i > -1)
    {
      // if the handler to be removed is the current one and it's the
      // first handler on the list, then let the reference to the current handler point
      // to the first element in the handler list after the current handler is removed
      if(this._currentKeyboardHandlerIdx == i && i == 0)
        this._currentKeyboardHandlerIdx = 0;

      // maintain the pointer to the current keyboard handler
      else if(i <= this._currentKeyboardHandlerIdx)
          this._currentKeyboardHandlerIdx--;
                  
      this._keyboardHandlers.splice(i, 1) ;
    }
  }
}

/**
  * Returns index of specified DvtComponentKeyboardHandler in the internal list,
  * or -1 if not found. 
  * @param {DvtComponentKeyboardHandler} handler
  * @return {Number}
  * @private
  */
DvtEventManager.prototype._findHandler = function(handler)
{
  var  idx = -1 ;
  var length = this._keyboardHandlers.length;

  for(var i=0; i<length; i++)
  {
    if(this._keyboardHandlers[i] === handler)
      idx = i;
  }

  return idx ;
} 

/**
 * Updates the pointer to the current keyboard handler
 * @param {DvtComponentKeyboardHandler} handler
 * @private
 */
DvtEventManager.prototype._updateKeyboardHandlerIdx = function(handler)
{
  var idx = this._findHandler(handler);
  
  if(idx > 0)
    this._currentKeyboardHandlerIdx = idx;
}

DvtEventManager.prototype._hasClickClientBehavior = function(logicalObject) {
  var behaviors = this._getClientBehaviors(logicalObject);
  if(behaviors) {
    for(var i=0; i<behaviors.length; i++) {
      var behavior = behaviors[i];
      if (behavior && (behavior.getTriggerType() == DvtClientBehavior.TRIGGER_TYPE_ACTION ||
                       behavior.getTriggerType() == DvtClientBehavior.TRIGGER_TYPE_CLICK)) {
        return true;
      }
    }
  }
  return false;
}

DvtEventManager.prototype.CreateClientBehaviorHandler = function(context, callback, callbackObj) {
  return new DvtClientBehaviorHandler(context, callback, callbackObj);
}

/**
 * Processes the client behavior event for touch device
 * 
 * @protected
 * @param {object} obj The target of event.
 * @param {array} clientBehaviors The array of applicable client behaviors.
 * @return {boolean} true if a client behavior was triggered, false otherwise
 */
DvtEventManager.prototype.ProcessClientBehaviorForTouch = function(obj, clientBehaviors) {
  return this.ClientBehaviorHandler.processClick(obj, clientBehaviors);
}

DvtEventManager.prototype.IsDragCandidate = function(obj) {
  // subclasses should override
  return true;
}

/**
 * Generates a random ARIA active element ID
 * @return {String}
 */
DvtEventManager._getActiveElementId = function() {
  var ariaIdPrefix = '_dvtActiveElement';
  return ariaIdPrefix + Math.floor(Math.random() * 1000000000);
}

/**
 * Fires an active element change event in order to add aria-activedescendant attribute to the outer div.
 * This is needed by accessibility client in order to determine active SVG element
 * @param {Object} obj The target object
 */
DvtEventManager.prototype._fireActiveElementChangeEvent = function(obj) {
  // WAI-ARIA is only supported in SVG
  if (!(this.getCtx() instanceof DvtSvgContext))
    return;
  
  // Find the displayable
  var disp;
  if (obj.getDisplayable) {
    disp = obj.getDisplayable();
  } else if (obj.getDisplayables) {
    disp = obj.getDisplayables()[0];
  } else {
    disp = obj;
  }
  
  // If the displayable doesn't own the ARIA tags, find an ancestor that has them.
  while (!disp.getAriaRole() && disp.getParent()) {
    disp = disp.getParent();
  }
  
  // Clear the previous active element
  if (this._prevActiveElement) {
    this._prevActiveElement.setId(null);
    this._prevActiveElement = null;
  }
  
  // Get the ID of the displayable. If it doesn't have an ID, create one for it.
  var id = disp.getId();
  if (!id) {
    var ariaId = DvtEventManager._getActiveElementId();
    disp.setId(ariaId);
    id = ariaId;
    this._prevActiveElement = disp;
  }
  
  // Create and fire the event
  var changeEvent = new DvtActiveElementChangeEvent(DvtActiveElementChangeEvent.CHANGE, id);
  if (this._callback)
    this._callback.call(this._callbackObj, changeEvent);
}


DvtEventManager.prototype._handlePreOverEvent = function(event) {
  var enteredHierarchy = this._getRolloverHierarchy(event.target);
  var exitedHierarchy = this._getRolloverHierarchy(event.relatedTarget);
  if (enteredHierarchy.length > 0) {
    var enteredObject = enteredHierarchy[enteredHierarchy.length - 1];
    var exitedObject = exitedHierarchy.length == 0 ? null : exitedHierarchy[exitedHierarchy.length - 1];
    if (enteredObject != exitedObject) {
      this._eventInfo[event] = {};
      try {
        this._eventInfo[event]['currentTarget'] = enteredObject;
        this.OnRollOver(event);
      }
      finally {
        delete this._eventInfo[event];
      }      
    }
  }
}

DvtEventManager.prototype._handlePreOutEvent = function(eventType, event, handler) {
  var enteredLogicalObject = this.GetLogicalObject(event.relatedTarget);
  var displayable = event.target;
  this._eventInfo[event] = {};
  try {
    // rollout
    var exitedHierarchy = this._getRolloverHierarchy(event.target);
    var enteredHierarchy = this._getRolloverHierarchy(event.relatedTarget);
    if (exitedHierarchy.length > 0) {
      var exitedObject = exitedHierarchy[exitedHierarchy.length - 1];
      var enteredObject = enteredHierarchy.length == 0 ? null : enteredHierarchy[enteredHierarchy.length - 1];
      if (exitedObject != enteredObject) {
        this._eventInfo[event]['currentTarget'] = exitedObject;
        this.OnRollOut(event);        
      }
    }

    var obj = this.GetLogicalObject(displayable);
    if (!obj) {
      // No logical objects anywhere in the hierarchy, call handler directly
      handler.call(this, event);
    }
    else {
      // mouseout
      
      //BUG FIX 15851577: pass the hierarchy of DvtDisplayables for the relatedTarget to the popup
      //handler so that it can determine whether it needs to dismiss a mousehover popup 
      //(NOTE: do this in PreOnMouseOut so that it only happens once, instead of in OnMouseOut,
      //which gets called multiple times in the loop below)
      
      // Popup Support for triggerType="hover"
      this.PopupHandler.processMouseOut(obj, this._getDisplayableHierarchy(event.relatedTarget));
      
      while (displayable) {
        var logicalObject = this.GetLogicalObject(displayable, true);
        if (logicalObject) {
          if (enteredLogicalObject === logicalObject) {
            // Do not mouse out of the new displayable (or its parents)
            break;
          }
          this._eventInfo[event]['currentTarget'] = displayable;
          handler.call(this, event);
          if (!this.IsPropagationEnabled(eventType, event, displayable)) {
            break;
          }
        }
        displayable = displayable.getParent();
      }
    }
  }
  finally {
    delete this._eventInfo[event];
  }
}

/**
 * Gets a touch manager object
 * @return {DvtTouchManager} touch manager object
 */
DvtEventManager.prototype.getTouchManager = function () {
  return this.TouchManager;
}
/**
 * Interactivity manager for custom tooltips and menus.  The two can be visually combined.
 * @class DvtCustomTooltipManager
 * @constructor
 */
var  DvtCustomTooltipManager = function(context, id) {
  this.Init(context, id);
};

DvtObj.createSubclass(DvtCustomTooltipManager, DvtObj, "DvtCustomTooltipManager");

DvtCustomTooltipManager.ACTION_POPUP_CLASS = "OraDVTContextMenuPopup";

DvtCustomTooltipManager.prototype.Init = function(context, id) {
    this._context = context;
    this._id = id;
    this._actionTooltip = new DvtCustomTooltip(this._context, this._id+"ActionPopup");
}

/*
 * For rendering a rich tooltip
 */
DvtCustomTooltipManager.prototype.GetRichTooltipManager = function() {
    if (!this._RichTooltipManager) {
      var tooltipId = this._id+"Tooltip";
      // TODO: may want to change tooltip managers to have their own impl
      this._RichTooltipManager = this._context.getImplFactory().newRichTooltipManager(tooltipId);
      this._RichTooltipManager.InitializeTooltipElem();
      
      // Attach listeners to action tooltip displayable
      var tooltipContext = this._RichTooltipManager.GetStoredContext();
      if (tooltipContext) {
        var eh = new DvtCustomTooltipEventHandler(tooltipContext, this, null, null);
        eh.addListeners(this._actionTooltip);
      }
    }
    return this._RichTooltipManager;
}

/*
 * For rendering a magnify lens
 */
DvtCustomTooltipManager.prototype.GetMagnifyLensManager = function() {
    if (!this._ManifyLensManager) {
        // TODO: may want to change tooltip managers to have their own impl
        this._ManifyLensManager = this._context.getImplFactory().newRichTooltipManager(this._id+"MagLens");
    }
    return this._ManifyLensManager;
}

DvtCustomTooltipManager.prototype.getMagnifyLens = function() {
    if (!this._magnifyLens) {
        this._magnifyLens = new DvtMagnifyLens(this._context);
    }
    return this._magnifyLens;
}

// Clear settings on old action tooltip
DvtCustomTooltipManager.prototype.clearActionTooltip = function() {
    if (this._actionTooltip)
        this._actionTooltip.clearContent();
}

// Retrieve current action tooltip
DvtCustomTooltipManager.prototype.getActionTooltip = function() {
    return this._actionTooltip;
}

// Hide a tooltip that is currently showing
DvtCustomTooltipManager.prototype.hideTooltip = function() {
    this.clearActionTooltip();
    if (this._RichTooltipManager)
      this._RichTooltipManager.hideTooltip();
}

// Closes an action tooltip that was left on screen
DvtCustomTooltipManager.prototype.closeActionTooltip = function() {
    var closedEvent = new DvtActionTooltipEvent(DvtActionTooltipEvent.TOOLTIP_CLOSED_TYPE, this._actionPopupObj);
    this.FireListener(closedEvent);
    this._actionPopupObj = null;
    this.hideTooltip();
}

DvtCustomTooltipManager.prototype.showMagnifyLens = function(x, y, magLens) {
    // optimized for a fixed mag lens size
    var length = magLens.getSize();
    var size = new DvtRectangle(0,0,length,length);
    this.GetMagnifyLensManager().showRichElementAtPosition(x - size.w/2, y - size.h, magLens, false, true);
}

// Hide a tooltip that is currently showing
DvtCustomTooltipManager.prototype.hideMagnifyLens = function() {
    this.GetMagnifyLensManager().hideTooltip();
}

DvtCustomTooltipManager.prototype.showMagnifyDatatip = function(x, y, text, borderColor) {

    var box = this.GetMagnifyLensManager().getTooltipBounds();

    var datatipLeftOffset = 12;
    var datatipTopOffset = 30;
    
    var radius = box.w/2;
    var angle = Math.PI/8;
    var segmentX = radius*Math.cos(angle);
    var segmentY = radius*Math.sin(angle);

    var adjustX = radius - segmentX;
    var adjustY = radius - segmentY;
    
    var actionTooltip = this.getActionTooltip();

    actionTooltip.setIncludePointer(true);
    actionTooltip._pointerLocation = new DvtPoint(-datatipLeftOffset - adjustX, datatipTopOffset + adjustY);
        
    this.showTextAtPosition(box.x + box.w - adjustX, box.y - datatipTopOffset, text, borderColor, false);
//  TODO  Removed the illegal private field access.  If we actually need this (not convinced), will rework it so 
//  that it uses appropriate APIs.
//    if (this.GetRichTooltipManager()._isRightAdjusted) {
//        actionTooltip.setIncludePointer(true);
//        var textBox = this.GetRichTooltipManager().getTooltipBounds();
//        actionTooltip._pointerLocation = new DvtPoint(textBox.w, datatipTopOffset + adjustY);
//        this.showTextAtPosition(box.x - textBox.w + adjustX, box.y - datatipTopOffset, text, borderColor, false);
//    }
    
    actionTooltip.setIncludePointer(false);
    
}

DvtCustomTooltipManager.prototype.showDatatip = function(x, y, text, borderColor) {
    var actionTooltip = this.getActionTooltip();
    text = DvtTextUtils.formatTextString(text);
    actionTooltip.setTooltipText(text);
    if (borderColor != null)
        actionTooltip.setTooltipBorderColor(borderColor);
    this.GetRichTooltipManager().showRichElement(x, y, actionTooltip, true);
}

DvtCustomTooltipManager.prototype.showTextAtPosition = function(x, y, text, borderColor, useOffset, popupClass) {
    var actionTooltip = this.getActionTooltip();
    text = DvtTextUtils.formatTextString(text);
    actionTooltip.setTooltipText(text);
    if (borderColor != null)
        actionTooltip.setTooltipBorderColor(borderColor);
    this.GetRichTooltipManager().showRichElement(x, y, actionTooltip, useOffset);
}

DvtCustomTooltipManager.prototype.displayActionPopup = function() {
    var actionTooltip = this.getActionTooltip();
    // If no action tooltip initialized with actions (not just tooltips), don't show one
    if (!actionTooltip || !actionTooltip.hasMenuItems())
        return false;
    return true;
}

DvtCustomTooltipManager.prototype.startActionPopupAtPosition = function(pageX, pageY, targetObj, alignment) {
    var actionTooltip = this.getActionTooltip();
    if (!actionTooltip)
        return;

    this.GetRichTooltipManager().showRichElementAtPosition(pageX, pageY, actionTooltip, true, false, DvtCustomTooltipManager.ACTION_POPUP_CLASS);
    
    // Clear the text
    var startEvent = new DvtActionTooltipEvent(DvtActionTooltipEvent.TOOLTIP_STARTED_TYPE, targetObj);
    this.FireListener(startEvent);

    this._actionPopupObj = targetObj;

}

/**
 * Adds an event listener.
 **/
DvtCustomTooltipManager.prototype.addTooltipEventListener = function (type, listener, obj) {
  // Store a reference to the listener
  var listenersArray = this._getListeners(type, true);
  listenersArray.push(listener);
  listenersArray.push(obj);
}

/**
 * Removes an event listener.
 **/
DvtCustomTooltipManager.prototype.removeTooltipEventListener = function (type, listener, obj) {
  // Remove the listener
  var listenersArray = this._getListeners(type, false);
  if (listenersArray !== null) {
    for (var i = 0;i < listenersArray.length;i+=2) {
      if (listenersArray[i] === listener && 
           listenersArray[i+1] === obj) {
        listenersArray.splice(i, 2);
        break;
      }
    }
  }
}

/**
 * Returns the listeners of the given event type
 **/
DvtCustomTooltipManager.prototype._getListeners = function (type, createNew) {
  // First find the object where the listener arrays are stored
  if (!this._listenerObj) {
    if (createNew) {
      this._listenerObj = {
      };
    }
    else {
      return null;
    }
  }

  // Then find the array for this event type, creating if necessary 
  var eventKey = type;
  var listenersArray = this._listenerObj[eventKey];
  if (!listenersArray && createNew) {
    listenersArray = [];
    this._listenerObj[eventKey] = listenersArray;
  }

  return listenersArray;
};

/**
 * Notifies all applicable event listeners of the given event.
 **/
DvtCustomTooltipManager.prototype.FireListener = function (event) {
  var listenersArray = this._getListeners(event.getType(), false);
  if (listenersArray) {
    for (var i = 0;i < listenersArray.length;i+=2) {
      var obj = listenersArray[i+1];
      listenersArray[i].call(obj, event);
    }
  }
};
// Custom tooltip event handler
/**
 * @constructor
 */
var  DvtCustomTooltipEventHandler = function(context, customTooltipManager, callback, callbackObj)
{
   this._customTooltipManager = customTooltipManager;
   this.Init(context, callback, callbackObj);

}

DvtObj.createSubclass(DvtCustomTooltipEventHandler, DvtEventManager, "DvtCustomTooltipEventHandler");

DvtCustomTooltipEventHandler.prototype.OnClickInternal = function(event) {
    var target = event.target;
    this._handleMenuClick(target);
}

DvtCustomTooltipEventHandler.prototype.HandleImmediateTouchStartInternal = function(event, touch) {
    event.blockTouchHold();
}

DvtCustomTooltipEventHandler.prototype.HandleTouchClickInternal = function(evt) {
    var target = evt.target;
    this._handleMenuClick(target);
}

DvtCustomTooltipEventHandler.prototype._handleMenuClick = function(targetObj) {
    var actionObj = this._customTooltipManager._actionPopupObj;
    var menuItem = targetObj._menu;
    if (menuItem) {
        var fireAction = true;
        if (menuItem instanceof DvtContextMenuTooltipItem && menuItem._menuItem.isDisabled()) {
            fireAction = false;
        }
        if (fireAction) {
            // Close before menu item event fired since listener may need restored component visual state
            this._customTooltipManager.closeActionTooltip();
            menuItem.FireActionTooltipItem(actionObj);
        }
    }
}
/*--------------------------------------------------------------------*/
/*   DvtCustomTooltip              Action tooltip Component           */
/*--------------------------------------------------------------------*/
/**
  * @constructor
  *  Action tooltip component.
  *  @extends DvtObj
  *  @class DvtCustomTooltip  Creates an action tooltip component.
  */
var   DvtCustomTooltip = function(context, id)
{
    this._Init(context, id);
}  

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

DvtCustomTooltip.DEFAULT_BORDER_COLOR = "gray";
DvtCustomTooltip.DEFAULT_BACKGROUND_COLOR = "#ffffff";

DvtCustomTooltip.TOOLTIP_ONLY = "tooltipOnly";
DvtCustomTooltip.MENU_ONLY = "menuOnly";
DvtCustomTooltip.MENU_AND_TOOLTIP = "menuAndTooltip";
DvtCustomTooltip.EMPTY = "empty";

DvtCustomTooltip.MENU_PADDING_LEFT = 21;
DvtCustomTooltip.MENU_PADDING_RIGHT = 10;

DvtCustomTooltip.prototype._Init = function(context, id)
{

    this._context = context;
    if (! this.getImpl()) {
     this.setImpl(context.getImplFactory().newContainer("customTT")) ;
    }
    DvtCustomTooltip.superclass.Init.call(this, context) ;

    this._tooltipText = null;
    this._tooltipBorderColor = null;
    this._menuFontSize = 11;
    this._tooltipFill = new DvtSolidFill(DvtCustomTooltip.DEFAULT_BACKGROUND_COLOR);    
    this._font = new DvtFont();
    this._font.size = 11;
    this._includePointer = false;//true;
    this._pointerLocation = new DvtPoint(0,0);
}

DvtCustomTooltip.prototype.setIncludePointer = function(includePointer)
{
    this._includePointer = includePointer;
}

DvtCustomTooltip.prototype.getRootDisplayable = function()
{
    return this;
}

DvtCustomTooltip.prototype.UpdateTooltipSize = function(width, height)
{
    this._displayWidth = width;
    this._displayHeight = height;
}

DvtCustomTooltip.prototype.getDisplayWidth = function() {
   return this._displayWidth;
}

DvtCustomTooltip.prototype.getDisplayHeight = function() {
   return this._displayHeight;
}

DvtCustomTooltip.prototype.clearContent = function() {
    this.clearMenuItems();
    this.setTooltipBorderColor(null);  
    this.setTooltipText(null);
}

DvtCustomTooltip.prototype.setMenuFontSize = function(font) {
    this._menuFontSize = font;
}

DvtCustomTooltip.prototype.setTooltipBorderColor = function(color) {
    this._tooltipBorderColor = color;
}

DvtCustomTooltip.prototype.getTooltipBorderColor = function() {
    return this._tooltipBorderColor;
}

DvtCustomTooltip.prototype.setTooltipFill = function(tooltipFill) {
    this._tooltipFill = tooltipFill;
}

DvtCustomTooltip.prototype.setFont = function(font) {
    this._font = font;
}

DvtCustomTooltip.prototype.setTooltipText = function(text) {
    this._tooltipText = text;
}

DvtCustomTooltip.prototype.getTooltipText = function() {
    return this._tooltipText;
}

DvtCustomTooltip.prototype.addMenuItem = function(menuItem) {
    this.getMenuItems().push(menuItem);
}

DvtCustomTooltip.prototype.hasMenuItems = function() {
    var menuItems = this.getMenuItems();
    return menuItems && menuItems.length > 0;
}

DvtCustomTooltip.prototype.hasTooltip = function() {
    return this._tooltipText != null && this._tooltipText.length > 0;
}

DvtCustomTooltip.prototype.clearMenuItems = function() {
    if (this._menuItems) {
        this._menuItems = [];
    }
}

DvtCustomTooltip.prototype.getMenuItems = function() {
    if (!this._menuItems)
        this._menuItems = new Array();
    return this._menuItems;
}

DvtCustomTooltip.prototype.containsMenuId = function(id) {
    var menuItems = this.getMenuItems();
    for (var i=0; i<menuItems.length; i++) {
        var menuItem = menuItems[i];
        if (menuItem.getId() == id) {
            return true;
        }
    }
    return false;
}

DvtCustomTooltip.prototype.getTooltipType = function() {
    var hasMenuItems = this.hasMenuItems();
    var hasTooltip = this.hasTooltip();
    if (hasMenuItems && hasTooltip) {
        return DvtCustomTooltip.MENU_AND_TOOLTIP;
    }
    if (hasMenuItems) {
        return DvtCustomTooltip.MENU_ONLY;
    }
    if (hasTooltip) {
        return DvtCustomTooltip.TOOLTIP_ONLY;
    }
    return DvtCustomTooltip.EMPTY;
}

/**
 * Renders this action tooltip.
 */
DvtCustomTooltip.prototype.Render = function() {

    var parent = this.getRootDisplayable();
    //parent.setPixelHinting(true);
    // First clear old drawables    
    parent.removeChildren();

    var type = this.getTooltipType();
    if (type == DvtCustomTooltip.EMPTY) {
        return;
    }
    
    // Constants
    var padding = 4;
    var menuItemPadding = 5;
    var tooltipBorderWidth = 2;

    if (this._includePointer) {
        tooltipBorderWidth = 3;
        padding = 10;
    }
            
    // Temp variables
    var hasMenuItems = this.hasMenuItems();
    var hasTooltip = this.hasTooltip();
    var topTextY = padding + tooltipBorderWidth/2;
    var runningHeight = 0;
    var maxWidth = 0;
    
    if (type == DvtCustomTooltip.TOOLTIP_ONLY) {
        parent.setAlpha(0.9);
    } else {
        parent.setAlpha(1);
    }

    if (hasTooltip) {
        
        var cursorText = new DvtTextArea(this._context, 0, 0);
        parent.addChild(cursorText);
        DvtTextUtils.applyFont(cursorText, this._font);
        
        cursorText.setMaxWidth(100000000);
        cursorText.setMouseEnabled(true);

        // There is a bug in Firefox and IE9 with aligning to top in SVG.
        // Wait until a feasible fix is in the toolkit for alignTop API.  For now the baseline is used.
        //  cursorText.alignTop();
        cursorText.alignBaseline();
        cursorText.alignStart();
        
        cursorText.setTranslateY(this._font.getSize() + padding+tooltipBorderWidth/2);

        this._cursorText = cursorText;
    
        this._cursorText.removeChildren();
        this._cursorText.setText(this._tooltipText);
    
        var textDimensions = this._cursorText.getDimensions();
        var textHeight = textDimensions.h;
        var textWidth = textDimensions.w;
        maxWidth = Math.max(textWidth, maxWidth);
        runningHeight += textHeight;
    }

    var textItems = new Array();
    var textWidths = new Array();
    
    if (type == DvtCustomTooltip.MENU_AND_TOOLTIP) {
        runningHeight += menuItemPadding;
    } else if (type == DvtCustomTooltip.MENU_ONLY) {
        runningHeight += menuItemPadding/2;        
    }
    
    if (hasMenuItems) {


        var menuItems = this.getMenuItems();
        for (var i=0; i<menuItems.length; i++) {
            var menuItem = menuItems[i];

            var text = new DvtText(this._context, menuItem.getText(), 0, 0, null); 

            DvtTextUtils.applyFont(text, this._font);
            text.setFontSize(this._menuFontSize);
            
            if (menuItem instanceof DvtContextMenuTooltipItem) {
                if (menuItem._menuItem.isDisabled()) {
                    text.setAlpha(0.5);                
                }
            }

            text._menu = menuItem;
            
            var dimensions = text.measureDimensions(parent);
            dimensions.y = topTextY+runningHeight;
            var textWidth = dimensions.w;
            textWidths.push(dimensions);
            maxWidth = Math.max(textWidth + DvtCustomTooltip.MENU_PADDING_LEFT + DvtCustomTooltip.MENU_PADDING_RIGHT, maxWidth);
            text.setY(dimensions.y);
            runningHeight += dimensions.h;
    
            if (i < menuItems.length - 1) {
                runningHeight += menuItemPadding;
            }
            
            textItems.push(text);        
            text.alignStart();
            text.alignTop();
        }

    }
    if (type == DvtCustomTooltip.MENU_ONLY || type == DvtCustomTooltip.MENU_AND_TOOLTIP) {
        runningHeight += menuItemPadding/2;        
    }
    
    var contentWidth = maxWidth + 2*padding;
    var tooltipWidth = contentWidth + tooltipBorderWidth;
    var tooltipHeight = topTextY + runningHeight + padding + tooltipBorderWidth/2; 
    
    if (this._cursorText)
        this._cursorText.setTranslateX(padding + tooltipBorderWidth/2);

    var pointerWidth = 12;
    var isRightPointer = (this._pointerLocation.x > tooltipWidth);
    var pointerOffsetX = -this._pointerLocation.x;
    var pointerOffsetY = this._pointerLocation.y;

    if (this._includePointer) {

        if (tooltipHeight/2 < pointerWidth)
            pointerWidth = tooltipHeight/3;

        var newTooltipHeight = tooltipHeight + tooltipBorderWidth;
        newTooltipHeight = Math.max(pointerOffsetY + tooltipBorderWidth, newTooltipHeight);
        if (isRightPointer) {
            this.UpdateTooltipSize(this._pointerLocation.x, newTooltipHeight);
            parent.setTranslateX(tooltipBorderWidth/2);
        } else {
            this.UpdateTooltipSize(tooltipWidth+pointerOffsetX+tooltipBorderWidth, newTooltipHeight);
            parent.setTranslateX(tooltipBorderWidth/2 + pointerOffsetX);
        }
     //   parent.setTranslateX(pointerOffsetX+tooltipBorderWidth);

        parent.setTranslateY(tooltipBorderWidth/2);
    } else {
        this.UpdateTooltipSize(tooltipWidth + tooltipBorderWidth, tooltipHeight + tooltipBorderWidth);
        parent.setTranslateX(tooltipBorderWidth/2);
        parent.setTranslateY(tooltipBorderWidth/2);
    }

        
    // Add background panels
    var backgroundPanel = new DvtRect(this._context, 0, 0, tooltipWidth, tooltipHeight);
    if (this._includePointer) {

        var radius = 5;
        var beginArrowY = tooltipHeight/2; 

        if (isRightPointer) {
            //this._pointerStartOffset;
            var p1 = new DvtPoint(0, 0);
            var p2 = new DvtPoint(tooltipWidth, 0);
            var p3 = new DvtPoint(tooltipWidth, beginArrowY);
            var p4 = new DvtPoint(this._pointerLocation.x, pointerOffsetY);
            var p5 = new DvtPoint(tooltipWidth, beginArrowY + pointerWidth);
            var p6 = new DvtPoint(tooltipWidth, tooltipHeight);
            var p7 = new DvtPoint(0, tooltipHeight);
    
            var cmd = DvtPathUtils.moveTo(p1.x + radius,p1.y) + 
                DvtPathUtils.lineTo(p2.x - radius,p2.y) + 
                DvtPathUtils.arcTo(radius,radius,Math.PI/2, 1, p2.x, p2.y + radius) + 
                DvtPathUtils.lineTo(p3.x,p3.y) + 
                DvtPathUtils.lineTo(p4.x,p4.y) + 
                DvtPathUtils.lineTo(p5.x,p5.y) + 
                DvtPathUtils.lineTo(p6.x,p6.y - radius) + 
                DvtPathUtils.arcTo(radius,radius,Math.PI/2, 1, p6.x - radius, p6.y) + 
                DvtPathUtils.lineTo(p7.x + radius,p7.y) + 
                DvtPathUtils.arcTo(radius,radius,Math.PI/2, 1, p7.x, p7.y - radius) + 
                DvtPathUtils.lineTo(0,radius) + 
                DvtPathUtils.arcTo(radius,radius,Math.PI/2, 1, radius, 0) + 
                DvtPathUtils.closePath();
             backgroundPanel = new DvtPath(this._context, cmd, null);
        } else {
            //this._pointerStartOffset;
            var p1 = new DvtPoint(0, 0);
            var p2 = new DvtPoint(tooltipWidth, 0);
            var p3 = new DvtPoint(tooltipWidth, tooltipHeight);
            var p4 = new DvtPoint(0, tooltipHeight);
            var p5 = new DvtPoint(0, beginArrowY + pointerWidth);
            var p6 = new DvtPoint(-pointerOffsetX, pointerOffsetY);
            var p7 = new DvtPoint(0, beginArrowY);
    
            var cmd = DvtPathUtils.moveTo(p1.x + radius,p1.y) + 
                DvtPathUtils.lineTo(p2.x - radius,p2.y) + 
                DvtPathUtils.arcTo(radius,radius,Math.PI/2, 1, p2.x, p2.y + radius) + 
                DvtPathUtils.lineTo(p3.x,p3.y - radius) + 
                DvtPathUtils.arcTo(radius,radius,Math.PI/2, 1, p3.x - radius, p3.y) + 
                DvtPathUtils.lineTo(p4.x + radius,p4.y) + 
                DvtPathUtils.arcTo(radius,radius,Math.PI/2, 1, p4.x, p4.y - radius) + 
                DvtPathUtils.lineTo(p5.x,p5.y) + 
                DvtPathUtils.lineTo(p6.x,p6.y) + 
                DvtPathUtils.lineTo(p7.x,p7.y) + 
                DvtPathUtils.lineTo(0,radius) + 
                DvtPathUtils.arcTo(radius,radius,Math.PI/2, 1, radius, 0) + 
                DvtPathUtils.closePath();
             backgroundPanel = new DvtPath(this._context, cmd, null);
        }
    }

    if (hasTooltip) {
        var borderColor = this._tooltipBorderColor;
        if (!borderColor) {
            borderColor = DvtCustomTooltip.DEFAULT_BACKGROUND_COLOR;
        }
        backgroundPanel.setSolidStroke(borderColor, 1, tooltipBorderWidth);
    } else {
        backgroundPanel.setSolidStroke("rgb(229,232,238)");
    }
    
    if (!this._tooltipFill)
        this._tooltipFill = new DvtSolidFill(DvtCustomTooltip.DEFAULT_BACKGROUND_COLOR);
    backgroundPanel.setFill(this._tooltipFill);
    parent.addChildAt(backgroundPanel, 0);

    var dividerWidth = contentWidth*0.95;
    var dividerX = (tooltipWidth - dividerWidth)/2;
    // Add text items
    for (var i=0; i<textItems.length; i++) {
        var textItem = textItems[i];
        parent.addChild(textItem);
        var textWidth = textWidths[i].w;
        if (textItem._menu) {
            // Menus have target areas
            var hitAreaRect = new DvtRectangle(0, textWidths[i].y - menuItemPadding/2, tooltipWidth, textWidths[i].h + menuItemPadding); 
            //hitAreaRect = DvtGeomUtils.getSpecificPaddedRectangle(hitAreaRect, 4, 4, 10, 10);
            var hitTarget = new DvtRect(this._context, hitAreaRect.x, hitAreaRect.y, hitAreaRect.w, hitAreaRect.h);
            hitTarget.setInvisibleFill();

            hitTarget._menu = textItem._menu;
            parent.addChild(hitTarget);

            if (i == 0 && hasTooltip) {
                var fill = new DvtSolidFill("gray", 1);
                // show division between menu and tooltip
                this.renderDivider(parent, dividerX, textWidths[i].y - menuItemPadding, dividerWidth, fill);
            } else if (i > 0) {
                var menuItemObj = textItem._menu._menuItem;
                
                var prevItem = textItems[i-1];

                if (prevItem._menu instanceof DvtContextMenuTooltipItem) {
                    //Separators between context menu items
                    if (menuItemObj.hasSeparatorBefore()) {
                        var fill = new DvtSolidFill("gray", 0.5);
                        this.renderDivider(parent, dividerX, textWidths[i].y - menuItemPadding/2, dividerWidth, fill);
                    }
                } else {
                    // First context menu item, show a division between this and built in actions
                    //var fill = new DvtSolidFill("gray", 1);
                    //this.renderDivider(parent, dividerX, textWidths[i].y - menuItemPadding/2, dividerWidth, fill);
                }
            }
        }
        textItem.setTranslateX(DvtCustomTooltip.MENU_PADDING_LEFT);
    }
}

DvtCustomTooltip.prototype.renderDivider = function(parent, x, y, width, fill) {
    var divider = new DvtRect(this._context, x, y, width, 1);
    divider.setFill(fill);
    parent.addChild(divider);
}

/*--------------------------------------------------------------------*/
/*   DvtCustomTooltipItem              Action tooltip item            */
/*--------------------------------------------------------------------*/
/**
  *  Logical action tooltip item.  No dependency on drawables
  *  @extends DvtObj
  *  @class DvtCustomTooltipItem  Creates an action tooltip item.
  *  @constructor  
  */
var   DvtCustomTooltipItem = function(context, id, text, listener, listenerObj)
{
    this.Init(context, id, text, listener, listenerObj);
}  

DvtObj.createSubclass(DvtCustomTooltipItem, DvtObj, "DvtCustomTooltipItem");

DvtCustomTooltipItem.prototype.Init = function(context, id, text, listener, listenerObj) {
    this._context = context;
    this._id = id;
    this._text = text;
    this._listener = listener;
    this._listenerObj = listenerObj;
}

DvtCustomTooltipItem.prototype.getId = function() {
    return this._id;
}

DvtCustomTooltipItem.prototype.getText = function() {
    return this._text;
}

DvtCustomTooltipItem.prototype.FireActionTooltipItem = function(target) {
    if (this._listenerObj && this._listener) {
        var evt = new Object();
        evt.target = target;
        evt.menuItem = this._menuItem;
        this._menuItemTimer = new DvtTimer(this._context, 1, this._handleMenuTimer, this);
        // store event on timer temporarily
        this._menuItemTimer.evt = evt;
        this._menuItemTimer.start();
    }
}

// Timer handler for menu item listener
DvtCustomTooltipItem.prototype._handleMenuTimer = function() {
    if (this._menuItemTimer) {
        this._menuItemTimer.stop();
    }
    this._listener.call(this._listenerObj, this._menuItemTimer.evt);
}

/**
 * @constructor
 */
var   DvtContextMenuTooltipItem = function(context, id, listener, listenerObj, menuItem)
{
    this.Init(context, id, listener, listenerObj, menuItem);
}  

DvtObj.createSubclass(DvtContextMenuTooltipItem, DvtCustomTooltipItem, "DvtContextMenuTooltipItem");

DvtContextMenuTooltipItem.prototype.Init = function(context, id, listener, listenerObj, menuItem) {
    DvtContextMenuTooltipItem.superclass.Init.call(this, context, id, menuItem.getText(), listener, listenerObj);
    this._menuItem = menuItem;
}
/**
 * Abstract class for Events. 
 * 
 * <p>The supported fields are:
 * <ul>
 * <li>target</li>
 * <li>type</li>
 * </ul>
 * <p>
 */
var DvtBaseEvent = function() {}

DvtObj.createSubclass(DvtBaseEvent, DvtObj, "DvtBaseEvent");

/**
 * Object initializer
 * 
 * @param {String} type
 * @param {String} target
 */
DvtBaseEvent.prototype.Init = function(type, target)
{
  this.type = type;
  this.target = target;
}

/**
 * Returns the native event that we are wrapping
 * @return {Object} The native event that we are wrapping
 */
DvtBaseEvent.prototype.getNativeEvent = function() {
  return null; // subclasses should override
}

/**
 * Prevents the default browser action that the native event would have triggered
 */
DvtBaseEvent.prototype.preventDefault = function() {
  // subclasses should override
} 

/**
 * Stops propagation of the native event in the browser's event bubbling phase.
 */
DvtBaseEvent.prototype.stopPropagation = function() {
  // subclasses should override
} 

/**
 * Checks whether event propagation was stopped
 */
DvtBaseEvent.prototype.isPropagationStopped = function() {
  // subclasses should override
  return false;
} 
/**
 * Base class for component level events.
 * @class The base class for component level events.
 * @constructor
 * @export
 */
var DvtBaseComponentEvent = function() {}

DvtObj.createSubclass(DvtBaseComponentEvent, DvtObj, "DvtBaseComponentEvent");


DvtBaseComponentEvent.CLIENT_ROW_KEY = "clientRowKey";


/**
 * @param {string} type The event type for this event.
 * @protected
 */
DvtBaseComponentEvent.prototype.Init = function(type) {
  this._type = type;
}

/**
 * Returns the event type for this event.
 * @return {string} The event type for this event.
 * @export
 */
DvtBaseComponentEvent.prototype.getType = function() {
  return this._type;
}

/**
 * Return a list of additional parameter keys
 * @return {array} paramKeys additional parameter keys
 */
DvtBaseComponentEvent.prototype.getParamKeys = function() {
  return this._paramKeys;
}

/**
 * Return a list of additional parameter values
 * @return {array} paramValues additional parameter values
 */
DvtBaseComponentEvent.prototype._getParamValues = function() {
  return this._paramValues;
}


/**
 * Add an additional parameter (key, value) to this event (ex clientRowKey)
 * @param {String} paramKey parameter key
 * @param {String} paramValue parameter value
 */
DvtBaseComponentEvent.prototype.addParam = function(paramKey, paramValue) {
  if (! this._paramKeys) {
    this._paramKeys = [];
    this._paramValues = [];
  }

  this._paramKeys.push(paramKey);
  this._paramValues.push(paramValue);
}


/**
 * Get parameter value in this event 
 * @param {String} paramKey parameter key
 * @return {String} paramValue parameter value
 */
DvtBaseComponentEvent.prototype.getParamValue = function(paramKey) {
  if (! paramKey || ! this._paramKeys || ! this._paramValues) {
    return null;
  }

  var index = -1;
  for (var i = 0; i < this._paramKeys.length; i++) {
    if (this._paramKeys[i] == paramKey) {
      index = i;
      break;
    }
  }

  if (index != -1) {
    return this._paramValues[index];
  }  

  return null;
};




/**
 * Platform independent class for Keyboard Events.  
 * 
 * <p>The supported fields are:
 * <ul>
 * <li>altKey</li>
 * <li>ctrlKey</li>
 * <li>shiftKey</li>
 * <li>charCode</li>
 * <li>keyCode</li>
 * </ul>
 * 
 * @constructor
 * @param {String} type
 * @param {boolean} bubbles
 * @param {boolean} cancelable
 * @param {Object} view
 * @param {Number} charCode
 * @param {Number} keyCode
 * @param {Number} keyLocation
 * @param {Boolean} ctrlKey
 * @param {Boolean} altKey
 * @param {Boolean} shiftKey
 * @param {Boolean} repeat
 * @param {String} locale
 */
var DvtKeyboardEvent = function(type, bubbles, cancelable, view, charCode, keyCode, keyLocation, ctrlKey, altKey, shiftKey, repeat, locale)
{
  this.Init(type, bubbles, cancelable, view, charCode, keyCode, keyLocation, ctrlKey, altKey, shiftKey, repeat, locale);
}

DvtObj.createSubclass(DvtKeyboardEvent, DvtBaseEvent, "DvtKeyboardEvent");

// Constants for keyboard event types
//FIX BUG 17075260: indirectly assign the value so the closure compiler will abbreviate references to the constant
DvtKeyboardEvent.KEYDOWN = DvtObj.defineConstant("keydown");
DvtKeyboardEvent.KEYUP = DvtObj.defineConstant("keyup");

// These constants apply for both Flash and Javascript
DvtKeyboardEvent.TAB = 9;
DvtKeyboardEvent.ENTER = 13;
DvtKeyboardEvent.SHIFT = 16;
DvtKeyboardEvent.CONTROL = 17;
DvtKeyboardEvent.ESCAPE = 27;
DvtKeyboardEvent.SPACE = 32;
DvtKeyboardEvent.PAGE_UP = 33;
DvtKeyboardEvent.PAGE_DOWN = 34;
DvtKeyboardEvent.UP_ARROW = 38;
DvtKeyboardEvent.DOWN_ARROW = 40;
DvtKeyboardEvent.DELETE = 46;
DvtKeyboardEvent.LEFT_ARROW = 37;
DvtKeyboardEvent.RIGHT_ARROW = 39;
DvtKeyboardEvent.OPEN_BRACKET = 219;
DvtKeyboardEvent.CLOSE_BRACKET = 221;
DvtKeyboardEvent.BACK_SLASH = 220;
DvtKeyboardEvent.FORWARD_SLASH = 191;
DvtKeyboardEvent.ZERO = 48;
DvtKeyboardEvent.NUMPAD_ZERO = 96;

DvtKeyboardEvent.NUMPAD_PLUS = 107;
DvtKeyboardEvent.NUMPAD_MINUS = 109;
DvtKeyboardEvent.WEBKIT_PLUS = 187;
DvtKeyboardEvent.WEBKIT_MINUS = 189;
DvtKeyboardEvent.GECKO_PLUS = 61;
DvtKeyboardEvent.GECKO_MINUS = 173;

DvtKeyboardEvent.A = 65;
DvtKeyboardEvent.F = 70;
DvtKeyboardEvent.M = 77;
DvtKeyboardEvent.PERIOD = 190;
DvtKeyboardEvent.NUMPAD_PERIOD = 110;
DvtKeyboardEvent.SINGLE_QUOTE = 222;
DvtKeyboardEvent.STAR = 56;
DvtKeyboardEvent.NUMPAD_STAR = 144;
DvtKeyboardEvent.SEMICOLON = 186;
DvtKeyboardEvent.OPEN_ANGLED_BRACKET = 188;
DvtKeyboardEvent.CLOSE_ANGLED_BRACKET = 190;
DvtKeyboardEvent.GRAVE_ACCENT = 192;

/**
 * Object initializer.  This essentially mirrors the DOM initKeyboardEvent() API
 * @param {String} type
 * @param {boolean} bubbles
 * @param {boolean} cancelable
 * @param {Object} view
 * @param {Number} charCode
 * @param {Number} keyCode
 * @param {Number} keyLocation
 * @param {Boolean} ctrlKey
 * @param {Boolean} altKey
 * @param {Boolean} shiftKey
 * @param {Boolean} repeat
 * @param {String} locale
 */
DvtKeyboardEvent.prototype.Init = function(type, bubbles, cancelable, view, charCode, keyCode, keyLocation, ctrlKey, altKey, shiftKey, repeat, locale)
{
  DvtKeyboardEvent.superclass.Init.call(this, type, null);
  this.bubbles = bubbles;
  this.cancelable = cancelable;
  this.view = view;
  this.charCode = charCode;
  this.keyCode = keyCode;
  this.keyLocation = keyLocation;
  this.ctrlKey = ctrlKey;
  this.altKey = altKey;
  this.shiftKey = shiftKey;
  this.repeat = repeat;
  this.locale = locale;
}

/**
 * Utility method that returns true if the keyboard event is a plus keystroke. Handy because keycodes differ
 * across different render kits
 * @param {DvtKeyboardEvent} event
 * @return {Boolean} true if the event is a "+" keystroke
 */
DvtKeyboardEvent.isPlus = function(event)
{
  var keyCode = event.keyCode;
  if (keyCode == DvtKeyboardEvent.NUMPAD_PLUS)
    return true;
  
  if(DvtAgent.isPlatformGecko())
  {
    // special case for Gecko/Firefox
    if(keyCode == DvtKeyboardEvent.GECKO_PLUS && event.shiftKey)
      return true;
    else
      return false;
  }
  else
  {
    if(keyCode == DvtKeyboardEvent.WEBKIT_PLUS && event.shiftKey)
      return true;
    else
      return false;
  }
  return false;
}

/**
 * Utility method that returns true if the keyboard event is a equals keystroke. Handy because keycodes differ
 * across different render kits
 * @param {DvtKeyboardEvent} event
 * @return {Boolean} true if the event is a "+" keystroke
 */
DvtKeyboardEvent.isEquals = function(event)
{
  var keyCode = event.keyCode;
  
  if(DvtAgent.isPlatformGecko())
  {
    // special case for Gecko/Firefox
    if(keyCode == DvtKeyboardEvent.GECKO_PLUS && !event.shiftKey)
      return true;
    else
      return false;
  }
  else
  {
    if(keyCode == DvtKeyboardEvent.WEBKIT_PLUS && !event.shiftKey)
      return true;
    else
      return false;
  }
  return false;
}


/**
 * Utility method that returns true if the keyboard event is a minus keystroke. Handy because keycodes differ
 * across different render kits
 * @param {DvtKeyboardEvent} event
 * @return {Boolean} true if the event is a "-" keystroke
 */
DvtKeyboardEvent.isMinus = function(event)
{
  var keyCode = event.keyCode;
  
  if (keyCode == DvtKeyboardEvent.NUMPAD_MINUS)
    return true;  
  
  if(DvtAgent.isPlatformGecko())
  {
    // special case for Gecko/Firefox
    if(keyCode == DvtKeyboardEvent.GECKO_MINUS && !event.shiftKey)
      return true;
    else
      return false;
  }
  else
  {
    if(keyCode == DvtKeyboardEvent.WEBKIT_MINUS && !event.shiftKey)
      return true;
    else
      return false;
  }
  return false;
}

/**
 * Utility method that returns true if the keyboard event is an underscore keystroke. Handy because keycodes differ
 * across different render kits
 * @param {DvtKeyboardEvent} event
 * @return {Boolean} true if the event is a "_" keystroke
 */
DvtKeyboardEvent.isUnderscore = function(event)
{
  var keyCode = event.keyCode;
  
  if(DvtAgent.isPlatformGecko())
  {
    // special case for Gecko/Firefox
    if(keyCode == DvtKeyboardEvent.GECKO_MINUS && event.shiftKey)
      return true;
    else
      return false;
  }
  else
  {
    if(keyCode == DvtKeyboardEvent.WEBKIT_MINUS && event.shiftKey)
      return true;
    else
      return false;
  }
  return false;
}
/**
 * @constructor
 * Abstract class for Mouse Events.  This class roughly follows the DOM Level 2 API.  
 * 
 * <p>The supported fields are:
 * <ul>
 * <li>ctrlKey</li>
 * <li>relatedTarget</li>
 * <li>target</li>
 * <li>type</li>
 * </ul>
 * <p>
 */
var DvtMouseEvent = function(type, target, relatedTarget, button, ctrlKey, metaKey, shiftKey, pageX, pageY) 
{
  this.Init(type, target, relatedTarget, button, ctrlKey, metaKey, shiftKey, pageX, pageY);
}

DvtObj.createSubclass(DvtMouseEvent, DvtBaseEvent, "DvtMouseEvent");

// Constants for mouse event types
//FIX BUG 17075260: indirectly assign the value so the closure compiler will abbreviate references to the constant
DvtMouseEvent.CLICK = DvtObj.defineConstant("click");
DvtMouseEvent.DBLCLICK = DvtObj.defineConstant("dblclick");
DvtMouseEvent.MOUSEOVER = DvtObj.defineConstant("mouseover");
DvtMouseEvent.MOUSEOUT = DvtObj.defineConstant("mouseout");
DvtMouseEvent.MOUSEDOWN = DvtObj.defineConstant("mousedown");
DvtMouseEvent.MOUSEUP = DvtObj.defineConstant("mouseup");
DvtMouseEvent.MOUSEMOVE = DvtObj.defineConstant("mousemove");
DvtMouseEvent.MOUSEWHEEL = DvtObj.defineConstant("mousewheel");

DvtMouseEvent.RIGHT_CLICK_BUTTON = 2;

/**
 * Object initializer. This essentially mirrors the DOM initMouseEvent() API
 * @param {String} type
 * @param {Boolean} bubbles
 * @param {Boolean} cancelable
 * @param {Object} view
 * @param {Number} detail
 * @param {Number} pageX
 * @param {Number} pageY
 * @param {Number} clientX
 * @param {Number} clientY
 * @param {Boolean} ctrlKey
 * @param {Boolean} altKey
 * @param {Boolean} shiftKey
 * @param {Boolean} metaKey
 * @param {Number} button
 * @param {Object} relatedTarget
 */
DvtMouseEvent.prototype.Init = function(type, bubbles, cancelable, view, detail, pageX, pageY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget)
{
  DvtMouseEvent.superclass.Init.call(this, type, null);
  this.bubbles = bubbles;
  this.cancelable = cancelable;
  this.view = view;
  this.detail = detail;
  this.pageX = pageX;
  this.pageY = pageY;
  this.clientX = clientX;
  this.clientY = clientY;
  this.ctrlKey = ctrlKey || metaKey;
  this.altKey = altKey;
  this.shiftKey = shiftKey;
  this.metaKey = metaKey;
  this.button = button;
  this.relatedTarget = relatedTarget;
}

/**
 * Abstract class for Touch Events.
 * 
 * <p>The supported fields are:
 * <ul>
 * <li>touches</li>
 * <li>targetTouches</li>
 * <li>changedTouches</li>
 * <li>target</li>
 * <li>type</li>
 * </ul>
 * <p>
 */
var DvtTouchEvent = function() {}

DvtObj.createSubclass(DvtTouchEvent, DvtBaseEvent, "DvtTouchEvent");

// Constants for touch event types
//FIX BUG 17075260: indirectly assign the value so the closure compiler will abbreviate references to the constant
DvtTouchEvent.TOUCHSTART = DvtObj.defineConstant("touchstart");
DvtTouchEvent.TOUCHMOVE = DvtObj.defineConstant("touchmove");
DvtTouchEvent.TOUCHEND = DvtObj.defineConstant("touchend");
DvtTouchEvent.TOUCHCANCEL = DvtObj.defineConstant("touchcancel");

DvtTouchEvent.prototype.Init = function()
{
}
/**
 * @constructor
 * Wrapper class providing access to a Touch.
 * @extends DvtObj
 * @class DvtTouch
 * <p>The supported fields are:
 * <ul>
 * <li>clientX</li>
 * <li>clientY</li>
 * <li>screenX</li>
 * <li>screenY</li>
 * <li>pageX</li>
 * <li>pageY</li>
 * <li>target</li>
 * <li>identifier</li>
 * </ul>
 * <p>
 */
var DvtTouch = function(touch) {
  this.Init(touch);
}

DvtObj.createSubclass(DvtTouch, DvtObj, "DvtTouch");

/**
 * @protected
 * @param {Touch} the DOM Touch
 */
DvtTouch.prototype.Init = function(touch) {
  this.clientX = touch.clientX;
  this.clientY = touch.clientY;
  this.screenX = touch.screenX;
  this.screenY = touch.screenY;
  this.pageX = touch.pageX;
  this.pageY = touch.pageY;
  this.target = touch.target;
  this.identifier = (touch.identifier == null || isNaN(touch.identifier)) ? 1 : touch.identifier;
}
/**
 * @constructor
 * Keeps track of the current state of touches and fires higher-level logical events
 * @extends DvtObj
 * @class DvtTouchManager
 */
var DvtTouchManager = function(id, context) {
  this.Init(id, context);
}

DvtObj.createSubclass(DvtTouchManager, DvtObj, "DvtTouchManager");

DvtTouchManager.TOUCH_MODE = "mode";
DvtTouchManager.TOUCH_MODE_DEFAULT = "defaultMode";
DvtTouchManager.TOUCH_MODE_LONG_PRESS = "longPressMode";

DvtTouchManager.PREV_HOVER_OBJ = "prevHoverObj";

DvtTouchManager.HOVER_TOUCH_KEY = "hoverTouch";

/**
 * @protected
 * Helper method called by the constructor to initialize this object.
 * @param {string} id Id for the this TouchManager instance
 * @param {DvtContext} context An application specific context
 */
DvtTouchManager.prototype.Init = function(id, context) {
  this._context = context;
  this._id = id;
  
  // Total number of touches on the screen
  this._touchCount = 0;

  // Single timer for touch hold 
  this._touchHoldTimer = new DvtTimer(this._context, 200, this._handleTouchHoldStartTimer, this);
  this._blockTouchHold = false;
  
  // Stored mapping for history information on touches
  this._touchMap = new Object();

  // Limit the region for which this touch manager applies.
  this._touchRegionBounds = null;  

  // Save information on touches which are already associated with listeners
  this._savedTouchInfo = new Array();

  // internal listener to translate to hover
  this._addTouchHoldMoveEventListener(this._onTouchHoldHover, this);

  // Single timer for double tap
  this._doubleTapTimer = new DvtTimer(this._context, 300, this._handleDoubleTapTimer, this, 1);

}

/**
 * Block touch hold event. We might want to prevent touch hold during zoom oparation.
 */
DvtTouchManager.prototype.blockTouchHold = function () {
    this._touchHoldTimer.stop();
    this._blockTouchHold = true;
}

/**
 * Releases touch hold block.
 */
DvtTouchManager.prototype.unblockTouchHold = function () {
    this._blockTouchHold = false;
}

DvtTouchManager.prototype._addTouchHoldMoveEventListener = function (listener, obj) {
    this.addTouchEventListener(DvtComponentTouchEvent.TOUCH_HOLD_START_TYPE, listener, obj);
    this.addTouchEventListener(DvtComponentTouchEvent.TOUCH_HOLD_MOVE_TYPE, listener, obj);
    this.addTouchEventListener(DvtComponentTouchEvent.TOUCH_HOLD_END_TYPE, listener, obj);
}

/**
 * Internal touch hold listener to translate to hover events
 * @private
 * @param {DvtComponentTouchEvent} evt A touch hold event.
 */
DvtTouchManager.prototype._onTouchHoldHover = function(evt) {
    var type = evt.getType();
    var touch = evt.touch;
    
    var info = this._touchMap[touch.identifier];
    
    var obj = evt.target;
    if (type == DvtComponentTouchEvent.TOUCH_HOLD_END_TYPE) {
        var hoverEvt = new DvtComponentTouchEvent(DvtComponentTouchEvent.TOUCH_HOVER_END_TYPE, touch, obj);
        hoverEvt._isCancelEvent = this._isCancelEvent;
        
        if (obj != null)
            this.FireListener(new DvtComponentTouchEvent(DvtComponentTouchEvent.TOUCH_HOVER_OUT_TYPE, touch, obj));
        info[DvtTouchManager.PREV_HOVER_OBJ] = null;
            
        this.FireListener(hoverEvt);
    } else if (type == DvtComponentTouchEvent.TOUCH_HOLD_START_TYPE) {
        info[DvtTouchManager.PREV_HOVER_OBJ] = null;
        if (obj != null)
            this.FireListener(new DvtComponentTouchEvent(DvtComponentTouchEvent.TOUCH_HOVER_OVER_TYPE, touch, obj));
        info[DvtTouchManager.PREV_HOVER_OBJ] = obj;

        this.FireListener(new DvtComponentTouchEvent(DvtComponentTouchEvent.TOUCH_HOVER_START_TYPE, touch, obj));
    } else if (type == DvtComponentTouchEvent.TOUCH_HOLD_MOVE_TYPE) {
        this._fireHoverOverOutEvents(obj, info[DvtTouchManager.PREV_HOVER_OBJ], touch);
        info[DvtTouchManager.PREV_HOVER_OBJ] = obj;
        this.FireListener(new DvtComponentTouchEvent(DvtComponentTouchEvent.TOUCH_HOVER_MOVE_TYPE, touch, obj));

    }
}

DvtTouchManager.prototype._getObjFromTouch = function(touch) {
    var obj = null;

    if (touch) {
        var relPos = this._context.getRelativePosition(touch.pageX, touch.pageY);
        if (!this._touchRegionBounds || this._touchRegionBounds.containsPoint(relPos.x, relPos.y)) {
        // If the rendering impl doesn't define document utils for finding the element at a point, don't continue
            var documentUtils = this._context.getDocumentUtils();
            if (documentUtils) {
                obj = documentUtils.elementFromTouch(touch);
            }
        }    
    }
    return obj;
}

// Fire logical hover events
DvtTouchManager.prototype._fireHoverOverOutEvents = function(currentObj, prevHoverObj, touch) {
    if (prevHoverObj != currentObj) {
        if (prevHoverObj != null)
            this.FireListener(new DvtComponentTouchEvent(DvtComponentTouchEvent.TOUCH_HOVER_OUT_TYPE, touch, prevHoverObj, currentObj));
        if (currentObj != null)
            this.FireListener(new DvtComponentTouchEvent(DvtComponentTouchEvent.TOUCH_HOVER_OVER_TYPE, touch, currentObj, prevHoverObj));
    }
}

/**
 * Adds an event listener.
 * @param {string} type One of the DvtComponentTouchEvent types
 * @param {function} listener Listener to add
 * @param {object} obj Optional object on which the listener function is defined
 */
DvtTouchManager.prototype.addTouchEventListener = function (type, listener, obj) {
  // Store a reference to the listener
  var listenersArray = this._getListeners(type, true);
  listenersArray.push(listener);
  listenersArray.push(obj);
}

/**
 * Removes an event listener.
 * @param {string} type One of the DvtComponentTouchEvent types
 * @param {function} listener Listener to remove
 * @param {object} obj Optional object on which the listener function is defined
 */
DvtTouchManager.prototype.removeTouchEventListener = function (type, listener, obj) {
  // Remove the listener
  var listenersArray = this._getListeners(type, false);
  if (listenersArray) {
    for (var i = 0;i < listenersArray.length;i+=2) {
      if (listenersArray[i] === listener && 
           listenersArray[i+1] === obj) {
        listenersArray.splice(i, 2);
        break;
      }
    }
  }
}

/**
 * Returns the listeners of the given event type and capture mode.
 **/
DvtTouchManager.prototype._getListeners = function (type, createNew) {
  // First find the object where the listener arrays are stored
  if (!this._listenerObj) {
    if (createNew) {
      this._listenerObj = {
      };
    }
    else {
      return null;
    }
  }

  // Then find the array for this event type, creating if necessary 
  var eventKey = type;
  var listenersArray = this._listenerObj[eventKey];
  if (!listenersArray && createNew) {
    listenersArray = [];
    this._listenerObj[eventKey] = listenersArray;
  }

  return listenersArray;
};

/**
 * Notifies all applicable event listeners of the given event.
 **/
DvtTouchManager.prototype.FireListener = function (event) {
  var listenersArray = this._getListeners(event.getType(), false);
  if (listenersArray) {
    for (var i = 0;i < listenersArray.length;i+=2) {
      var obj = listenersArray[i+1];
      listenersArray[i].call(obj, event);
    }
  }
};

DvtTouchManager.prototype.startTouchHold = function() {
    var touchid = this._startSingleFingerTouchId;
    if (touchid != null && !isNaN(touchid)) {
        var info = this._touchMap[touchid];
        if (info) {

            var touchStartObj = info["startTarget"];
            var startTouch = info["startTouch"];

            // If there are any immediate touches for this id, end it since touch hold has started            
            var matches = this._findMatches("touchId", touchid);
            for (var i=0; i<matches.length;i++) {
                var savedInfo = matches[i];
                var touchObj = savedInfo["touchObj"];
                var touchId = savedInfo["touchId"];
                if (touchObj != DvtTouchManager.HOVER_TOUCH_KEY) {
                    var touchInfo = this.getTouchInfo(touchId);
                    var touch = touchInfo["startTouch"];
                    this.performAssociatedTouchEnd(touch, touchObj, null);
                }
            }

            // Save mode on touch
            info[DvtTouchManager.TOUCH_MODE] = DvtTouchManager.TOUCH_MODE_LONG_PRESS;
            this.saveProcessedTouch(touchid, DvtTouchManager.HOVER_TOUCH_KEY, null, DvtTouchManager.HOVER_TOUCH_KEY, DvtTouchManager.HOVER_TOUCH_KEY, this.HoverMoveInternal, this.HoverEndInternal, this);
            
            var touchHoldStartEvent = new DvtComponentTouchEvent(DvtComponentTouchEvent.TOUCH_HOLD_START_TYPE, startTouch, touchStartObj);
            this.FireListener(touchHoldStartEvent);
            
        }
    }
}

// Execute listeners for logical events.
// Note that there is no way to bubble logical events in the framework at the moment
DvtTouchManager.prototype.fireLogicalEvents = function(touchEvent) {
    var type = touchEvent.type;
    if (type == DvtTouchEvent.TOUCHSTART) {
      if (!touchEvent.isTouchHoldBlocked()) {
        this.processAssociatedTouchAttempt(touchEvent, DvtTouchManager.HOVER_TOUCH_KEY, this.HoverStartInternal, this);
      }
      if (this._doubleTapAttemptStarted) {
          touchEvent.preventDefault();
      }
      else {
      //this is in here to prevent the magnifying glass from coming up in chrome on android, a la bug 14033184. Disabling default event only on legend text and markers so that the mag glass doesn't show up.
        var info = this.getTouchInfo(touchEvent.changedTouches[0].identifier);
        var targetObj = info.currentObj;
        if (DvtAgent.isBrowserChrome() && (targetObj instanceof DvtMarker || targetObj instanceof DvtText)){
          touchEvent.preventDefault();
        }
      }
    } else if (type == DvtTouchEvent.TOUCHMOVE) {
        this.processAssociatedTouchMove(touchEvent, DvtTouchManager.HOVER_TOUCH_KEY);
    } else if (type == DvtTouchEvent.TOUCHEND) {
        this.processAssociatedTouchEnd(touchEvent, DvtTouchManager.HOVER_TOUCH_KEY);
    }

    if (type == DvtTouchEvent.TOUCHEND) {
        var changedTouches = this._getStoredTouches(touchEvent.changedTouches);
        for (var i=0; i<changedTouches.length; i++) {
            var touch = changedTouches[i];
            var identifier = touch.identifier;
            var info = this.getTouchInfo(identifier);
    
            var targetObj = info.currentObj;
            
            if (info[DvtTouchManager.TOUCH_MODE] != DvtTouchManager.TOUCH_MODE_LONG_PRESS) {
                if (info.fireClick) {
                    var touchClickEvt = new DvtComponentTouchEvent(DvtComponentTouchEvent.TOUCH_CLICK_TYPE, touch, targetObj);
                    touchClickEvt.touchEvent = touchEvent;
                    this.FireListener(touchClickEvt);

                    if (this._doubleTapAttemptStarted) {
                        var prevTapObj = this._doubleTapAttemptObj;
                        this.resetDoubleTap();
                        if (targetObj == prevTapObj) {
                            var touchDblClickEvt = new DvtComponentTouchEvent(DvtComponentTouchEvent.TOUCH_DOUBLE_CLICK_TYPE, touch, targetObj);
                            touchDblClickEvt.touchEvent = touchEvent;
                            this.FireListener(touchDblClickEvt);
                        }
                    } else {
                        this.resetDoubleTap();
                        this._doubleTapTimer.start();
                        this._doubleTapAttemptStarted = true;
                        this._doubleTapAttemptObj = targetObj;
                    }
                }
            }
        }
    }
}

DvtTouchManager.prototype.getTouchInfo = function(touchId) {
    return this._touchMap[touchId];
}

/**
 * Handler for the touch event. Starts timer for the potential hover event.
 * @param {DvtTouchEvent} event Wrapper for the native event
 * @param {DvtTouch} touch Touch object for the event
 * @protected
 */
DvtTouchManager.prototype.HoverStartInternal = function(event, touch) {
    if (this._blockTouchHold)
        return;
    var identifier = touch.identifier;
    this._startSingleFingerTouchId = identifier;
    this._touchHoldTimer.stop();
    this._touchHoldTimer.start();
}

DvtTouchManager.prototype.HoverMoveInternal = function(event, touch) {
    var identifier = touch.identifier;
    var info = this.getTouchInfo(identifier);
    var targetObj = info.currentObj;
    var touchHoldMoveEvent = new DvtComponentTouchEvent(DvtComponentTouchEvent.TOUCH_HOLD_MOVE_TYPE, touch, targetObj);
    this.FireListener(touchHoldMoveEvent);
    event.preventDefault();
}

DvtTouchManager.prototype.HoverEndInternal = function(event, touch) {
    var identifier = touch.identifier;
    var info = this.getTouchInfo(identifier);
    var targetObj = info.currentObj;
    var touchHoldEndEvent = new DvtComponentTouchEvent(DvtComponentTouchEvent.TOUCH_HOLD_END_TYPE, touch, targetObj);
    this.FireListener(touchHoldEndEvent);
    if (event)
        event.preventDefault();
}

DvtTouchManager.prototype.resetTouchHold = function() {
  this._startSingleFingerTouchId = null;
  if (this._touchHoldTimer)
      this._touchHoldTimer.stop();
}

/**
 * Updates state information for current touches before logical event "bubbles"
 * @param {DvtTouchEvent} touchEvent Touch event
 */
DvtTouchManager.prototype.preEventBubble = function(touchEvent) {
  if (touchEvent.setTouchManager)
    touchEvent.setTouchManager(this);

  var type = touchEvent.type;
  if (type == DvtTouchEvent.TOUCHSTART) {
    this.processTouchStart(touchEvent); 
  } else if (type == DvtTouchEvent.TOUCHMOVE) {
    this.processTouchMove(touchEvent); 
  } else if (type == DvtTouchEvent.TOUCHEND) {
    this.processTouchEnd(touchEvent);    
  }
}

// Updates state information after logical event is done "bubbling"
DvtTouchManager.prototype.postEventBubble = function(touchEvent) {
     var type = touchEvent.type;

     if (type == DvtTouchEvent.TOUCHSTART) {
     } else if (type == DvtTouchEvent.TOUCHMOVE) {
     } else if (type == DvtTouchEvent.TOUCHEND) {
        var changedTouches = this._getStoredTouches(touchEvent.changedTouches);
        for (var i=0; i<changedTouches.length; i++)
        {
            var changedTouch = changedTouches[i];
            var identifier = changedTouch.identifier;

            delete this._touchMap[identifier];
            if (this._startSingleFingerTouchId == identifier) {
                this._startSingleFingerTouchId = null;
            }
            // Stop propagation may not allow touch manager to clear saved touch info.  Need to check for any remaining
            // info and remove
            var savedMatches = this._findMatches("touchId", identifier);
            for (var j=0;j<savedMatches.length;j++) {
                var match = savedMatches[j];
                this.removeTooltipInfo(match["touchId"], match["touchObj"]);
            }
        }
    }
}

DvtTouchManager.prototype.saveProcessedTouch = function(touchId, touchObj, tooltipObjOverride, grouping, type, moveListener, endListener, listenerObj) {
  if (touchId != null && !isNaN(touchId)) {
    var info = DvtTouchManager.createSavedTouchInfo(touchId, touchObj, tooltipObjOverride, grouping, type, moveListener, endListener, listenerObj);
    this._savedTouchInfo.push(info);
    return true;
  }
  return false;
}

DvtTouchManager.prototype.saveProcessedTouchDefault = function(touchId, touchObj, tooltipObjOverride, grouping, type, listenerObj) {
    var movedListener = null;
    if (listenerObj.immediateTouchMoved)
       movedListener = listenerObj.immediateTouchMoved;
    var endListener = null;
    if (listenerObj.immediateTouchEnded)
       endListener = listenerObj.immediateTouchEnded;

    this.saveProcessedTouch(touchId, touchObj, tooltipObjOverride, grouping, type, movedListener, endListener, listenerObj);
}

DvtTouchManager.prototype.processAssociatedTouch = function(event, uniqueKey, startListener, listenerObj) {
    var type = event.type;
    if (type == DvtTouchEvent.TOUCHSTART) {
        this.processAssociatedTouchAttempt(event, uniqueKey, startListener, listenerObj);
    } else if (type == DvtTouchEvent.TOUCHMOVE) {
        this.processAssociatedTouchMove(event, uniqueKey);
    } else if (type == DvtTouchEvent.TOUCHEND) {
        this.processAssociatedTouchEnd(event, uniqueKey);
    }
}

// immediateTouchObj implements a defined interface for handling single touches on the object
DvtTouchManager.prototype.processAssociatedTouchDefault = function(event, immediateTouchObj) {
    this.processAssociatedTouch(event, immediateTouchObj, immediateTouchObj.immediateTouchAttempted, immediateTouchObj);
}

DvtTouchManager.prototype.cancelTouchHold = function() {
    var uniqueKey = DvtTouchManager.HOVER_TOUCH_KEY;
    var touchIds = this.getTouchIdsForObj(uniqueKey);
    for (var i=0; i<touchIds.length; i++) {
        var touchId = touchIds[i];
        var info = this.getTouchInfo(touchId);
        var touch = info["startTouch"];
        if (touch) {
            this._isCancelEvent = true;
            this.performAssociatedTouchEnd(touch, uniqueKey, null);
            this._isCancelEvent = false;
            delete this._touchMap[touchId];
        }
    }

}

DvtTouchManager.prototype.performAssociatedTouchEnd = function(touch, uniqueKey, event) {
    var identifier = touch.identifier;
    var savedInfo = this.getSavedTouchInfo(identifier, uniqueKey);
    if (savedInfo) {
        var listenerObj = savedInfo["listenerObj"];
        var endListener = savedInfo["endListener"]; 

        // Remove tooltip info first
        this.removeTooltipInfo(identifier, uniqueKey);
        if (endListener) {
            endListener.call(listenerObj, event, touch, savedInfo);
        }
    }
}

DvtTouchManager.prototype.processAssociatedTouchAttempt = function(event, uniqueKey, startListener, listenerObj) {
    var touches = event.changedTouches;
    for (var i=0; i<touches.length; i++) {
        var touch = touches[i];
        if (touch) {
            if (startListener)
                startListener.call(listenerObj, event, touch);
        }
    }
}

DvtTouchManager.prototype.processAssociatedTouchMove = function(event, uniqueKey) {
    var touchIds = this.getTouchIdsForObj(uniqueKey);
    for (var i=0; i<touchIds.length; i++) {
        var touchId = touchIds[i];
        if (touchId != null && !isNaN(touchId)) {
            var touch = DvtTouchManager.getTouchById(touchId, this._getStoredTouches(event.changedTouches));
            if (touch) {
                var savedInfo = this.getSavedTouchInfo(touch.identifier, uniqueKey);
                if (savedInfo) {
                    var listenerObj = savedInfo["listenerObj"];
                    var moveListener = savedInfo["moveListener"];
    
                    if (moveListener)
                        moveListener.call(listenerObj, event, touch);
                        
                }
            }
        }
    }
}

DvtTouchManager.prototype.processAssociatedTouchEnd = function(event, uniqueKey) {
    var touchIds = this.getTouchIdsForObj(uniqueKey);
    for (var i=0; i<touchIds.length; i++) {
        var touchId = touchIds[i];
        var touch = DvtTouchManager.getTouchById(touchId, this._getStoredTouches(event.changedTouches));
        if (touch) {
            this.performAssociatedTouchEnd(touch, uniqueKey, event);
        }
    }
}

DvtTouchManager.prototype._findMatch = function(matchProp, matchValue) {
    var idx = -1;
    for (var i=0; i<this._savedTouchInfo.length; i++) {
        var info = this._savedTouchInfo[i];
        if (info[matchProp] == matchValue) {
            idx = i;
        }
    }
    return idx;
}
DvtTouchManager.prototype._findMatches = function(matchProp, matchValue) {
    var results = new Array();
    for (var i=0; i<this._savedTouchInfo.length; i++) {
        var info = this._savedTouchInfo[i];
        if (info[matchProp] == matchValue) {
            results.push(info);
        }
    }
    return results;
}



DvtTouchManager.prototype.getSavedTouchInfo = function(touchId, uniqueKey) {
    var matches = this._findMatches("touchId", touchId);
    for (var i=0; i<matches.length;i++) {
        var info = matches[i];
        if (info["touchObj"] == uniqueKey) {
            return info;
        }
    }
    return null;
}
DvtTouchManager.prototype.containsTouchId = function(touchId) {
    var matches = this._findMatches("touchId", touchId);
    return matches.length > 0;
}

DvtTouchManager.prototype.getTouchIdsForObj = function(touchObj) {
    var results = new Array();
    var matches = this._findMatches("touchObj", touchObj);
    for (var i=0; i<matches.length;i++) {
        var info = matches[i];
        var touchId = info["touchId"];
        results.push(touchId);
    }
    return results;
}

DvtTouchManager.prototype.removeTooltipInfo = function(touchId, uniqueKey) {
    var matches = this._findMatches("touchId", touchId);
    var removeObjects = new Array();
    for (var i=0; i<matches.length;i++) {
        var info = matches[i];
        if (info["touchObj"] == uniqueKey) {
            removeObjects.push(info);
        }
    }
    for (var i=0; i<removeObjects.length;i++) {
        var removeObj = removeObjects[i];
        var removeIdx = -1;
        for (var j=0; j<this._savedTouchInfo.length; j++) {
            var info = this._savedTouchInfo[j];
            if (info == removeObj) {
                removeIdx = j;
            }
        }
        if (removeIdx != -1 ){
            this._savedTouchInfo.splice(removeIdx, 1);
            if (this._savedTouchInfo.length == 0) {
                this._context.getTooltipManager().hideTooltip();
            }
        }
    }
}

DvtTouchManager.prototype.setTooltipEnabled = function(touchId, enabled) {
    var tooltipInfoArray = this._savedTouchInfo;
    for (var i=0; i<tooltipInfoArray.length; i++) {
        var tooltipInfo = tooltipInfoArray[i];
        var tooltipTouchId = tooltipInfo.touchId;
        if (tooltipTouchId == touchId) {
            tooltipInfo["allowTooltips"] = enabled;
        }
    }
}

DvtTouchManager.prototype.getTooltipInfo = function() {
  var tooltipInfoObj = new Object();
  var touchIds = new Array();
  var tooltipTarget = null;

  var firstGroup = null;
  var multipleGroups = false;

  var matches = this._findMatches("allowTooltips", true);
  for (var i=0;i<matches.length;i++) {
    var tooltipInfo = matches[i];
    var touchId = tooltipInfo["touchId"];

    if (!firstGroup) {
        firstGroup = tooltipInfo.grouping;
    } else {
        // Multiple groups are not supported for tooltips yet
        if (tooltipInfo.grouping != firstGroup) {
            multipleGroups = true;
        }
    }
    
    var touchInfo = this._touchMap[touchId];
    // By default, show tooltip for item under finger
    var tooltipObj = touchInfo["currentObj"];
    // If an override is present, use this instead
    if (tooltipInfo["tooltipObjOverride"]) {
        tooltipObj = tooltipInfo["tooltipObjOverride"];
    }
    
    touchIds.push(touchId);
    tooltipTarget = tooltipObj;
        
  }
  if (multipleGroups)
    touchIds = new Array();
  tooltipInfoObj.touchIds = touchIds;
  tooltipInfoObj.tooltipTarget = tooltipTarget;

  return tooltipInfoObj;
}


DvtTouchManager.prototype.containsGrouping = function(type) {
    var matches = this._findMatches("grouping", type);
    return matches.length > 0;
}

DvtTouchManager.prototype.processTouchStart = function(touchEvent) {
  var changedTouches = touchEvent.changedTouches;
  var touches = touchEvent.touches;

  this._touchCount = touches.length;
  for (var i=0; i<changedTouches.length; i++)
  {
    var changedTouch = changedTouches[i];
    var screenX = changedTouch.screenX;
    var screenY = changedTouch.screenY;
    var identifier = changedTouch.identifier;
    var targetObj = this._getObjFromTouch(changedTouch);
    var touchInfo = {
      "x":screenX,
      "y":screenY,
      "prevX":screenX,
      "prevY":screenY,
      "pageX":changedTouch.pageX,
      "pageY":changedTouch.pageY,
      "prevPageX":changedTouch.pageX,
      "prevPageY":changedTouch.pageY,
      "dx":0,
      "dy":0,
      "fireClick":true,
      "startTarget":targetObj,
      "currentObj":targetObj,
      "touchMoved":false,
      "touchtype":null,
      "startTouch":changedTouch
    };
    touchInfo[DvtTouchManager.TOUCH_MODE] = DvtTouchManager.TOUCH_MODE_DEFAULT;
    touchInfo["origx"] = screenX;
    touchInfo["origy"] = screenY;      
      
    this._touchMap[identifier] = touchInfo;

  }
  
  // For now, keep the restriction of only one finger being able to be in hover mode at a time
  if (this._isHovering() || !this._isTouchHoldAllowed()) {
      touchEvent.blockTouchHold();
  }
  return true;
}

DvtTouchManager.prototype._isTouchHoldAllowed = function() {
  var touchMoved = false;
  var count = 0;
  for (var id in this._touchMap) {
      var info = this.getTouchInfo(id);
      if (info.touchMoved)
        touchMoved = true;
      count++;
  }
  if (count > 1 && !touchMoved) {
    this.resetTouchHold();
    return false;
  }
  return true;
}

DvtTouchManager.prototype._isHovering = function() {
    for (var id in this._touchMap) {
        var info = this.getTouchInfo(id);
        if (info[DvtTouchManager.TOUCH_MODE] == DvtTouchManager.TOUCH_MODE_LONG_PRESS) {
            return true;
        }
    }
    return false;
}

DvtTouchManager.prototype._getStoredTouches = function(touches) {
  var storedTouches = new Array();
  for (var i=0; i<touches.length; i++) {
    var touch = touches[i];
    var touchid = touch.identifier;
    var info = this.getTouchInfo(touchid);
    if (info) {
        storedTouches.push(touch);
    }
  }
  return storedTouches;
}

DvtTouchManager.prototype.processTouchMove = function(touchEvent) {
  var changedTouches = this._getStoredTouches(touchEvent.changedTouches);
  var touches = this._getStoredTouches(touchEvent.touches);

  for (var i=0; i<changedTouches.length; i++) {
    var touch = changedTouches[i];
    var targetObj = this._getObjFromTouch(touch);
    var touchid = touch.identifier;
    var info = this.getTouchInfo(touchid);
    // If the single finger touch id moves, don't attempt a touch hold
    if (info[DvtTouchManager.TOUCH_MODE] != DvtTouchManager.TOUCH_MODE_LONG_PRESS &&
        this._startSingleFingerTouchId == touchid && 
        Math.abs(info["pageX"] - touch.pageX) > 0)  {
        this.resetTouchHold();
    }
    
    info.currentObj = targetObj;
    info.touchMoved = true;
   
    // If the move ever goes out of the initial object, don't fire a click
    if (info.fireClick) {
        if (info[DvtTouchManager.TOUCH_MODE] == DvtTouchManager.TOUCH_MODE_LONG_PRESS) {
        } else {
            if (info.currentObj != info.startTarget) {
                info.fireClick = false;
            }
        }
    }

  }

  for (var i=0; i<touches.length;i++) {
      var touch = touches[i];
      var identifier = touch.identifier;
      var info = this.getTouchInfo(identifier);
      var screenX = touch.screenX;
      var screenY = touch.screenY;
      var deltaX = screenX - info["x"];
      var deltaY = screenY - info["y"];

      info["prevX"] = info["x"];
      info["prevY"] = info["y"];
      info["dx"] = deltaX;
      info["dy"] = deltaY;
      info["x"] = screenX;
      info["y"] = screenY;
      info["prevPageX"] = info["pageX"];
      info["prevPageY"] = info["pageY"];
      info["pageX"] = touch.pageX;
      info["pageY"] = touch.pageY;
  }
  return true;
}

DvtTouchManager.prototype.processTouchEnd = function(touchEvent) {  
    var changedTouches = touchEvent.changedTouches;
    this._touchCount = touchEvent.touches.length;  

    for (var i=0; i<changedTouches.length; i++) {
        var touch = changedTouches[i];
        var targetObj = this._getObjFromTouch(touch); 
        var touchid = touch.identifier;
        var info = this.getTouchInfo(touchid);
        if (!info)
            continue;
        if (info.fireClick) {
            if (info[DvtTouchManager.TOUCH_MODE] == DvtTouchManager.TOUCH_MODE_LONG_PRESS) {
              this._startSingleFingerTouchId = null;
            } else {
                var sameTarget = targetObj == info.startTarget;
                var touchMoved = info.touchMoved;
                var fireClick = (sameTarget && !touchMoved);
                info.fireClick = fireClick;
            }
        }
    }
  return true;
}

DvtTouchManager.getTouchById = function(id, touches) {
    if (id != null && !isNaN(id)) {
        for (var i=0; i<touches.length; i++) {
            var touch = touches[i];
            if (touch.identifier == id) {
                return touch;
            }
        }
    }
    return null;
}

DvtTouchManager.createSavedTouchInfo = function(touchId, touchObj, tooltipObjOverride, grouping, type, moveListener, endListener, listenerObj) {
    var obj = {"touchId" : touchId, "tooltipObjOverride" : tooltipObjOverride, "grouping" : grouping, "type" : type, "touchObj" : touchObj, "allowTooltips" : true};
    obj["moveListener"] = moveListener;
    obj["endListener"] = endListener;
    obj["listenerObj"] = listenerObj;
    return obj;
}

DvtTouchManager.prototype.setTouchRegionBounds = function(touchRegionBounds) {
    this._touchRegionBounds = touchRegionBounds;
}

// Timer used for touch hold event
DvtTouchManager.prototype._handleTouchHoldStartTimer = function() {

    this._touchHoldTimer.stop();
    this.startTouchHold();

}

DvtTouchManager.prototype._handleDoubleTapTimer = function() {
    this.resetDoubleTap();
}

DvtTouchManager.prototype.resetDoubleTap = function() {
    this._doubleTapAttemptStarted = false;
    this._doubleTapAttemptObj = null;
    this._doubleTapTimer.stop();
}

/**
 * Returns a map containing touch data for two finger touch events. 
 * The map contins the following:
 * dz - The distance moved since the last touch event
 * cx - The current center of the two finger touch
 * dcx - The horizontal distance moved for the center of the two finger touch since the last touch event
 * dcy - The vertical distance moved for the center of the two finger touch since the last tch event
 */
DvtTouchManager.prototype.getMultiTouchData = function(touchIds) {
  if (touchIds.length == 2) {
    var touch1Data = this._touchMap[touchIds[0]];
    var touch2Data = this._touchMap[touchIds[1]];
    if (touch1Data == null || touch2Data == null)
      return null;
    
    if (touch1Data.dx == 0 && touch2Data.dy == 0)
        return null;
    
    //FIX BUG 17185041: use page coords instead of screen coords, which the component can then turn into
    //stage-relative coords
    
    // current distance 2 fingers are apart
    var dx = touch1Data.pageX - touch2Data.pageX;
    var dy = touch1Data.pageY - touch2Data.pageY;
    var dist = Math.sqrt(dx*dx + dy*dy);
    
    // previous distance 2 fingers are apart
    var prevdx = touch1Data.prevPageX - touch2Data.prevPageX;
    var prevdy = touch1Data.prevPageY - touch2Data.prevPageY;
    var prevdist = Math.sqrt(prevdx*prevdx + prevdy*prevdy);
    
    var dz = (dist-prevdist);
    
    // calculate the distance the center btwn the two fingers has moved
    var cx = (touch1Data.pageX+touch2Data.pageX)/2;
    var cy = (touch1Data.pageY+touch2Data.pageY)/2;
    var prevcx = (touch1Data.prevPageX+touch2Data.prevPageX)/2;
    var prevcy = (touch1Data.prevPageY+touch2Data.prevPageY)/2;
    var dcx = (cx - prevcx);
    var dcy = (cy - prevcy);
    
    return {dz:dz, cx:cx, cy:cy, dcx:dcx, dcy:dcy, dist:dist};
  }
  return null;
}

// pan code
DvtTouchManager.prototype.getMultiTouchDelta = function(touchIds)
{

  //Touches are a list of those currently being tracked
  // When a touch is moved, we will compare its screenX and screenY from its last known point.
  // We will then call the handlerCallback, passing in the deltaX and deltaY information.
  //var touches = touchEvent.touches;
  // Handle the case when the number of touches on the surfaces is equal to this._touchCount:
  if (touchIds)
  {
    var touchData;
    var touchType;
    // Have we determined the type of touch this is (only applicable for two finger touches)?
    if (touchIds.length == 2)
    {
      var touch1Data = this._touchMap[touchIds[0]];
      var touch2Data = this._touchMap[touchIds[1]];
      if (touch1Data == null || touch2Data == null)
      {
        // Missing touch data
        return;
      }

      if (touch1Data["touchtype"] == null)
      {
        // Determine if the touches are going away from each other, towards each other, or are
        // moving in unison.
        var touch1Direction = this._determineTouchDirection(touch1Data);
        if (touch1Direction != null)
        {
          var touch2Direction = this._determineTouchDirection(touch2Data);
          if (touch2Direction != null)
          {

            // We have enough data to determine which direction each touch is going.
            touchType = this._isSameDirection(touch1Direction, touch2Direction) ?
              "scroll" : "pinch";

            touch1Data["touchtype"] = touchType;
          }
        }
      }

      touchType = touch1Data["touchtype"];
      if (touchType == null)
      {
        return;
      }
      else if (touchType == "pinch")
      {
        return null;
      }
    }
    // If we have the right number of touches that were moved or less (the devices tend to pick up
    // one finger on occassion).
    //if (changedTouches.length <= this._touchCount)
   // {
      // Compute the average deltas:
      var averageXDelta = 0;
      var averageYDelta = 0;
      for (var j=0; j<touchIds.length; j++)
      {
        touchData = this._touchMap[touchIds[j]];
        averageXDelta += touchData["dx"];
        averageYDelta += touchData["dy"];
      }
      averageXDelta /= touchIds.length;
      averageYDelta /= touchIds.length;

      // Determine if each delta was within a small tolerance from the average:
      var changesWereMovement = true;
/*
      for (j=0; j<touchIds.length; j++)
      {
        touchId = touchIds[j];
        touchData = this._touchMap[touchId];
        var DISTANCE_TOLERANCE = 20; // pixels
        if (Math.abs(touchData["dx"] - averageXDelta) > DISTANCE_TOLERANCE ||
            Math.abs(touchData["dy"] - averageYDelta) > DISTANCE_TOLERANCE)
        {
          changesWereMovement = false;
          break;
        }
      }

*/
      if (changesWereMovement)
      {
        // Prevent the default event handling if we've determined that the touch event was in fact
        // for a movement.
        //touchEvent.preventDefault();

        // If there was non-zero movement:
  //      if (averageXDelta != 0 || averageYDelta != 0)
    //    {
          var scaleFactor = 1;//(window.outerWidth / window.innerWidth);
          return {deltaX: averageXDelta * scaleFactor, deltaY: averageYDelta * scaleFactor};
          // Notify the registered function of the deltas:
          //this._handlerCallback(averageXDelta * scaleFactor, averageYDelta * scaleFactor);
      //  }
      }
    }
  return null;
}

/**
 * Given two directions (see _determineTouchDirection) determine if they are moving roughly in
 * the same direction or opposite directions (pinch vs. scroll).
 */
DvtTouchManager.prototype._isSameDirection = function(
  direction1,
  direction2)
{
  if (direction1 == direction2)
  {
    return true;
  }

  // Code below determines that the directions are the same if they are adjacent to each other.
  // Example: treat "ul" as the same direction as "l" and "u".
  if (direction1.length == 2 && direction2.length == 1)
  {
    return direction1.charAt(0) == direction2 || direction1.charAt(1) == direction2;
  }
  else if (direction1.length == 1 && direction2.length == 2)
  {
    return direction1 == direction2.charAt(0) || direction2 == direction1.charAt(1);
  }
  else
  {
    return false;
  }
}

/**
 * Given a touch data object (see _handleTouchMove), determine which direction it is moving.
 * Values are "ul", "u", "ur", "l", "r", "dl", "d" or "dr",
 * where "u" is up, "l" is left, "r" is right and "d" is down.
 * "
 * @return {String} the direction or null if the distance is not significant enough.
 */
DvtTouchManager.prototype._determineTouchDirection = function(touchData)
{
  var origX = touchData["origx"];
  var origY = touchData["origy"];

  if (origX == null || origY == null)
  {
    return null;
  }

  var currentX = touchData["x"];
  var currentY = touchData["y"];

  var MINIMUM_DISTANCE_REQUIRED_RATIO = 0.01; // 1% of zoomed screen

  // Distance formula = sqrt( (x2 - x1)^2 + (y2 - y2)^2 )
  var distanceX = currentX - origX;
  var distanceY = currentY - origY;

  var distance = Math.sqrt( Math.pow(distanceX, 2) + Math.pow(distanceY, 2) );
  var minDistance = window.innerWidth * MINIMUM_DISTANCE_REQUIRED_RATIO;
  if (distance < minDistance)
  {
    // Touch has not moved far enough to determine
    return null;
  }

  // Given a change to both the x and the y coordinates, determine if one is insignificant.
  var INSIGNIFICANT_RATIO = 0.25;
  var directionX = null;
  if (Math.abs(distanceX / distanceY) >= INSIGNIFICANT_RATIO)
  {
    directionX = (distanceX < 0) ? "l" : "r"; // note that we are not considering LTR/RTL here.
      // This is okay as we are not currently concerned with left or right, only if the touches
      // are in the same or different directions.
  }

  var directionY = null;
  if (Math.abs(distanceY / distanceX) >= INSIGNIFICANT_RATIO)
  {
    directionY = (distanceY < 0) ? "u" : "d";
  }

  if (directionX != null && directionY != null)
  {
    return directionY + directionX;
  }
  else if (directionX == null)
  {
    return directionY;
  }
  else if (directionY == null)
  {
    return directionX;
  }

  // Should never reach here
  return null;
}

DvtTouchManager.prototype.calcAveragePosition = function(touchIds) {
    var pointX = 0;
    var pointY = 0;
    var touchCount = touchIds.length;
    for (var i=0;i<touchCount;i++) {
        var touchId = touchIds[i];
        var touchData = this.getTouchInfo(touchId);
        pointX += touchData.pageX;
        pointY += touchData.pageY;
    }
    pointX /= touchCount;
    pointY /= touchCount;
    return new DvtPoint(pointX, pointY);
}

/**
 * Returns an array of start targets for corresponding touch ids
 * @param {array} touchIds An array of touch ids
 * @return {array} An array of touch targets
 */
DvtTouchManager.prototype.getStartTargetsByIds = function(touchIds) {
  var targets = new Array();
  for (var i=0;i<touchIds.length;i++) {
    var touchData = this.getTouchInfo(touchIds[i]);
    targets.push(touchData["startTarget"]);
  }
  return targets;
}
/**
 * @constructor
 * Abstract class for DvtRestoreCollapse Events.  
 * 
 * <p>The supported fields are:
 * <ul>
 * <li>state</li>
 * </ul>
 * <p>
 */
var DvtListItemEvent = function(listItemId, attributeGroupId, showHide, evt)
{   
  this.Init(DvtListItemEvent.TYPE);
  this._listItemId = listItemId;
  this._attrGroupId = attributeGroupId;
  this._showHide = showHide;
  this._evt = evt;
};

DvtListItemEvent.TYPE = 'listItemShowHide';
DvtListItemEvent.SHOW = 'show';
DvtListItemEvent.HIDE = 'hide';

DvtObj.createSubclass(DvtListItemEvent, DvtBaseComponentEvent, "DvtListItemEvent");

DvtListItemEvent.prototype.getNativeEvent = function() {
  return this._evt;
};

DvtListItemEvent.prototype.getListItemId = function () 
{
  return this._listItemId;    
}

DvtListItemEvent.prototype.getShowHide = function () 
{
  return this._showHide;    
}

DvtListItemEvent.prototype.getAttributeGroupId = function () 
{
  return this._attrGroupId;
}
/**
 * @constructor
 * Abstract class for DvtRestoreCollapse Events.  
 * 
 * <p>The supported fields are:
 * <ul>
 * <li>state</li>
 * </ul>
 * <p>
 */
var DvtLegendItemRollOverEvent = function(listItemId, attributeGroupId, hideAttr, mouseState, evt)
{     
  this.Init(DvtLegendItemRollOverEvent.TYPE);
  this._listItemId = listItemId;
  this._attrGroupId = attributeGroupId;
  this._hideAttr = hideAttr;
  this._mouseState = mouseState;
  this._evt = evt;
};

DvtLegendItemRollOverEvent.TYPE = 'legendItemRollOver';
DvtLegendItemRollOverEvent.MOUSEOVER = 'mouseover';
DvtLegendItemRollOverEvent.MOUSEOUT = 'mouseout';

DvtObj.createSubclass(DvtLegendItemRollOverEvent, DvtBaseComponentEvent, "DvtLegendItemRollOverEvent");

DvtLegendItemRollOverEvent.prototype.getNativeEvent = function() {
  return this._evt;
};


DvtLegendItemRollOverEvent.prototype.getHideAttributes = function () 
{
  return this._hideAttr;    
}

DvtLegendItemRollOverEvent.prototype.getListItemId = function () 
{
  return this._listItemId;    
}

DvtLegendItemRollOverEvent.prototype.getMouseState = function () 
{
  return this._mouseState;
}

DvtLegendItemRollOverEvent.prototype.getAttributeGroupId = function () 
{
  return this._attrGroupId;
}
/**
 * @constructor
 */
var ViewportChangeEvent = function (oldDim, newDim, evt) {
  this.Init(ViewportChangeEvent.TYPE);
  this._oldDim = oldDim;
  this._newDim = newDim;
  this._evt = evt;
};

ViewportChangeEvent.TYPE = 'viewportChange';

DvtObj.createSubclass(ViewportChangeEvent, DvtBaseComponentEvent, "ViewportChangeEvent");

ViewportChangeEvent.prototype.getNativeEvent = function () {
  return this._evt;
};

ViewportChangeEvent.prototype.getOldDimensions = function () {
  return this._oldDim;
}

ViewportChangeEvent.prototype.getNewDimensions = function () {
  return this._newDim;
}
// Events when a custom tooltip action menu is interacted with
/**
 * @constructor
 */
var DvtActionTooltipEvent = function(type, target) {
    this.Init(type);
    this.target = target;
}

DvtActionTooltipEvent.TOOLTIP_CLOSED_TYPE = "actiontooltipclosed";
DvtActionTooltipEvent.TOOLTIP_STARTED_TYPE = "actiontooltipstarted";

DvtObj.createSubclass(DvtActionTooltipEvent, DvtBaseComponentEvent, "DvtActionTooltipEvent");

DvtActionTooltipEvent.prototype.Init = function(type) {
  DvtActionTooltipEvent.superclass.Init.call(this, type);
}
/**
 * @constructor
 * A higher-level class that represents a single touch event. 
 * The target and the relatedTarget simulate target and relatedTarget of the MouseEvent 
 * and the target does not match target on the native TouchEvent.
 * @param {string} type One of the DvtComponentTouchEvent event types.
 * @param {DvtTouch} touch A single point of contact with the surface
 * @param {object} target An element that triggered the event
 * @param {object} relatedTarget An element related to the element that triggered the event
 * @extends DvtBaseComponentEvent
 * @class DvtComponentTouchEvent
 */
var DvtComponentTouchEvent = function(type, touch, target, relatedTarget) {
    this.Init(type, touch, target, relatedTarget);
}

/**
 * Touch hold start event
 * @const
 */
DvtComponentTouchEvent.TOUCH_HOLD_START_TYPE = "touchholdstart";
/**
 * Touch hold move event
 * @const
 */
DvtComponentTouchEvent.TOUCH_HOLD_MOVE_TYPE = "touchholdmove";
/**
 * Touch hold end event
 * @const
 */
DvtComponentTouchEvent.TOUCH_HOLD_END_TYPE = "touchholdend";

/**
 * Touch hover start event
 * @const
 */
DvtComponentTouchEvent.TOUCH_HOVER_START_TYPE = "touchhoverstart";
/**
 * Touch hover move event
 * @const
 */
DvtComponentTouchEvent.TOUCH_HOVER_MOVE_TYPE = "touchhovermove";
/**
 * Touch hover end event
 * @const
 */
DvtComponentTouchEvent.TOUCH_HOVER_END_TYPE = "touchhoverend";
/**
 * Touch hover over event
 * @const
 */
DvtComponentTouchEvent.TOUCH_HOVER_OVER_TYPE = "touchhoverover";
/**
 * Touch hover out event
 * @const
 */
DvtComponentTouchEvent.TOUCH_HOVER_OUT_TYPE = "touchhoverout";

/**
 * Touch click event
 * @const
 */
DvtComponentTouchEvent.TOUCH_CLICK_TYPE = "touchclick";
/**
 * Touch double click event
 * @const
 */
DvtComponentTouchEvent.TOUCH_DOUBLE_CLICK_TYPE = "touchdblclick";

DvtObj.createSubclass(DvtComponentTouchEvent, DvtBaseComponentEvent, "DvtComponentTouchEvent");

/**
 * Helper method called by the constructor to initialize this object.
 * @param {string} type One of the DvtComponentTouchEvent event types
 * @param {DvtTouch} touch A single point of contact with the surface
 * @param {object} target An element that triggered the event
 * @param {object} relatedTarget An element related to the element that triggered the event
 */
DvtComponentTouchEvent.prototype.Init = function(type, touch, target, relatedTarget) {
  DvtComponentTouchEvent.superclass.Init.call(this, type);
  this.target = target;
  this.touch = touch;
  this.relatedTarget = relatedTarget;
  this._isPropagationStopped = false;
}

// Stub methods so that mouse click handlers don't break
DvtComponentTouchEvent.prototype.preventDefault = function() {
}

DvtComponentTouchEvent.prototype.stopPropagation = function() {
  this._isPropagationStopped = true;
}

DvtComponentTouchEvent.prototype.isPropagationStopped = function() {
  return this._isPropagationStopped;
}
/**
 * @constructor
 * @extends DvtObj
 * @class DvtScrollbarEvent
 * <p>
 */
var DvtScrollbarEvent = function(type, scrollbar, startCoord, endCoord,
                                valueDelta, scaledDelta) {
    this.Init(type, scrollbar, startCoord, endCoord, valueDelta, scaledDelta);
}


DvtObj.createSubclass(DvtScrollbarEvent, DvtBaseComponentEvent, "DvtScrollbarEvent");


DvtScrollbarEvent.SB_MOVE            = "scrollbarMove";
DvtScrollbarEvent.SB_MOVE_COMPLETE   = "scrollbarMoveComplete";
DvtScrollbarEvent.SB_RESIZE          = "scrollbarResize";
DvtScrollbarEvent.SB_RESIZE_COMPLETE = "scrollbarResizeComplete";


/**
 * Initializer
 * @protected
 */
DvtScrollbarEvent.prototype.Init = function(type, scrollbar, startCoord, endCoord, valueDelta, scaledDelta) {

    DvtScrollbarEvent.superclass.Init.call(this, type);

    this.type = type;           // TODO: temp workaround until this.type is supported in the base class
    this._sb = scrollbar;
    this._startCoord = startCoord;
    this._endCoord = endCoord;
    this._delta = valueDelta;
    this._scaledDelta = scaledDelta;
}



/**
 * Returns the id of the scrollbar
 */
DvtScrollbarEvent.prototype.getScrollbarId = function() {
    return this._sb.getUIId();
}

/**
 * Returns the start coordinate
 */
DvtScrollbarEvent.prototype.getStartCoord = function() {
    return this._startCoord;
}

/**
 * Returns the end coordinate
 */
DvtScrollbarEvent.prototype.getEndCoord = function() {
    return this._endCoord;
}

/**
 * Returns the signed number of pixels moved
 */
DvtScrollbarEvent.prototype.getMovedDelta = function() {
    return this._delta;
}

/**
 * Returns the signed number of pixels scaled by the size of the scrollbar
 */
DvtScrollbarEvent.prototype.getScaledDelta = function() {
    return this._scaledDelta;
}

/**
 * Returns the {@link DvtScrollbar} component
 */
DvtScrollbarEvent.prototype.getScrollbar = function() {
    return this._sb;
}




// Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
/**
 * @constructor
 */
var DvtResizeEvent = function(ww, hh, xx, yy) {
  this.Init(DvtResizeEvent.RESIZE_EVENT, ww, hh, xx, yy);
};

DvtObj.createSubclass(DvtResizeEvent, DvtBaseEvent, "DvtResizeEvent");

DvtResizeEvent.RESIZE_EVENT = "dvtResizeEvent";

DvtResizeEvent.prototype.Init = function(type, ww, hh, xx, yy) {
  DvtResizeEvent.superclass.Init.call(this, type);
  
  this._ww = ww;
  this._hh = hh;
  this._xx = xx;
  this._yy = yy;
};

DvtResizeEvent.prototype.getWidth = function() {
  return this._ww;
};

DvtResizeEvent.prototype.getHeight = function() {
  return this._hh;
};

DvtResizeEvent.prototype.getX = function() {
  return this._xx;
};

DvtResizeEvent.prototype.getY = function() {
  return this._yy;
};
// Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
/**
 * @constructor
 */
var DvtScrollEvent = function(x, y) {
  this.Init(DvtScrollEvent.SCROLL_EVENT, x, y);
};

DvtObj.createSubclass(DvtScrollEvent, DvtBaseEvent, "DvtScrollEvent");

DvtScrollEvent.SCROLL_EVENT = "dvtScrollEvent";

/**
 * Initializer
 * @protected
 */
DvtScrollEvent.prototype.Init = function(type, x, y) {
  DvtScrollEvent.superclass.Init.call(this, type);

  this._x = x;
  this._y = y;
};

DvtScrollEvent.prototype.getX = function() {
  return this._x;
};

DvtScrollEvent.prototype.getY = function() {
  return this._y;
};




/**
 * @constructor
 * @param {string} type Event type.
 * @param {number} x Marquee x.
 * @param {number} y Marquee y.
 * @param {number} w Marquee w.
 * @param {number} h Marquee h.
 * @param {boolean} ctrlKey Whether the ctrl key is pressed.
 */
var DvtMarqueeEvent = function(subtype, x, y, w, h, ctrlKey) {
  this.Init(DvtMarqueeEvent.TYPE);
  this.subtype = subtype;
  this.x = x;
  this.y = y;
  this.w = w;
  this.h = h;
  this.ctrlKey = ctrlKey;
};

DvtObj.createSubclass(DvtMarqueeEvent, DvtBaseComponentEvent, "DvtMarqueeEvent");

DvtMarqueeEvent.TYPE = "dvtMarquee";

DvtMarqueeEvent.SUBTYPE_START = "dvtMarqueeStartEvent";
DvtMarqueeEvent.SUBTYPE_MOVE  = "dvtMarqueeMoveEvent";
DvtMarqueeEvent.SUBTYPE_END   = "dvtMarqueeEndEvent";

/**
 * @return {number} Event subtype.
 */
DvtMarqueeEvent.prototype.getSubtype = function() {
  return this.subtype;
};
/**
 * @constructor
 * @param {string} type Event subtype.
 * @param {number} dxMin The delta of the xMin.
 * @param {number} dxMax The delta of the xMax.
 * @param {number} dyMin The delta of the yMin.
 * @param {number} dyMax The delta of the yMax.
 * @param {number} [dxMinTotal] The total delta of the xMin (relative to the beginning of action).
 * @param {number} [dxMaxTotal] The total delta of the xMax (relative to the beginning of action).
 * @param {number} [dyMinTotal] The total delta of the yMin (relative to the beginning of action).
 * @param {number} [dyMaxTotal] The total delta of the yMax (relative to the beginning of action).
 */
var DvtPanZoomEvent = function(subtype, dxMin, dxMax, dyMin, dyMax, dxMinTotal, dxMaxTotal, dyMinTotal, dyMaxTotal) {
  this.Init(DvtPanZoomEvent.TYPE);
  this.subtype = subtype;
  this.dxMin = dxMin;
  this.dxMax = dxMax;
  this.dyMin = dyMin;
  this.dyMax = dyMax;
  this.dxMinTotal = dxMinTotal;
  this.dxMaxTotal = dxMaxTotal;
  this.dyMinTotal = dyMinTotal;
  this.dyMaxTotal = dyMaxTotal;
};

DvtObj.createSubclass(DvtPanZoomEvent, DvtBaseComponentEvent, "DvtPanZoomEvent");

DvtPanZoomEvent.TYPE = "dvtPanZoom";

DvtPanZoomEvent.SUBTYPE_PAN_START   = "dvtPanStartEvent";
DvtPanZoomEvent.SUBTYPE_PAN_MOVE    = "dvtPanMoveEvent";
DvtPanZoomEvent.SUBTYPE_PAN_END     = "dvtPanEndEvent";
DvtPanZoomEvent.SUBTYPE_ZOOM        = "dvtZoomEvent";
DvtPanZoomEvent.SUBTYPE_PINCH_START = "dvtPinchStartEvent";
DvtPanZoomEvent.SUBTYPE_PINCH_MOVE  = "dvtPinchMoveEvent";
DvtPanZoomEvent.SUBTYPE_PINCH_END   = "dvtPinchEndEvent";

/**
 * @return {string} Event subtype.
 */
DvtPanZoomEvent.prototype.getSubtype = function() {
  return this.subtype;
};
/**
 * @constructor
 */
var DvtBaseScroller= function(context, w, h, id)
{

  this.Init(context, w, h, id);
}

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

// The type of upState to display
DvtBaseScroller.TYPE_DISABLED = 0;
DvtBaseScroller.TYPE_ENABLED = 1;
DvtBaseScroller.TYPE_ACTIVE = 2;

// Width and Height of the scroller symbol
DvtBaseScroller.SYMBOL_WIDTH = 11;
DvtBaseScroller.SYMBOL_HEIGHT = 5;
DvtBaseScroller.BUTTON_CORNER_RADIUS = 0;

DvtBaseScroller.UP_BORDER_COLOR = "#858585";
DvtBaseScroller.UP_FILL_COLOR = "#FFFFFF";
DvtBaseScroller.OVER_BORDER_COLOR = "#7BA0D9";
DvtBaseScroller.OVER_FILL_COLORS = ["#BFD8FB", "#FFFFFF"];
DvtBaseScroller.OVER_FILL_ALPHAS = [100, 100];
DvtBaseScroller.OVER_FILL_RATIOS = [0, 255];
    
DvtBaseScroller.DOWN_BORDER_COLOR = "#69849E";
DvtBaseScroller.DOWN_FILL_COLORS= ["#ACB9CA", "#7488A5"];
DvtBaseScroller.DOWN_FILL_ALPHAS = [100, 100];
DvtBaseScroller.DOWN_FILL_RATIOS = [0, 255];
    
DvtBaseScroller.SYMBOL_BORDER_COLOR_UP = "#46566C"; 
DvtBaseScroller.SYMBOL_BORDER_COLOR_OVER = "#858585";     
DvtBaseScroller.SYMBOL_BORDER_COLOR_DOWN = "#858585";  
DvtBaseScroller.SYMBOL_BORDER_COLOR_DISABLED = "#999999"; 
DvtBaseScroller.SYMBOL_FILL_COLOR_UP = "#625D5D"; //0x333333
DvtBaseScroller.SYMBOL_FILL_COLOR_OVER = "#46566C";     
DvtBaseScroller.SYMBOL_FILL_COLOR_DOWN = "#FFFFFF"; 
DvtBaseScroller.SYMBOL_FILL_COLOR_UP_DISABLED = "#999999"; 

DvtBaseScroller.prototype.Init = function(context, w, h, id){
    this._context = context;
    
    var container = new DvtContainer(context, "legendScroller_" + id);
    var impl = container.getImpl();
    DvtBaseScroller.superclass.Init.call(this, context, impl.getId());
    
    this._width = w;
    this._height = h;

    
    if (DvtAgent.isTouchDevice()) {
        var back = this._createTouchArea();
        this.addChild(back);      
    }

    this.drawUpState();
    
}


DvtBaseScroller.prototype.addMouseEventHandlers = function(){
    this.addEvtListener(DvtMouseEvent.MOUSEOUT, this.mouseOutHandler, false, this);
    this.addEvtListener(DvtMouseEvent.MOUSEOVER, this.mouseOverHandler, false, this);
}

DvtBaseScroller.prototype.mouseOverHandler = function(event){
    if(this._upStateType == DvtBaseScroller.TYPE_ENABLED)
        this.drawOverState();
}
DvtBaseScroller.prototype.mouseOutHandler = function(event){
    if(this._upStateType == DvtBaseScroller.TYPE_ENABLED)
        this.drawUpState();
}

DvtBaseScroller.prototype.drawUpState = function(){
    if(this._upStateType == DvtBaseScroller.TYPE_ENABLED){
        return this.drawEnabledState();
    }
    else if(this._upStateType == DvtBaseScroller.TYPE_ACTIVE){
        return this.drawActiveState();
    }
    else
        return this.drawDisabledState();
}

DvtBaseScroller.prototype.drawEnabledState = function(){
    if(this._upStateType !== DvtBaseScroller.TYPE_DISABLED){
        if(!this._button){
            this._button = this._drawButtonShape();
            this.addChild(this._button);
        }
        if(!this._upButton){
          this._upButton = this._drawUpButtonShape();
          this.addChild(this._upButton);
        }
        if(!this._pointer){
          this._pointer = this._drawSymbol();
          this.addChild(this._pointer);
        }
    }
      

    this._button.setAlpha(0.001);
    this._upButton.setAlpha(1);
    
    // Although the button is invisible when first loading
    // We still need to set the fill or stroke, so it can respond to mouse event
    this._button.setInvisibleFill();
    
    var buttonStroke = new DvtSolidStroke(DvtBaseScroller.UP_BORDER_COLOR, 1);
    this._upButton.setStroke(buttonStroke);
    
    var pointerStroke = new DvtSolidStroke(DvtBaseScroller.SYMBOL_BORDER_COLOR_UP, 1);
    var pointerFill = new DvtSolidFill(DvtBaseScroller.SYMBOL_FILL_COLOR_UP, 1);
    this._pointer.setFill(pointerFill);
    this._pointer.setStroke(pointerStroke);
    
    return;
}

DvtBaseScroller.prototype.drawActiveState = function(){
    if(this._upStateType !== DvtBaseScroller.TYPE_DISABLED){
        if(!this._button){
            this._button = this._drawButtonShape();
            this.addChild(this._button);
        }
        if(!this._upButton){
          this._upButton = this._drawUpButtonShape();
          this.addChild(this._upButton);
        }
        if(!this._pointer){
          this._pointer = this._drawSymbol();
          this.addChild(this._pointer);
        }
        
        this._upButton.setAlpha(0.001);
        this._button.setAlpha(1);
        
        var buttonStroke = new DvtSolidStroke(DvtBaseScroller.UP_BORDER_COLOR, 1);
        var buttonFill = new DvtSolidFill(DvtBaseScroller.UP_FILL_COLOR, 1);
        this._button.setStroke(buttonStroke);
        this._button.setFill(buttonFill);
        
        var pointerStroke = new DvtSolidStroke(DvtBaseScroller.SYMBOL_BORDER_COLOR_UP, 1);
        var pointerFill = new DvtSolidFill(DvtBaseScroller.SYMBOL_FILL_COLOR_UP, 1);
        this._pointer.setFill(pointerFill);
        this._pointer.setStroke(pointerStroke);
    }
    
    return;
}

DvtBaseScroller.prototype.drawDisabledState = function(){
    if(!this._button){
        this._button = this._drawButtonShape();
        this.addChild(this._button);
    }
    if(!this._upButton){
      this._upButton = this._drawUpButtonShape();
      this.addChild(this._upButton);
    }
    if(!this._pointer){
      this._pointer = this._drawSymbol();
      this.addChild(this._pointer);
    }
    
    this._button.setAlpha(0.001);
    this._upButton.setAlpha(1);
    
    var buttonStroke = new DvtSolidStroke(DvtBaseScroller.SYMBOL_FILL_COLOR_UP_DISABLED, 1);
    this._upButton.setStroke(buttonStroke);
    
    var pointerStroke = new DvtSolidStroke(DvtBaseScroller.SYMBOL_BORDER_COLOR_DISABLED, 1);
    var pointerFill = new DvtSolidFill(DvtBaseScroller.SYMBOL_FILL_COLOR_UP_DISABLED, 1);
    this._pointer.setFill(pointerFill);
    this._pointer.setStroke(pointerStroke);
    
    return;
}

DvtBaseScroller.prototype.drawOverState = function(){
    if(this._upStateType !== DvtBaseScroller.TYPE_DISABLED){
        if(!this._button){
            this._button = this._drawButtonShape();
            this.addChild(this._button);
        }
        if(!this._upButton){
          this._upButton = this._drawUpButtonShape();
          this.addChild(this._upButton);
        }
        if(!this._pointer){
          this._pointer = this._drawSymbol();
          this.addChild(this._pointer);
        }
    
        this._upButton.setAlpha(0.001);
        this._button.setAlpha(1);
          
        var buttonFill = new DvtLinearGradientFill(90, DvtBaseScroller.OVER_FILL_COLORS);
        var buttonStroke = new DvtSolidStroke(DvtBaseScroller.OVER_BORDER_COLOR, 1);
        this._button.setFill(buttonFill);
        this._button.setStroke(buttonStroke);
        
        var pointerStroke = new DvtSolidStroke(DvtBaseScroller.SYMBOL_BORDER_COLOR_OVER, 1);
        var pointerFill = new DvtSolidFill(DvtBaseScroller.SYMBOL_FILL_COLOR_OVER, 1);
        this._pointer.setFill(pointerFill);
        this._pointer.setStroke(pointerStroke);
    }
    return;
}
/*--------------------------------------------------------------------*/
/*  init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    Implementation of DvtDetObj.init()
 */
DvtBaseScroller.prototype.init = function()
{

}

DvtBaseScroller.prototype._drawSymbol = function(){

}

DvtBaseScroller.prototype._drawUpButtonShape = function(){

}

DvtBaseScroller.prototype._drawButtonShape = function(){
    var buttonCmds = DvtPathUtils.moveTo(DvtBaseScroller.BUTTON_CORNER_RADIUS, 0)+
    DvtPathUtils.lineTo(this._width - DvtBaseScroller.BUTTON_CORNER_RADIUS, 0)+
    DvtPathUtils.quadTo(this._width, 0, this._width, DvtBaseScroller.BUTTON_CORNER_RADIUS)+
      DvtPathUtils.lineTo(this._width, DvtBaseScroller.BUTTON_CORNER_RADIUS)+
      DvtPathUtils.lineTo(this._width, this._height - DvtBaseScroller.BUTTON_CORNER_RADIUS)+
      DvtPathUtils.quadTo(this._width, this._height, this._width - DvtBaseScroller.BUTTON_CORNER_RADIUS, this._height)+
      DvtPathUtils.lineTo(this._width - DvtBaseScroller.BUTTON_CORNER_RADIUS, this._height)+
      DvtPathUtils.lineTo(DvtBaseScroller.BUTTON_CORNER_RADIUS, this._height)+
      DvtPathUtils.quadTo(0, this._height, 0, this._height - DvtBaseScroller.BUTTON_CORNER_RADIUS)+
      DvtPathUtils.lineTo(0, this._height - DvtBaseScroller.BUTTON_CORNER_RADIUS)+
      DvtPathUtils.lineTo(0, DvtBaseScroller.BUTTON_CORNER_RADIUS)+
      DvtPathUtils.quadTo(0, 0, DvtBaseScroller.BUTTON_CORNER_RADIUS, 0)+
      DvtPathUtils.lineTo(DvtBaseScroller.BUTTON_CORNER_RADIUS, 0);
    DvtPathUtils.closePath();
    var button = new DvtPath(this._context, buttonCmds);
    button.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
    button.setPixelHinting(true);
    return button;
}

DvtBaseScroller.prototype.setUpStateType = function(type){
    this._upStateType = type;
    if(type == DvtBaseScroller.TYPE_DISABLED){
        this.drawDisabledState();
    } else {
        this.drawUpState();
    }
}
var DvtUpScroller = function(context, w, h, id) {
    DvtUpScroller.superclass.Init.call(this, context, w, h, id);
}

DvtObj.createSubclass(DvtUpScroller, DvtBaseScroller, "DvtUpScroller");

DvtUpScroller.prototype._drawSymbol = function(){
    // Position container
    var symbolX = (this._width/2) - (DvtBaseScroller.SYMBOL_WIDTH/2);
    var symbolY = (this._height/2) - (DvtBaseScroller.SYMBOL_HEIGHT/2);


    // Arrow
    var pointerCmds = DvtPathUtils.moveTo(symbolX + DvtBaseScroller.SYMBOL_WIDTH/2, symbolY) + 
              DvtPathUtils.lineTo(symbolX+DvtBaseScroller.SYMBOL_WIDTH, symbolY + DvtBaseScroller.SYMBOL_HEIGHT) + 
              DvtPathUtils.lineTo(symbolX, symbolY+DvtBaseScroller.SYMBOL_HEIGHT) + 
              DvtPathUtils.lineTo(symbolX+DvtBaseScroller.SYMBOL_WIDTH/2, symbolY);
              DvtPathUtils.closePath();
    var pointer = new DvtPath(this._context, pointerCmds);
    var dataCursorArrowFill = new DvtSolidFill("rgb(193, 194, 211)", 1);
    pointer.setFill(dataCursorArrowFill);
    this._pointer = pointer;
    pointer.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
    return pointer;
}

DvtUpScroller.prototype._drawUpButtonShape = function(){
        var buttonCmds = DvtPathUtils.moveTo(0, this._height)+
        DvtPathUtils.lineTo(this._width, this._height)+
        DvtPathUtils.moveTo(0, 0);
    DvtPathUtils.closePath();
    var button = new DvtPath(this._context, buttonCmds);
    var buttonFill = new DvtSolidFill("rgb(19, 180, 200)", 1);
    button.setFill(buttonFill);
    var buttonStroke = new DvtSolidStroke("rgb(19, 180, 200)", 1);
    button.setStroke(buttonStroke);
    button.setPixelHinting(true);
    return button;
}

DvtUpScroller.prototype._createTouchArea = function(){
    var back = new DvtRect(this._context, 0, -15, this._width, this._height + 15);
    back.setInvisibleFill();
    return back;
}
var DvtDownScroller = function(context, w, h, id) {
    DvtDownScroller.superclass.Init.call(this, context, w, h, id);
}

DvtObj.createSubclass(DvtDownScroller, DvtBaseScroller, "DvtDownScroller");

DvtDownScroller.prototype._drawSymbol = function(){
    // Position container
    var symbolX = (this._width/2) - (DvtBaseScroller.SYMBOL_WIDTH/2);
    var symbolY = (this._height/2) - (DvtBaseScroller.SYMBOL_HEIGHT/2);


    // Arrow
    var pointerCmds = DvtPathUtils.moveTo(symbolX, symbolY) + 
              DvtPathUtils.lineTo(symbolX+DvtBaseScroller.SYMBOL_WIDTH, symbolY) + 
              DvtPathUtils.lineTo(symbolX+DvtBaseScroller.SYMBOL_WIDTH/2, symbolY+DvtBaseScroller.SYMBOL_HEIGHT) + 
              DvtPathUtils.lineTo(symbolX, symbolY);
              DvtPathUtils.closePath();
    var pointer = new DvtPath(this._context, pointerCmds);
    var dataCursorArrowFill = new DvtSolidFill("rgb(193, 194, 211)", 1);
    pointer.setFill(dataCursorArrowFill);
    this._pointer = pointer;
    pointer.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
    return pointer;
}

DvtDownScroller.prototype._drawUpButtonShape = function(){
        var buttonCmds = DvtPathUtils.moveTo(0, 0)+
        DvtPathUtils.lineTo(this._width, 0)+
        DvtPathUtils.moveTo(0, 0);
    DvtPathUtils.closePath();
    var button = new DvtPath(this._context, buttonCmds);
    var buttonFill = new DvtSolidFill("rgb(19, 180, 200)", 1);
    button.setFill(buttonFill);
    var buttonStroke = new DvtSolidStroke("rgb(19, 180, 200)", 1);
    button.setStroke(buttonStroke);
    button.setPixelHinting(true);
    return button;
}

DvtDownScroller.prototype._createTouchArea = function(){
    var back = new DvtRect(this._context, 0, 0, this._width, this._height + 15);
    back.setInvisibleFill();
    return back;
}
/**
 * Class representing a scrollable legend.
 * @param {DvtContext} context
 * @param {Number} x
 * @param {Number} y
 * @param {Number} w
 * @param {Number} h
 * @param {Number} legendHeight
 * @param {Object} handle
 * @param {Number} numItems
 * @class 
 * @constructor
 * @extends {DvtRect}
 * @implements {DvtAutomationContainer}
 */

var DvtScrollableLegend = function (context, x, y, w, h, legendHeight, handle, numItems) {
  this.Init(context, x, y, w, h, legendHeight, handle, numItems);
}

DvtObj.createSubclass(DvtScrollableLegend, DvtRect, "DvtScrollableLegend");

DvtScrollableLegend.VERTICAL_GAP = 4;
DvtScrollableLegend.SCROLLER_HEIGHT = 10;
DvtScrollableLegend.HORIZONTAL_SCROLLER_PADDING = 2;
DvtScrollableLegend.VERTICAL_SCROLLER_PADDING = 2;
DvtScrollableLegend.SCROLL_ACCELERATION_PER_SECOND = 7;
DvtScrollableLegend.MAX_SCROLL_VELOCITY_PER_SECOND = 30;

// for automation purposes
DvtScrollableLegend._TEST_ID_THIS = 0;
DvtScrollableLegend._TEST_ID_SCROLL_BUTTON_TOP = 1;
DvtScrollableLegend._TEST_ID_SCROLL_BUTTON_BOTTOM = 2;

DvtScrollableLegend.prototype.Init = function (context, x, y, w, h, legendHeight, handle, numItems) {
  this._context = context;
  var container = new DvtRect(context, x, y, w, h, "scrollableLegend");
  var impl = container.getImpl();
  DvtScrollableLegend.superclass.Init.call(this, context, x, y, w, h, impl.getId());

  this._width = w;
  this._height = h;
  this._x = x;
  this._y = y;
  this._legendHeight = legendHeight;
  this._topScroller = null;
  this._bottomScroller = null;
  this._legendHandle = null;
  this._overlay = null;
  this._legendHandleMaxY = 0;
  this._legendHandleMinY = 0;
  this._legendItemSpace = 0;
  this._legendViewableHeight = 0;
  /** 
   * The array of logical objects for this legend.
   * @private 
   */
  this._peers = [];

  this._topScroller = new DvtUpScroller(context, (this._width - 2 * DvtScrollableLegend.HORIZONTAL_SCROLLER_PADDING), DvtScrollableLegend.SCROLLER_HEIGHT, "top");

  this._topScroller.setTranslateX(x + DvtScrollableLegend.HORIZONTAL_SCROLLER_PADDING);
  this._topScroller.setTranslateY(y + DvtScrollableLegend.VERTICAL_GAP);
  this.addChild(this._topScroller);

  this._bottomScroller = new DvtDownScroller(context, (this._width - 2 * DvtScrollableLegend.HORIZONTAL_SCROLLER_PADDING), DvtScrollableLegend.SCROLLER_HEIGHT, "bottom");

  this._bottomScroller.setTranslateX(x + DvtScrollableLegend.HORIZONTAL_SCROLLER_PADDING);
  this._bottomScroller.setTranslateY(y + h - DvtScrollableLegend.SCROLLER_HEIGHT - DvtScrollableLegend.VERTICAL_GAP);
  this.addChild(this._bottomScroller);

  this._maskTopY = y + DvtScrollableLegend.SCROLLER_HEIGHT + DvtScrollableLegend.VERTICAL_SCROLLER_PADDING + DvtScrollableLegend.VERTICAL_GAP;
  this._maskBottomY = y + h - DvtScrollableLegend.SCROLLER_HEIGHT - DvtScrollableLegend.VERTICAL_SCROLLER_PADDING - DvtScrollableLegend.VERTICAL_GAP;
  this._legendViewableHeight = this._maskBottomY - this._maskTopY;

  this._bottomScrollTimer = new DvtTimer(context, 50, this.scrollDown, this);
  this._topScrollTimer = new DvtTimer(context, 50, this.scrollUp, this);

  this._handle = handle;

  if (!DvtAgent.isTouchDevice()) {
    // Touch is handled in MEH
    this._topScroller.addMouseEventHandlers();
    this._bottomScroller.addMouseEventHandlers();
    this._topScroller.addEvtListener(DvtMouseEvent.MOUSEDOWN, this.onStartTopScroller, false, this);
    this._topScroller.addEvtListener(DvtMouseEvent.MOUSEUP, this.onStopTopScroller, false, this);
    this._topScroller.addEvtListener(DvtMouseEvent.CLICK, this.onClickTopScroller, false, this);
    this._bottomScroller.addEvtListener(DvtMouseEvent.MOUSEDOWN, this.onStartBottomScroller, false, this);
    this._bottomScroller.addEvtListener(DvtMouseEvent.MOUSEUP, this.onStopBottomScroller, false, this);
    this._bottomScroller.addEvtListener(DvtMouseEvent.CLICK, this.onClickBottomScroller, false, this);
  }
  else {
    if (this._handle) {
      this._handle.addEvtListener(DvtTouchEvent.TOUCHSTART, this._touchHandler, false, this);
      this._handle.addEvtListener(DvtTouchEvent.TOUCHMOVE, this._touchHandler, false, this);
      this._handle.addEvtListener(DvtTouchEvent.TOUCHEND, this._touchHandler, false, this);
      this._topScroller.addEvtListener(DvtTouchEvent.TOUCHSTART, this._touchTopHandler, false, this);
      this._topScroller.addEvtListener(DvtTouchEvent.TOUCHMOVE, this._touchTopHandler, false, this);
      this._topScroller.addEvtListener(DvtTouchEvent.TOUCHEND, this._touchTopHandler, false, this);
      this._bottomScroller.addEvtListener(DvtTouchEvent.TOUCHSTART, this._touchBottomHandler, false, this);
      this._bottomScroller.addEvtListener(DvtTouchEvent.TOUCHMOVE, this._touchBottomHandler, false, this);
      this._bottomScroller.addEvtListener(DvtTouchEvent.TOUCHEND, this._touchBottomHandler, false, this);
    }
  }

  if (this._handle) {
    this._handle.moveY(this._maskTopY - this._y);
    this._topScroller.setUpStateType(DvtBaseScroller.TYPE_DISABLED);
    this._bottomScroller.setUpStateType(DvtBaseScroller.TYPE_ENABLED);
    this._legendHandleMaxY = this._handle._y;
    this._legendHandleMinY = (this._legendHandleMaxY - this._legendHeight) + (this._maskBottomY - this._maskTopY);
    this._handle.setClipRect(new DvtRectangle(this._x, this._maskTopY, this._width, this._maskBottomY - this._maskTopY));
  }

  this._legendItemSpace = (this._legendHeight) / (numItems == 0 ? 1 : numItems);
}

/**
 * Registers the object peer with the legend.  The peer must be registered to participate
 * in interactivity.
 * @param {DvtLegendObjPeer} peer
 */
DvtScrollableLegend.prototype.__registerObject = function(peer) {
  this._peers.push(peer);
}

/**
 * Processes a category rollover event. If the event is for a logical object associated with this scrollable legend, 
 * the legend will scroll it into view.
 * @param {object} event
 */
DvtScrollableLegend.prototype.processCategoryRollover = function(event) {
  var category = event.getCategory();
  category = category[0] ? category[0] : category;
  for (var i = 0; i < this._peers.length; i++) {
    var obj = this._peers[i];
    if (obj && obj.getCategories && DvtArrayUtils.getIndex(obj.getCategories(), category) > -1) {
      this.scrollIntoView(i);
      break;
    }
  }
}

DvtScrollableLegend.prototype.scrollLegend = function (diff) {
  if (diff != 0) {
    this._handle.moveY(diff);

    // Set enabled/disabled for the scroller buttons
    if (this.isTopScrollerDisabled())
      this._topScroller.setUpStateType(DvtBaseScroller.TYPE_DISABLED);
    else 
      this._topScroller.setUpStateType(DvtBaseScroller.TYPE_ENABLED);

    if (this.isBottomScrollerDisabled())
      this._bottomScroller.setUpStateType(DvtBaseScroller.TYPE_DISABLED);
    else 
      this._bottomScroller.setUpStateType(DvtBaseScroller.TYPE_ENABLED);
  }
}

DvtScrollableLegend.prototype.onStartTopScroller = function (event) {
  this.processStartTopScroller();
  if (event)
    event.preventDefault();
}

DvtScrollableLegend.prototype.processStartTopScroller = function () {
  // Shade the background of the scrollable legend
  if (!DvtAgent.isTouchDevice()) {
    this._topScroller.drawActiveState();
  }

  this._prevTime = new Date().getTime();
  this._currTime = this._prevTime;
  this._velocity = 0;
  this._topScrollTimer.start();
}

DvtScrollableLegend.prototype.onStopTopScroller = function (event) {
  this.processStopTopScroller();
  if (event)
    event.preventDefault();
}

DvtScrollableLegend.prototype.processStopTopScroller = function () {
  // Remove the shading of the background of the scrollable legend
  if (DvtAgent.isTouchDevice()) {
    this._topScroller.drawUpState();
  }
  else {
    this._topScroller.drawOverState();
  }
  this._topScrollTimer.stop();
}

DvtScrollableLegend.prototype.onClickTopScroller = function (event) {
  this.processClickTopScroller();
  if (event)
    event.preventDefault();
}

DvtScrollableLegend.prototype.processClickTopScroller = function () {
  var diff = this._legendHandleMaxY - this._handle._y;
  if (diff > this._legendItemSpace) {
    this.scrollLegend(this._legendItemSpace);
  }
  else {
    this.scrollLegend(diff);
  }
  this.onStopTopScroller(null);
}

DvtScrollableLegend.prototype.onStartBottomScroller = function (event) {
  this.processStartBottomScroller();
  if (event)
    event.preventDefault();
}

DvtScrollableLegend.prototype.processStartBottomScroller = function () {
  if (!DvtAgent.isTouchDevice()) {
    this._bottomScroller.drawActiveState();
  }
  this._prevTime = new Date().getTime();
  this._currTime = this._prevTime;
  this._velocity = 0;

  this._bottomScrollTimer.start();
}

DvtScrollableLegend.prototype.onStopBottomScroller = function (event) {
  this.processStopBottomScroller();
  if (event)
    event.preventDefault();
}

DvtScrollableLegend.prototype.processStopBottomScroller = function () {
  if (DvtAgent.isTouchDevice()) {
    this._bottomScroller.drawUpState();
  }
  else {
    this._bottomScroller.drawOverState();
  }
  this._bottomScrollTimer.stop();
}

DvtScrollableLegend.prototype.onClickBottomScroller = function (event) {
  this.processClickBottomScroller();
  if (event)
    event.preventDefault();
}

DvtScrollableLegend.prototype.processClickBottomScroller = function () {
  var diff = this._legendHandleMinY - this._handle._y;
  if (Math.abs(diff) > this._legendItemSpace) {
    this.scrollLegend( - this._legendItemSpace);
  }
  else {
    this.scrollLegend(diff);
  }

  this.onStopBottomScroller(null);
}

DvtScrollableLegend.prototype.processScrollerStart = function (targetObj) {
  if (targetObj) {
    var searchArray = new Array();
    searchArray.push(targetObj);
    var parentObj = targetObj.getParent();
    if (parentObj)
      searchArray.push(parentObj);
    for (var i = 0;i < searchArray.length;i++) {
      var searchObj = searchArray[i];
      if (searchObj instanceof DvtDownScroller) {
        this.processStartBottomScroller();
        return true;
      }
      else if (searchObj instanceof DvtUpScroller) {
        this.processStartTopScroller();
        return true;
      }
    }
  }
  return false;
}

DvtScrollableLegend.prototype.processScrollerEnd = function (targetObj) {
  if (targetObj) {
    var searchArray = new Array();
    searchArray.push(targetObj);
    var parentObj = targetObj.getParent();
    if (parentObj)
      searchArray.push(parentObj);
    for (var i = 0;i < searchArray.length;i++) {
      var searchObj = searchArray[i];
      if (searchObj instanceof DvtDownScroller) {
        this.processStopBottomScroller();
        return true;
      }
      else if (searchObj instanceof DvtUpScroller) {
        this.processStopTopScroller();
        return true;
      }
    }
  }
  return false;
}

DvtScrollableLegend.prototype.isTopScrollerDisabled = function () {
  if (this._handle._y >= this._legendHandleMaxY)
    return true;
  else 
    return false;
}

DvtScrollableLegend.prototype.isBottomScrollerDisabled = function () {
  if (this._handle._y <= this._legendHandleMinY)
    return true;
  else 
    return false;
}

DvtScrollableLegend.prototype.scrollDown = function () {
  if (this._handle._y > this._legendHandleMinY) {
    this._prevTime = this._currTime;
    this._currTime = new Date().getTime();
    var elapsed = this._currTime - this._prevTime;
    var acceleration = DvtScrollableLegend.SCROLL_ACCELERATION_PER_SECOND * (elapsed / 1000);
    this._velocity = Math.min(DvtScrollableLegend.MAX_SCROLL_VELOCITY_PER_SECOND, this._velocity + acceleration);
    var diff = Math.max(this._legendHandleMinY - this._handle._y,  - this._velocity);
    this.scrollLegend(diff);
  }
  else {
    this.onStopBottomScroller(null);
  }
}

DvtScrollableLegend.prototype.scrollUp = function () {
  if (this._handle._y < this._legendHandleMaxY) {
    this._prevTime = this._currTime;
    this._currTime = new Date().getTime();
    var elapsed = this._currTime - this._prevTime;
    var acceleration = DvtScrollableLegend.SCROLL_ACCELERATION_PER_SECOND * (elapsed / 1000);
    this._velocity = Math.min(DvtScrollableLegend.MAX_SCROLL_VELOCITY_PER_SECOND, this._velocity + acceleration);
    var diff = Math.min(this._legendHandleMaxY - this._handle._y, this._velocity);
    this.scrollLegend(diff);
  }
  else {
    this.onStopTopScroller(null);
  }
}

DvtScrollableLegend.prototype.scrollIntoView = function (idx) {
  if (idx < 0) {
    return;
  }

  var diff = 0;
  var targetY = this._legendHandleMaxY - (idx * this._legendItemSpace);

  if (targetY <= this._handle._y - this._legendViewableHeight + 1) {
    diff = (targetY + this._legendViewableHeight) - this._handle._y - this._legendItemSpace;
    this.scrollLegend(diff);
  }
  else if (targetY > this._handle._y) {
    diff = targetY - this._handle._y - this._legendItemSpace;
    this.scrollLegend(diff);

    //scroll up one more notch if necessary
    diff = this._legendHandleMaxY - this._handle._y;
    if (diff > this._legendItemSpace) {
      this.scrollLegend(this._legendItemSpace);
    }
    else {
      this.scrollLegend(diff);
    }
  }
}

/**
 * Handler for the touch event
 * @param {DvtTouchEvent} event Touch event
 * @private
 */
DvtScrollableLegend.prototype._touchHandler = function (event) {
  this.getTouchManager().processAssociatedTouchDefault(event, this);
}

/**
 * Handler for the touch event for the top scroller
 * @param {DvtTouchEvent} event Touch event
 * @private
 */
DvtScrollableLegend.prototype._touchTopHandler = function (event) {
  this.getTouchManager().processAssociatedTouch(event, this._topScroller, this.OnTouchStartTopScroller, this);
}

/**
 * Handler for the touch event for the bottom scroller
 * @param {DvtTouchEvent} event Touch event
 * @private
 */
DvtScrollableLegend.prototype._touchBottomHandler = function (event) {
  this.getTouchManager().processAssociatedTouch(event, this._bottomScroller, this.OnTouchStartBottomScroller, this);
}

/**
 * Handler for the touch start event for the top scroller
 * @param {DvtTouchEvent} event Touch event
 * @param {DvtTouch} touch Touch Object
 */
DvtScrollableLegend.prototype.OnTouchStartTopScroller = function (event, touch) {
  this.processStartTopScroller();
  this.getTouchManager().saveProcessedTouch(touch.identifier, this._topScroller, this._topScroller, "scrollablelegendtop", "scrollablelegendtop", null, this.OnTouchEndTopScroller, this);
  event.blockTouchHold();
}

DvtScrollableLegend.prototype.OnTouchEndTopScroller = function (event, touch) {
  this.processStopTopScroller();
}

/**
 * Handler for the touch start event for the bottom scroller
 * @param {DvtTouchEvent} event Touch event
 * @param {DvtTouch} touch Touch Object
 */
DvtScrollableLegend.prototype.OnTouchStartBottomScroller = function (event, touch) {
  this.processStartBottomScroller();
  this.getTouchManager().saveProcessedTouch(touch.identifier, this._bottomScroller, this._bottomScroller, "scrollablelegendbottom", "scrollablelegendbottom", null, this.OnTouchEndBottomScroller, this);
  event.blockTouchHold();
}

DvtScrollableLegend.prototype.OnTouchEndBottomScroller = function (event, touch) {
  this.processStopBottomScroller();
}

/**
 * Processes "touchstart" event
 * @param {DvtTouchEvent} event Touch event
 * @param {DvtTouch} touch Touch object
 */
DvtScrollableLegend.prototype.immediateTouchAttempted = function (event, touch) {
  var tm = this.getTouchManager();

  var touchX = touch.pageX;
  var touchY = touch.pageY;
  var relPos = this.getCtx().getRelativePosition(touchX, touchY);
  var legendRect = new DvtRectangle(this._x, this._y, this._width, this._height);
  var stagePoint = this.localToStage(new DvtPoint(legendRect.x, legendRect.y));
  legendRect.x = stagePoint.x;
  legendRect.y = stagePoint.y;
  if (legendRect.containsPoint(relPos.x, relPos.y)) {
      this._prevTouchY = relPos.y;
      tm.saveProcessedTouchDefault(touch.identifier, this, this, "scrollablelegend", "scrollablelegend", this);
      // BUG #12883879: Still allow touch and hold to be performed on legend
      //event.blockTouchHold();
  }
}

DvtScrollableLegend.prototype.immediateTouchMoved = function (event, touch) {
  var touchX = touch.pageX;
  var touchY = touch.pageY;
  var relPos = this.getCtx().getRelativePosition(touchX, touchY);
  var relX = relPos.x;
  var relY = relPos.y;
  var diffY = (relY - this._prevTouchY);
  if (this._handle._y + diffY < this._legendHandleMinY)
    diffY = 0;
  if (this._handle._y + diffY > this._legendHandleMaxY)
    diffY = 0;
  this.scrollLegend(diffY);
  this._prevTouchY = relY;
  event.preventDefault();

}

DvtScrollableLegend.prototype.immediateTouchEnded = function (event, touch) {

  //var handled = legend.processScrollerEnd(targetObj);
}

/**
 * @override
 */
DvtScrollableLegend.prototype.dispatchTestEvent = function(subId, eventType, params) 
{
  // implementation of interface DvtAutomationContainer
  var subComponent;

  if(subId <= DvtScrollableLegend._TEST_ID_THIS)
    subComponent = this;
  else if(subId == DvtScrollableLegend._TEST_ID_SCROLL_BUTTON_TOP)
    subComponent = this._topScroller;
  else if(subId == DvtScrollableLegend._TEST_ID_SCROLL_BUTTON_BOTTOM)
    subComponent = this._bottomScroller;

  if(subComponent && subComponent instanceof DvtDisplayable)
    subComponent.dispatchDisplayableEvent(eventType, params);
              
};

/**
 * Gets a touch manager object
 * @return {DvtTouchManager} touch manager object
 */
DvtScrollableLegend.prototype.getTouchManager = function () {
  return this._touchManager;  
}

/**
 * Sets a touch manager object
 * @param {DvtTouchManager} touch manager object
 */
DvtScrollableLegend.prototype.setTouchManager = function (touchManager) {
  this._touchManager = touchManager;  
}
/**
  *  Handle.
  *  @extends DvtContainer
  *  @class DvtHandle  handle. 
  *  @constructor  
  */
var   DvtHandle = function(context, sId, boundRect, clipRect, clipId) {
    this.Init(context, sId, boundRect, clipRect, clipId);
}

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



/**
 *    @protected
 */
DvtHandle.prototype.Init = function(context, sId, boundRect, clipRect, clipId) {

    DvtHandle.superclass.Init.call(this, context, sId);
    this.Id = sId;
    this._x = 0;
    this._y = 0;
    this._boundRect = boundRect;
    this._clipRect = this._normalizeRect(clipRect);
    this._clipId = clipId;
}

/**
 * getId - returns this handle ID
 */
DvtHandle.prototype.getId = function() {
    return this.Id;
}

/**
 * getBoundingPoints - returns the bounding points for this handle
 *
 */
DvtHandle.prototype.getBoundingPoints = function() {
    return this._boundRect;
}

/**
 * getClipRect - returns the clipping rect for this handle
 *
 */
DvtHandle.prototype.getClipRect = function() {
    return this._clipRect;
}

/**
 * setClipRect - sets the clipRect on this handle
 *
 */
DvtHandle.prototype.setClipRect = function(clipRect) {
    this._clipRect = this._normalizeRect(clipRect);
}

DvtHandle.prototype.getX = function() {
    return this._x;
}

DvtHandle.prototype.getY = function() {
    return this._y;
}

DvtHandle.prototype.moveX = function(nx) {

    this._x += nx;

    if (this._boundRect != null) {
        this._boundRect[0] += nx;
        this._boundRect[2] += nx;
    }
    
    // Bug #12826435: Translate the container instead of all the children indiv
    var oldX = this.getTranslateX();
    this.setTranslateX(oldX + nx);

    // TODO: preClippingSanityCheck();
    // TODO: updateBackgroundEraser();
}


DvtHandle.prototype.moveY = function(ny) {

    this._y += ny;

    if (this._boundRect != null) {
        this._boundRect[1] += ny;
        this._boundRect[3] += ny;
    }
    
    // Bug #12826435: Translate the container instead of all the children indiv
    
    var oldY = this.getTranslateY();
    this.setTranslateY(oldY + ny);

    // TODO: preClippingSanityCheck();
    // TODO: updateBackgroundEraser();
}


DvtHandle.prototype.render = function() {
    if (this._clipRect) {
        var cp = new DvtGraphClipPath(this._clipId);
        cp.addRect(this._clipRect.x, this._clipRect.y, this._clipRect.w, this._clipRect.h);
        this.setClipPath(cp);
    }
}


DvtHandle.prototype._normalizeRect = function(rect) {

    // for some reason the clipRect may be defined "backward" with height
    // (and width ?) < 0, if so, normalize it
    if (rect && rect.w < 0) {
        rect.w = -rect.w;
        rect.x -= rect.w;
    }
    if (rect && rect.h < 0) {
        rect.h = -rect.h;
        rect.y -= rect.h;
    }
    return rect;
}
/**
 *  A static class for DnD support.
 *  @class DvtDnDUtils
 */
var DvtDnDUtils = function () {
};

DvtObj.createSubclass(DvtDnDUtils, DvtObj, "DvtDnDUtils");

/**
 * Returns a {DvtRectangle} representing the drag feedback bounds.
 * @param {object} displayables The DvtDisplayable or array of DvtDisplayables to display for drag feedback.
 * @param {DvtDisplayable} targetCoordinateSpace The displayable defining the coordinate space of the resulting bounds.  The
 *                                               parent coordinate space is used if none is provided.
 */
DvtDnDUtils.getDragFeedbackBounds = function (displayables, targetCoordinateSpace) {
  if (!displayables)
    return null;

  var bounds = null;
  if (!(displayables instanceof Array)) {
    // for simple objects, just return the object bounds
    // TODO: eventually should ensure that bounds are in container coordinates
    bounds = displayables.getDimensions(targetCoordinateSpace ? targetCoordinateSpace : displayables.getParent());
  }
  else if (displayables.length > 0) {
    // TODO: eventually should ensure that bounds are in container coordinates
    // for arrays, initialize bounds based on the first object
    bounds = displayables[0].getDimensions(targetCoordinateSpace ? targetCoordinateSpace : displayables[0].getParent());

    // ... and adjust it to acommodate other objects in the collection
    for (var i = 1;i < displayables.length;i++) {
      var obj = displayables[i];
      var objBounds = obj.getDimensions(targetCoordinateSpace ? targetCoordinateSpace : obj.getParent());

      if (objBounds.x < bounds.x) {
        bounds.w += (bounds.x - objBounds.x);
        bounds.x = objBounds.x;
      }
      if (objBounds.y < bounds.y) {
        bounds.h += (bounds.y - objBounds.y);
        bounds.y = objBounds.y;
      }
      if ((objBounds.x + objBounds.w) > (bounds.x + bounds.w)) {
        bounds.w = objBounds.x + objBounds.w - bounds.x;
      }
      if ((objBounds.y + objBounds.h) > (bounds.y + bounds.h)) {
        bounds.h = objBounds.y + objBounds.h - bounds.y;
      }
    }
  }
  return bounds;
}
/**
 * Abstract class for XML parser.  
 * @extends DvtObj
 * @class DvtXmlParser
 * @constructor
 * 
 * @param {DvtContext}  context  platform specific context object
 */
var DvtXmlParser = function(context) {
  if (!this._impl) {
    this._impl = context.getImplFactory().newParser();
  }
};

DvtObj.createSubclass(DvtXmlParser, DvtObj, "DvtXmlParser");

/**
 * Parses the XML string and returns the root node.
 * @param {string} xmlString the XML string to parse
 * @return {DvtXmlNode} the root node of the XML document
 */
DvtXmlParser.prototype.parse = function(xmlString) {
  if (this._impl && this._impl.parse) {
    return this._impl.parse(xmlString);
  }
  
  return null;
}
/**
 * Abstract class for XML node.  Subclasses should override to provide support
 * for different platforms.
 * @extends DvtObj
 * @class DvtXmlNode
 * @constructor
 */
var DvtXmlNode = function() {}

DvtObj.createSubclass(DvtXmlNode, DvtObj, "DvtXmlNode");

/**
 * Returns the value for the given attribute in this XML node.
 * @param {string} attr the attribute to look for
 * @return {string} the value of the attribute, if available
 */
DvtXmlNode.prototype.getAttr = function(attr) {
  return null; // subclasses should override
}

/**
 * Returns an Array containing the child elements of this node.  Only nodes that
 * are XML elements are returned.  Text nodes are ignored.
 * @return {array} an Array containing all child elements of this node.
 */
DvtXmlNode.prototype.getChildNodes = function() {
  return new Array(); // subclasses shoudl override
}

/**
 * Returns the child element of this node. Only nodes that
 * are XML elements are returned.  Text nodes are ignored.
 * @return {DvtXmlNode} the first child element
 */
DvtXmlNode.prototype.getChild = function(i) {
  var children = this.getChildNodes();
  if(children != null && children.length > i)
    return children[i];
  else
    return null;
}

/**
 * Returns the first child element of this node. Only nodes that
 * are XML elements are returned.  Text nodes are ignored.
 * @return {DvtXmlNode} the first child element
 */
DvtXmlNode.prototype.getFirstChild = function() {
  return this.getChild(0);
}

/**
 * Returns the name of this node.
 * @return {string} the name of the node
 */
DvtXmlNode.prototype.getName = function() {
  return null; //subclasses should override
};

/**
 * Returns the attributes of this node as an array of 
 * {name:, value:} pairs.
 * @return {array} the attributes of the node
 */
DvtXmlNode.prototype.getAttributes = function() {
  return null; //subclasses should override
};


/**
 * Returns an Array containing the children of this node. 
 * @return {array} an Array containing all child elements and text nodes of this node.
 */
DvtXmlNode.prototype.getAllChildNodes = function() {
  return new Array(); // subclasses shoudl override
}


/**
 * Returns the value of this node.
 * @return {string} the value of the node
 */
DvtXmlNode.prototype.getNodeValue = function() {
  return null; //subclasses should override
};

/**
 * Returns the textContent of this node.
 * @return {string} the textContent of the node
 */
DvtXmlNode.prototype.getTextContent = function() {
  return null; //subclasses should override
};

/**
 * Returns a node list of all child element nodes whose tagName match the speccified name.
 * @return {string} the name of the node
 */
DvtXmlNode.prototype.getElementsByTagName = function(name) {
  return null; //subclasses should override
};

// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.

/**
  * A factory class for HTML to create object implementations.
  * @extends  DvtImplFactory
  * @class DvtHtmlImplFactory  A factory class to create HTML implementation objects.
  * @constructor  Creates HTML inplementation objects.
  */
var DvtHtmlImplFactory = function(context) {
  this.Init(context) ;
};

DvtObj.createSubclass(DvtHtmlImplFactory, DvtImplFactory, "DvtHtmlImplFactory");

/**
  *  Object initializer.
  *  @protected
  */
DvtHtmlImplFactory.prototype.Init = function(context)
{
   DvtHtmlImplFactory.superclass.Init.call(this, context) ;
};

/*-------------------------------------------------------------------------*/
/*     Other Constructors                                                  */
/*-------------------------------------------------------------------------*/

/**
 * @override
 */
DvtHtmlImplFactory.prototype.newParser = function() {
  if (DvtAgent.isEnvironmentBrowser())
    return new DvtDomXmlParser();
  else
    return new DvtJavaXmlParser();
};


/**
  * Create a new timer.
  * 
  * @returns {DvtHtmlTimer}
  * @override
  */
DvtHtmlImplFactory.prototype.newTimer = function () {
  return new DvtHtmlTimer();
};

/**
 * @override
 */
DvtHtmlImplFactory.prototype.newContextMenuHandler = function () {
  return new DvtHtmlContextMenuHandler();
};

/**
 * @override
 */
DvtHtmlImplFactory.prototype.newTooltipManager = function (id) {
  return new DvtHtmlTooltipManager(this.GetContext(), id);
};

/**
 * @override
 */
DvtHtmlImplFactory.prototype.newRichTooltipManager = function (id) {
  return new DvtHtmlRichTooltipManager(this.GetContext(), id);
};

/**
 * @override
 */
DvtHtmlImplFactory.prototype.newJsExt = function () {
  return new DvtSvgJsExt();
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing a timer in HTML.
  * @class DvtHtmlTimer
  * @constructor
  */
var DvtHtmlTimer = function()
{
  this.Init();
};

DvtObj.createSubclass(DvtHtmlTimer, DvtObj, "DvtHtmlTimer");

/**
  * @protected
  */
DvtHtmlTimer.prototype.Init = function()
{
  this._timerId = null;
};

/**
  * Start the internal timer implementation.
  * 
  * @param {number}  interval  interval between timer ticks, in milliseconds
  * @param {function}  callback  function to call for each timer tick
  * @param {object}  callbackObj  optional object instance that callback 
  *        function is defined on
  */
DvtHtmlTimer.prototype.startTimer = function(interval, callback, callbackObj)
{
  this._timerId = window.setInterval(function() {callback.call(callbackObj)}, interval);
};

/**
  * Stop the internal timer implementation.
  */
DvtHtmlTimer.prototype.stopTimer = function()
{
  if (this._timerId)
  {
    window.clearInterval(this._timerId);
    this._timerId = null;
  }
};
/**
 * Context Menu Handler implementation for HTML context menus.
 * @class DvtHtmlContextMenuHandler
 * @constructor
 */
var DvtHtmlContextMenuHandler = function() {
	this.Init();
};

DvtObj.createSubclass(DvtHtmlContextMenuHandler, DvtObj, "DvtHtmlContextMenuHandler");

DvtHtmlContextMenuHandler.prototype.Init = function()
{
}

DvtHtmlContextMenuHandler.prototype.setObj = function(obj)
{
	this._obj = obj;
}

DvtHtmlContextMenuHandler.prototype.prepareMenuItems = function(event, menuType, menuId) {
    //Nothing
}

DvtHtmlContextMenuHandler.prototype.getMenuEvent = function(xmlNodes, event, menuType, menuId) {
  var nativeEvent = event.getNativeEvent();
  // Look through all context menu definitions for the right context menu to show
  for(var i=0; i<xmlNodes.length; i++) {
    var xmlNode = xmlNodes[i];
    
    if(menuType == xmlNode.getAttr(DvtContextMenuHandler._ATTR_MENU_CONTAINER_TYPE)) {
      // Match found.  This implementation will fire a showPopupEvent.
      var popupId = xmlNode.getAttr(DvtContextMenuHandler._ATTR_MENU_CONTAINER_POPUP_ID);
      return new DvtGraphContextMenuEvent(nativeEvent, popupId);
    }
  }
  return null;
}
/**
 * @param {DvtContext} context
 * @param {string} domElementId
 * @constructor
 * @extends DvtTooltipManager
 * @class DvtHtmlTooltipManager
 */
var DvtHtmlTooltipManager = function(context, domElementId) {
    this.Init(context, domElementId);
};

DvtObj.createSubclass(DvtHtmlTooltipManager, DvtTooltipManager, "DvtHtmlTooltipManager");

/** @private */
DvtHtmlTooltipManager._TOOLTIP_DIV_ID = "_dvtTooltip";

DvtHtmlTooltipManager.SHOW_DELAY = 500;  // in ms
DvtHtmlTooltipManager.HIDE_DELAY = 5000; // in ms
DvtHtmlTooltipManager.TRANSITION_DURATION = 250;  // in ms

DvtHtmlTooltipManager.BG_COLOR = "#c1ccd7";
DvtHtmlTooltipManager.BORDER_COLOR = "#9ba2b0";
DvtHtmlTooltipManager.FONT_COLOR = "#383a47";

DvtHtmlTooltipManager.UNSTYLED_POPUP_CLASS = "OraDVTUnstyledPopup";
DvtHtmlTooltipManager.DATATIP_POPUP_CLASS = "OraDVTDatatipPopup";
DvtHtmlTooltipManager.TOOLTIP_POPUP_CLASS = "OraDVTTooltipPopup";

DvtHtmlTooltipManager.BORDER_BUFFER = 2;            // Buffer for border
DvtHtmlTooltipManager.TOOLTIP_ABOVE_OFFSET = 10;    // Adjustment for tooltip position when above mouse

/**
 * @param {DvtContext} context
 * @param {string} domElementId
 */ 
DvtHtmlTooltipManager.prototype.Init = function(context, domElementId) 
{
  this._context = context;
  
  // if a dom element is specified, it will override the default tooltip div
  this._domElementId = (domElementId ? domElementId : DvtHtmlTooltipManager._TOOLTIP_DIV_ID);
  this._isTooltip = true;
  this._font = null;
}

/**
 * Sets the tooltip font.
 * @param {DvtFont} font The font.
 */
DvtHtmlTooltipManager.prototype.setFont = function(font) {
  this._font = font;
}

/**
 * Displays a datatip.
 * @param {number} x The pageX coordinate at which to display the datatip.
 * @param {number} y The pageY coordinate at which to display the datatip.
 * @param {string} text The string to show in the datatip.
 * @param {string} borderColor The border color for the datatip.
 * @param {boolean} [useOffset] false to prevent offsets from being applied. Offsets will be applied otherwise.
 * @param 
 */
DvtHtmlTooltipManager.prototype.showDatatip = function(x, y, text, borderColor, useOffset)
{
  // If useOffset not specified, then apply offsets
  if(useOffset == null)
    useOffset = true;

  this._isTooltip = false;
  this._showTextAtPosition(x, y, text, borderColor, useOffset, DvtHtmlTooltipManager.DATATIP_POPUP_CLASS);
}

/**
 * Helper to display a datatip or tooltip.
 * @param {number} x The pageX coordinate at which to display the tooltip.
 * @param {number} y The pageY coordinate at which to display the tooltip.
 * @param {string} text The string to show in the tooltip.
 * @param {string} borderColor The border color for the tooltip.
 * @param {boolean} useOffset True if offsets should be applied to the coordinates.
 * @param {string} popupClass The style class to use for the outer tooltip div.
 * @private
 */
DvtHtmlTooltipManager.prototype._showTextAtPosition = function(x, y, text, borderColor, useOffset, popupClass)
{
  // Make replacements on the text string as needed
  if(text) {
    text = text.replace(/</g, '&lt;');
    text = text.replace(/>/g, '&gt;');
    
    // Support a subset of HTML tags, including bold and italic
    text = text.replace(/&lt;b&gt;/g, '<b>');
    text = text.replace(/&lt;\/b&gt;/g, '<\/b>'); 
    text = text.replace(/&lt;i&gt;/g, '<i>');
    text = text.replace(/&lt;\/i&gt;/g, '<\/i>'); 
  
    // Replace logical newlines sequences
    text = text.replace(/\n/g, '<br>'); 
    text = text.replace(/&lt;br&gt;/g, '<br>');
    text = text.replace(/&lt;br\/&gt;/g, '<br>');
    
    // Prevent text from wrapping by replacing spaces with non-breaking spaces
    text = text.replace(/ /g, '&nbsp;');
  }
    
  // Make sure the popup class is defined
  if(!popupClass)
    popupClass = DvtHtmlTooltipManager.UNSTYLED_POPUP_CLASS;

  // Create the tooltip element
  var tooltipElem = document.createElement("span");
  tooltipElem.style.visibility = "inherit";  
  tooltipElem.style.width = null;
  tooltipElem.style.height = null;
  
  // Apply the font if speicfied
  if(this._font) {
    tooltipElem.style.fontSize = this._font.size+"px";
    tooltipElem.style.color = this._font.color;
    tooltipElem.style.fontFamily = this._font.name;
    if(this._font.bold) 
      tooltipElem.style.fontWeight = 'bold';
    if(this._font.italic) 
      tooltipElem.style.fontStyle = 'italic';
    if(this._font.underline)
      tooltipElem.style.textDecoration = 'underline';
  }
  
  // Set the text
  tooltipElem.innerHTML = text;
  
  // Apply the border on the outer element
  var outerElem = this.GetTooltipElem();
  outerElem.style.borderColor = borderColor;
  
  // Position the outer element and add the tooltip
  outerElem.className = popupClass;
  outerElem.style.position = 'absolute';
  outerElem.style.zIndex = 2147483647;
  outerElem.innerHTML = '';
  outerElem.appendChild(tooltipElem);
  this.PostElement(outerElem, x, y, true, useOffset);
}

/**
 * Performs a bunch of processing for an assembled tooltip or datatip.
 * @param {object} tooltip The outer DOM element of the tooltip.
 * @param {number} x The pageX coordinate at which to display the tooltip.
 * @param {number} y The pageY coordinate at which to display the tooltip.
 * @param {boolean} noEvents True if this tooltip should not recieve mouse events.
 * @param {boolean} useOffset True if this tooltip should be offset from the x and y coordinates.
 * @protected
 */
DvtHtmlTooltipManager.prototype.PostElement = function(tooltip, x, y, noEvents, useOffset) {
  // Block mouse events if the tip is not interactive
  tooltip.style["pointer-events"] = noEvents ? "none" : "auto";

  // Clear the width and height. This allows _positionTip to get the accurate tooltip size
  tooltip.style.width = null;
  tooltip.style.height = null;
  
  // Add offsets to the tip position as needed
  if(useOffset) {
    var offsets = this._getOffsets(tooltip);
    this.positionTip(x+offsets.x, y+offsets.y, x, y);
  } 
  else
    this.positionTip(x, y, x, y);

  // Make the tooltip visible
  tooltip.style.visibility = "visible";
}

/**
 * @override
 */
DvtHtmlTooltipManager.prototype.hideTooltip = function()
{
  this.clearTooltip();
  this._tooltipDisplayed = false;
  this._timerIsRunning = false;

  var tooltip = document.getElementById(this._domElementId);
  if(tooltip) {
    tooltip.style.visibility='hidden';
    tooltip.style.width = '0px';
    tooltip.style.height = '0px';
    if (!DvtAgent.isRightToLeft(this._context))    
      tooltip.style.left = '0px';
    tooltip.style.top = '0px';
  }
};

/**
 * Retrieves the outer DOM element of the tooltip.
 * @return {object}
 * @protected
 */
DvtHtmlTooltipManager.prototype.GetTooltipElem = function() {
  // Retrieve the tooltip element and create if it doesn't exist.
  var tooltip = document.getElementById(this._domElementId);
  if(!tooltip)
    tooltip = this.InitializeTooltipElem();

  return tooltip;
}

/**
 * Initializes the outer DOM element of the tooltip.
 * @return {object} The outer DOM element.
 * @protected
 */
DvtHtmlTooltipManager.prototype.InitializeTooltipElem = function() {
  var tooltip = document.createElement("div");
  tooltip.id = this._domElementId;
  tooltip.style.visibility = "hidden";
  document.body.appendChild(tooltip);
  this.InitContent(tooltip);
  return tooltip;
}

/**
 * TODO JSDoc
 * @protected
 */
DvtHtmlTooltipManager.prototype.InitContent = function(tooltip) {
}

/**
 * @override
 */
DvtHtmlTooltipManager.prototype.showTooltip = function(x, y, text, shape, bTrackMouse, borderColor)
{

  if (! text || text.length == 0)
    return;

  this._shape = shape;
  this._x = x;
  this._y = y;
  this._text = text;

  //TODO: trackMouse is not working!
  this._trackMouse = bTrackMouse;

  if (this._timerIsRunning)
    return;
  
  var tooltipElem = document.createElement("span");
  tooltipElem.className = "OraDVTTooltipText";
  tooltipElem.innerHTML = text;
  tooltipElem.style.color = DvtHtmlTooltipManager.FONT_COLOR;
  
  if (! borderColor) {
    borderColor = DvtHtmlTooltipManager.BORDER_COLOR;
  }
  var outerElem = this.GetTooltipElem();
  outerElem.style.borderColor = borderColor;
  
  if (DvtAgent.isTouchDevice() || (this._tooltipDisplayed && this._trackMouse)) {
      this._displayTooltip(this._x, this._y, this._text, this._borderColor);
  } else {
      // Tooltips fade-in and remove themselves after a delay.
      this._timerIsRunning = true;
      this._showTimerId = window.setTimeout(DvtObj.createCallback(this, this._handleShowTimer), DvtHtmlTooltipManager.SHOW_DELAY);
  }
}


/**
 * Clears the currently displayed tooltip.
 * @export
 */
DvtHtmlTooltipManager.prototype.clearTooltip = function()
{
  if (this._showTimerId) {
    window.clearTimeout(this._showTimerId);

    this._showTimerId = undefined;
    this._x = undefined;
    this._y = undefined;
    this._text = undefined;

    this._trackMouse = undefined;
    this._shape = undefined;
  }
  if (this._hideTimerId) {
    window.clearTimeout(this._hideTimerId);
    this._hideTimerId = undefined;
  }
}

/**
 * TODO JSDoc
 * @private
 */
DvtHtmlTooltipManager.prototype._handleShowTimer = function(event)
{
  this._timerIsRunning = false;
  this._displayTooltip(this._x, this._y, this._text, this._borderColor);
  this._tooltipDisplayed = true;
}

/**
 * TODO JSDoc
 * @private
 */
DvtHtmlTooltipManager.prototype._displayTooltip = function(x, y, text, borderColor) 
{
  this._isTooltip = true;
  this._showTextAtPosition(x, y, text, borderColor, true, DvtHtmlTooltipManager.TOOLTIP_POPUP_CLASS);
}

/**
 * TODO JSDoc
 * TODO This should return DvtDimension, not DvtPoint
 * @private
 */
DvtHtmlTooltipManager.prototype._getOffsets = function(tooltip)
{
  var tooltipBounds = this.getTooltipBounds(tooltip);
  if(DvtAgent.isTouchDevice()) {
    return new DvtPoint(0 - tooltipBounds.w/2, -30 - tooltipBounds.h);
  } 
  else {
    var yOffset = -12 - tooltipBounds.h;
    var xOffset = 12;
    if (this._isTooltip) {
      yOffset = 22;
      xOffset = 0;
    }
    var bBidi = DvtAgent.isRightToLeft(this._context);
    if (bBidi) {
      xOffset = -xOffset - tooltipBounds.w;          
    }

    return new DvtPoint(xOffset, yOffset);
  }
}

/**
 * Returns an approximation of the tooltip bounds.
 * @return {DvtRectangle}
 */
DvtHtmlTooltipManager.prototype.getTooltipBounds = function()
{
  var tooltip = this.GetTooltipElem();

  // Fix for 17898759: The offset height can be incorrect if the tooltip div inherits from the HTML body.  Set to auto
  // temporarily to prevent this.
  tooltip.style.height = 'auto';
  return new DvtRectangle(parseInt(tooltip.style.left), parseInt(tooltip.style.top), tooltip.offsetWidth, tooltip.offsetHeight);
}

/**
 * Positions the tip based on the mouse position in relation to the tooltip size.
 * @param {number} x The coordinate at which to display the tooltip with offsets.
 * @param {number} y The coordinate at which to display the tooltip with offsets.
 */
DvtHtmlTooltipManager.prototype.positionTip = function(x, y)
{
  var tooltip = this.GetTooltipElem();
  var tooltipDimensions = this.getTooltipBounds();
  var tooltipWidth = tooltipDimensions.w;
  var tooltipHeight = tooltipDimensions.h;

  // Calculate the bounds of the browser viewport, within which we'll position the tooltip
  var viewportBounds = new DvtRectangle(window.pageXOffset + DvtHtmlTooltipManager.BORDER_BUFFER, 
                                        window.pageYOffset + DvtHtmlTooltipManager.BORDER_BUFFER, 
                                        window.innerWidth - 2*DvtHtmlTooltipManager.BORDER_BUFFER, 
                                        window.innerHeight - 2*DvtHtmlTooltipManager.BORDER_BUFFER);
  
  // X Position
  var tooltipX = x;
  if(x + tooltipWidth > viewportBounds.x + viewportBounds.w)
    tooltipX = viewportBounds.x + viewportBounds.w - tooltipWidth;
  else if(x < viewportBounds.x)
    tooltipX = viewportBounds.x;
  
  // Y Position
  var tooltipY = y;
  if(y < viewportBounds.y)
    tooltipY = viewportBounds.y;
  else if(y + tooltipHeight > viewportBounds.y + viewportBounds.h) 
    tooltipY = viewportBounds.y + viewportBounds.h - tooltipHeight;
  
  // Apply the calculated positions
  tooltip.style.left = tooltipX + 'px';
  tooltip.style.top = tooltipY + 'px';
}
/**
 * Utility class for providing keyboard listeners to add to HTML divs
 * @class DvtHtmlKeyboardListenerUtils
 */
var DvtHtmlKeyboardListenerUtils = function() {};

DvtObj.createSubclass(DvtHtmlKeyboardListenerUtils, DvtObj, "DvtHtmlKeyboardListenerUtils");

//FIX BUG 16901294: for editable DvtText, ignore events from the HTML textArea
/**
 * Field to look for on event target to see if events from that target should be ignored.
 */
DvtHtmlKeyboardListenerUtils.ATTR_IGNORE_EVENTS_FROM_TARGET = "_dvtIgnoreEventsFromTarget";

/**
 * @param {Boolean} useCapture True if the listener is to be used in the event capture phase, 
 *                             false if the listener is to be used in the event bubble phase
 * @return {function} A function that can be used as an event listener during the bubble or capture phase
 */
DvtHtmlKeyboardListenerUtils.getListener = function(useCapture)
{
  if(useCapture)
    return DvtHtmlKeyboardListenerUtils._captureListener;
  else
    return DvtHtmlKeyboardListenerUtils._bubbleListener;  
}


/**
 * The event listener that is called by the implementation object's bubble phase listeners.
 * This function will wrap the event and delegate to the real event listeners.  
 * @param {object} event the DOM event object
 * @this {object} the platform object actively processing the event object with an event listener
 * @private
 */
DvtHtmlKeyboardListenerUtils._bubbleListener = function(event)
{
  //FIX BUG 16901294: for editable DvtText, ignore events from the HTML textArea
  if (DvtHtmlKeyboardListenerUtils._checkIgnoreTarget(event)) {
    return;
  }
  if(this._obj && this._obj instanceof Array)
  {
    var i;
    var svgObj;
    var dvtEvent;
    var length = this._obj.length;
    for(i=0; i<length; i++)
    {
      svgObj = this._obj[i];
      dvtEvent = DvtSvgEventFactory.newEvent(event, svgObj.getObj().getCtx());
      svgObj.getObj().FireListener(dvtEvent, false);
    }
  }  
}


/**
 * The event listener that is called by the implementation object's capture phase listeners.
 * This function will wrap the event and delegate to the real event listeners.  
 * @param {object} event the DOM event object
 * @this {object} the platform object actively processing the event object with an event listener
 * @private
 */
DvtHtmlKeyboardListenerUtils._captureListener = function(event)
{
  //FIX BUG 16901294: for editable DvtText, ignore events from the HTML textArea
  if (DvtHtmlKeyboardListenerUtils._checkIgnoreTarget(event)) {
    return;
  }
  if(this._obj && this._obj instanceof Array)
  {
    var i;
    var svgObj;
    var dvtEvent;
    var length = this._obj.length;
    for(i=0; i<length; i++)
    {
      svgObj = this._obj[i];
      dvtEvent = DvtSvgEventFactory.newEvent(event, svgObj.getObj().getCtx());
      svgObj.getObj().FireListener(dvtEvent, true);
    }
  }  
}

/**
 * Check whether the event should be ignored by the toolkit.
 * @param {object} event the DOM event object
 * @private
 */
DvtHtmlKeyboardListenerUtils._checkIgnoreTarget = function(event) {
  //FIX BUG 16901294: for editable DvtText, ignore events from the HTML textArea
  if (event && event.target && event.target[DvtHtmlKeyboardListenerUtils.ATTR_IGNORE_EVENTS_FROM_TARGET]) {
    return true;
  }
  return false;
};
/**
 * Wrapper class providing XML parsing functionality using the DOM.
 * @class DvtDomXmlParser
 * @extends DvtXmlParser
 * @constructor
 */
var DvtDomXmlParser = function() {
  this._parser = new DOMParser();
}

DvtObj.createSubclass(DvtDomXmlParser, DvtObj, "DvtDomXmlParser");

/**
 * Parses the XML string and returns the root node.
 * @param {string} xmlString the XML string to parse
 * @return {DvtDomXmlNode} the root node of the XML document
 */
DvtDomXmlParser.prototype.parse = function(xmlString) {
  var xmlDoc = this._parser.parseFromString(xmlString, "text/xml");
  var root = xmlDoc.documentElement;
  return new DvtDomXmlNode(root);
}
/**
 * Wrapper class providing access to DOM XML Nodes.
 * @extends DvtXmlNode
 * @class DvtDomXmlNode
 * @constructor
 * @param {Node} node the DOM Node object
 */
var DvtDomXmlNode = function(node) {
  this._node = node;
}

// Make DvtDomXmlNode a subclass of DvtXmlNode
DvtObj.createSubclass(DvtDomXmlNode, DvtXmlNode, "DvtDomXmlNode");

/**
 * @override
 */
DvtDomXmlNode.prototype.getAttr = function(attr) {
  return this._node.getAttribute(attr);
}

/**
 * @override
 */
DvtDomXmlNode.prototype.getChildNodes = function() {
  var ret = new Array();
  
  // Loop through the children of this node.  
  // Note: Using nextSibling because w3schools.com says it's faster than 
  //       childNodes for looping through the children.
  var child = this._node.firstChild;
  while(child != null) {
    if(child.nodeType == 1) // Element
      ret.push(new DvtDomXmlNode(child));
      
    child = child.nextSibling;  
  }
  
  return ret;
}

/**
  * @override
  */
DvtDomXmlNode.prototype.getName = function() {
  return this._node.nodeName;
};

/**
  * @override
  */
DvtDomXmlNode.prototype.getAttributes = function() {
  var array = [];
  var numAttrs = this._node.attributes.length;
  for (var i = 0; i < numAttrs; i++)
  {
    var attr = this._node.attributes[i];
    if (attr.name && typeof attr.value != 'undefined' && attr.value != null )
    {
      array.push({name:attr.name, value:attr.value});
    }
  }
  return array;
};


/**
 * @override
 * @return {array} an Array containing all child elements and text nodes of this node.
 */
DvtDomXmlNode.prototype.getAllChildNodes = function() {
  var ret = new Array();
  
  // Loop through the children of this node.  
  // Note: Using nextSibling because w3schools.com says it's faster than 
  //       childNodes for looping through the children.
  var child = this._node.firstChild;
  while(child != null) {
    ret.push(new DvtDomXmlNode(child));
    child = child.nextSibling;  
  }
  
  return ret;
}


/**
  * @override
  */
DvtDomXmlNode.prototype.getNodeValue = function() {
  return this._node.nodeValue;
};

/**
  * @override
  */
DvtDomXmlNode.prototype.getTextContent = function() {
  return this._node.textContent;
};

/**
  * @override
  */
DvtDomXmlNode.prototype.getElementsByTagName = function(name) {
  var ret = [];
  var nodes = this._node.getElementsByTagName(name);
  if (nodes) {
    for (var i = 0; i < nodes.length; i++) {
        ret.push(new DvtDomXmlNode(nodes[i]));
    }
  }
  return ret;
};

// Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
/**
 * Document APIs.
 * @class DvtDocumentUtils
 */
var DvtDocumentUtils = function(){};

DvtObj.createSubclass(DvtDocumentUtils, DvtObj, "DvtDocumentUtils");

// TODO JSDoc
// Return displayable
DvtDocumentUtils.elementFromTouch = function(touchEvent) {
    return null;
};
/**
 * A component level selection event.
 * @param {array} selection The array of currently selected ids for the component.
 * @class
 * @constructor
 * @export
 */
var DvtGraphSelectionEvent = function(selection) {
  this.Init(selection);
}

DvtObj.createSubclass(DvtGraphSelectionEvent, DvtBaseComponentEvent, "DvtGraphSelectionEvent");

/**
 * @export
 */
DvtGraphSelectionEvent.TYPE = "selection";

/**
 * @param {array} selection The array of currently selected ids for the component.
 * @override
 */
DvtGraphSelectionEvent.prototype.Init = function(selection) {
  DvtGraphSelectionEvent.superclass.Init.call(this, DvtGraphSelectionEvent.TYPE);
  this._selection = selection;
};

/**
 * Returns the array of currently selected ids for the component.
 * @return {array} The array of currently selected ids for the component.
 * @export
 */
DvtGraphSelectionEvent.prototype.getSelection = function() {
  return this._selection;
}
/**
 * A component level value change event.
 * @param {object} oldValue The old value.
 * @param {object} newValue The old value.
 * @param {boolean} bComplete True if the value change interaction is complete.
 * @class
 * @constructor
 * @export
 */
var DvtValueChangeEvent = function(oldValue, newValue, bComplete) {
  var type = (bComplete === false) ? DvtValueChangeEvent.TYPE_INPUT : DvtValueChangeEvent.TYPE;
  this.Init(type);
  this._oldValue = oldValue;
  this._newValue = newValue;
}

DvtObj.createSubclass(DvtValueChangeEvent, DvtBaseComponentEvent, "DvtValueChangeEvent");

/**
 * @export
 * Event type for completion of a value change interaction.
 */
DvtValueChangeEvent.TYPE = "valueChange";

/**
 * @export
 * Event type for an intermediate value change interaction.
 */
DvtValueChangeEvent.TYPE_INPUT = "valueChangeInput";

/**
 * Returns the old value.
 * @return {object} The old value.
 * @export
 */
DvtValueChangeEvent.prototype.getOldValue = function() {
  return this._oldValue;
}

/**
 * Returns the new value.
 * @return {object} The new value.
 * @export
 */
DvtValueChangeEvent.prototype.getNewValue = function() {
  return this._newValue;
}
/**
 * A component level selection event.
 * @param {object} context Component specific context about this event, such as the clientRowKey.
 * @param {DvtGraphShowPopupBehavior} showPopupBehavior The showPopupBehavior that was triggered.
 * @param {DvtRectangle} launcherBounds The bounding box of the object that triggered the popup, 
 *                                      relative to the component's top left coordinates.
 * @param {string} launcherId The component that launched the popup.  This may be different
 *                            than the component that owns the showPopupBehavior.
 *                                      
 * @class
 * @constructor
 * @export
 */
var DvtGraphShowPopupEvent = function(showPopupBehavior, launcherBounds, launcherId) {
  this.Init(DvtGraphShowPopupEvent.TYPE);
  this._showPopupBehavior = showPopupBehavior;
  this._launcherBounds = launcherBounds;
  this._launcherId = launcherId;
}

DvtObj.createSubclass(DvtGraphShowPopupEvent, DvtBaseComponentEvent, "DvtGraphShowPopupEvent");

/**
 * @export
 */
DvtGraphShowPopupEvent.TYPE = "showPopup";

/**
 * Returns the showPopupBehavior that was triggered.
 * @return {DvtGraphShowPopupBehavior} The showPopupBehavior that was triggered.
 * @export
 */
DvtGraphShowPopupEvent.prototype.getShowPopupBehavior = function() {
  return this._showPopupBehavior;
}

/**
 * Returns the bounding box of the object that triggered the popup, relative to the component's
 * top left coordinates.
 * @return {DvtRectangle} The bounding box of the object that triggered the popup, relative 
 *                        to the component's top left coordinates.
 * @export
 */
DvtGraphShowPopupEvent.prototype.getLauncherBounds = function() {
  return this._launcherBounds;
}

/**
 * Returns the component that launched the popup.  This may be different than the component 
 * that owns the showPopupBehavior.
 * @return {string} The component that launched the popup.
 * @export
 */
DvtGraphShowPopupEvent.prototype.getLauncherId = function() {
  return this._launcherId;
}

/**
 * Set the component that launched the popup.  This may be different than the component 
 * that owns the showPopupBehavior.
 * @param {string} The component that launched the popup.
 * @export
 */
DvtGraphShowPopupEvent.prototype.setLauncherId = function(launcherId) {
  this._launcherId = launcherId;
}
/**
 * A component level hide popup event.
 * @param {string} popupId The id of the popup to be hidden.
 * @param {string} clientId The id of the naming container owning the showPopupBehavior.
 * @class
 * @constructor
 * @export
 */
var DvtGraphHidePopupEvent = function(popupId) {
  this.Init(DvtGraphHidePopupEvent.TYPE);
  this._popupId = popupId;
}

DvtObj.createSubclass(DvtGraphHidePopupEvent, DvtBaseComponentEvent, "DvtGraphHidePopupEvent");

/**
 * @export
 */
DvtGraphHidePopupEvent.TYPE = "hidePopup";

/**
 * Returns the id of the popup to be hidden.
 * @return {string} popupId The id of the popup to be hidden.
 * @export
 */
DvtGraphHidePopupEvent.prototype.getPopupId = function() {
  return this._popupId;
}
/**
 * A component client behavior event.
 * @param {DvtClientBehavior} clientHeavior The clientBehavior that was triggered.
 *                                      
 * @class
 * @constructor
 * @export
 */
var DvtClientBehaviorEvent = function(clientBehavior) {
  this.Init(DvtClientBehaviorEvent.TYPE);
  this._clientBehavior = clientBehavior;
}

DvtObj.createSubclass(DvtClientBehaviorEvent, DvtBaseComponentEvent, "DvtClientBehaviorEvent");

/**
 * @export
 */
DvtClientBehaviorEvent.TYPE = "clientBehavior";

/**
 * Returns the clientBehavior that was triggered.
 * @return {DvtClientBehavior} The clientBehavior that was triggered.
 * @export
 */
DvtClientBehaviorEvent.prototype.getClientBehavior = function() {
  return this._clientBehavior;
}
/**
 * Event that is fired when interacting with a component.  This event provides context about the objects that were the
 * target of the interaction.
 * @param {string} eventType
 * @param {object} params A component specific object map describing the target of the event.
 * @class
 * @constructor
 * @export
 */
var DvtComponentUIEvent = function(eventType, params) {
  DvtComponentUIEvent.superclass.Init.call(this, eventType);
  
  /**
   * The object describing the target of the event.
   * @type {object}
   */
  this['params'] = params ? params : {};
}

DvtObj.createSubclass(DvtComponentUIEvent, DvtBaseComponentEvent, "DvtComponentUIEvent");

/**
 * @const
 * @export
 */
DvtComponentUIEvent.TYPE_CLICK = "click";

/**
 * @const
 * @export
 */
DvtComponentUIEvent.TYPE_ROLL_OUT = "rollOut";

/**
 * @const
 * @export
 */
DvtComponentUIEvent.TYPE_ROLL_OVER = "rollOver";
/**
 * A component level context menu event.
 * @param {object} nativeEvent The native event that triggered the context menu.
 * @param {string} menuId The client id of the context menu that is being requested.
 * @class
 * @constructor
 * @export
 */
var DvtGraphContextMenuEvent = function(nativeEvent, menuId) {
  this.Init(DvtGraphContextMenuEvent.TYPE);
  this._nativeEvent = nativeEvent;
  this._menuId = menuId;
}

DvtObj.createSubclass(DvtGraphContextMenuEvent, DvtBaseComponentEvent, "DvtGraphContextMenuEvent");

/**
 * @export
 */
DvtGraphContextMenuEvent.TYPE = "contextMenu";

/**
 * Returns the native event that triggered the context menu.
 * @return {object} The native event that triggered the context menu.
 * @export
 */
DvtGraphContextMenuEvent.prototype.getNativeEvent = function() {
  return this._nativeEvent;
}

/**
 * Returns the client id of the context menu that is being requested.
 * @return {string} The client id of the context menu that is being requested.
 * @export
 */
DvtGraphContextMenuEvent.prototype.getMenuId = function() {
  return this._menuId;
}
/**
 * A component level event for selection of a context menu item.
 * @param {string} menuType The type of context menu being requested, such as bodyContextMenu.
 * @param {string} clientId The id for a commandMenuItem.
 * @param {string} destination The destination for a goMenuItem.
 * @param {string} targetFrame The targetFrame for a goMenuItem.
 * @class
 * @constructor
 * @export
 */
var DvtGraphContextMenuSelectEvent = function(menuType, clientId, destination, targetFrame) {
  this.Init(DvtGraphContextMenuSelectEvent.TYPE);
  this._menuType = menuType;
  this._clientId = clientId;
  this._destination = destination;
  this._targetFrame = targetFrame;
}

DvtObj.createSubclass(DvtGraphContextMenuSelectEvent, DvtBaseComponentEvent, "DvtGraphContextMenuSelectEvent");

/**
 * @export
 */
DvtGraphContextMenuSelectEvent.TYPE = "contextMenuSelect";

/**
 * Returns the type of context menu being requested, such as bodyContextMenu.
 * @return {string} The type of context menu being requested, such as bodyContextMenu.
 * @export
 */
DvtGraphContextMenuSelectEvent.prototype.getMenuType = function() {
  return this._menuType;
}

/**
 * Returns the id for a commandMenuItem.
 * @return {string} The id for a commandMenuItem.
 * @export
 */
DvtGraphContextMenuSelectEvent.prototype.getClientId = function() {
  return this._clientId;
}

/**
 * Returns the destination for a goMenuItem.
 * @return {string} The destination for a goMenuItem.
 * @export
 */
DvtGraphContextMenuSelectEvent.prototype.getDestination = function() {
  return this._destination;
}

/**
 * Returns the targetFrame for a goMenuItem.
 * @return {string} The targetFrame for a goMenuItem.
 * @export
 */
DvtGraphContextMenuSelectEvent.prototype.getTargetFrame = function() {
  return this._targetFrame;
}
/**
 * A component level drill replace event.
 * @param {string} id The id of the data item that was drilled.
 * @class
 * @constructor
 * @export
 */
var DvtDrillReplaceEvent = function(id) {
  this.Init(DvtDrillReplaceEvent.TYPE);
  this._id = id;
}

DvtObj.createSubclass(DvtDrillReplaceEvent, DvtBaseComponentEvent, "DvtDrillReplaceEvent");

/**
 * @export
 */
DvtDrillReplaceEvent.TYPE = "drillReplace";

/**
 * Returns the id of the data item that was drilled.
 * @return {string} The id of the data item that was drilled.
 * @export
 */
DvtDrillReplaceEvent.prototype.getId = function() {
  return this._id;
}
/**
 * @constructor
 * A component level rollover behavior event.
 * @type {string} type The type of event, either roll over or roll out.
 * @type {string} category The category that is the target of the event.
 * @class
 * @constructor
 * @extends DvtBaseComponentEvent
 * @export
 */
var DvtCategoryRolloverEvent = function(type, category) {
  this.Init(type);
  this._category = category;
}

DvtObj.createSubclass(DvtCategoryRolloverEvent, DvtBaseComponentEvent, "DvtCategoryRolloverEvent");

/**
 * @export
 */
DvtCategoryRolloverEvent.TYPE_OVER = "categoryRollOver";

/**
 * @export
 */
DvtCategoryRolloverEvent.TYPE_OUT = "categoryRollOut";

/**
 * Returns the category that is target of the event.
 * @return {string}
 * @export
 */
DvtCategoryRolloverEvent.prototype.getCategory = function() {
  return this._category;
}
/**
 * A component level hide and show behavior event.
 * @type {string} type The type of event, either hide or show.
 * @type {string} category The category that is the target of the event.
 * @constructor
 * @class
 * @extends DvtBaseComponentEvent
 * @export
 */
var DvtCategoryHideShowEvent = function(type, category) {
  this.Init(type);
  this._category = category;
}

DvtObj.createSubclass(DvtCategoryHideShowEvent, DvtBaseComponentEvent, "DvtCategoryHideShowEvent");

/**
 * @export
 */
DvtCategoryHideShowEvent.TYPE_HIDE = "hideCategory";

/**
 * @export
 */
DvtCategoryHideShowEvent.TYPE_SHOW = "showCategory";

/**
 * Returns the category that is target of the event.
 * @return {string}
 * @export
 */
DvtCategoryHideShowEvent.prototype.getCategory = function() {
  return this._category;
}
/**
 * A pan event.
 * @param {string}  subType  subtype of the event; one of the constants 
 *        defined in this class
 * @param {number}  newX  new x-coord
 * @param {number}  newY  new y-coord
 * @param {number}  oldX  old x-coord
 * @param {number}  oldY  old y-coord
 * @param {DvtAnimator}  animator  optional animator used to animate the zoom
 * @class
 * @constructor
 */
var DvtPanEvent = function(subType, newX, newY, oldX, oldY, animator) {
  this.Init(DvtPanEvent.TYPE);
  this.type = this.getType();
  this._subtype = subType;
  this._newX = newX;
  this._newY = newY;
  this._oldX = oldX;
  this._oldY = oldY;
  this._animator = animator;
};

DvtObj.createSubclass(DvtPanEvent, DvtBaseComponentEvent, "DvtPanEvent");

DvtPanEvent.TYPE = "dvtPan";

DvtPanEvent.SUBTYPE_DRAG_PAN_BEGIN = "dragPanBegin";
DvtPanEvent.SUBTYPE_DRAG_PAN_END = "dragPanEnd";
DvtPanEvent.SUBTYPE_PANNED = "panned";
DvtPanEvent.SUBTYPE_PANNING = "panning";
DvtPanEvent.SUBTYPE_ELASTIC_ANIM_BEGIN = "elasticAnimBegin";
DvtPanEvent.SUBTYPE_ELASTIC_ANIM_END = "elasticAnimEnd";

/**
 * 
 */
DvtPanEvent.prototype.getSubType = function() {
  return this._subtype;
};

/**
 * 
 */
DvtPanEvent.prototype.getNewX = function() {
  return this._newX;
};

/**
 * 
 */
DvtPanEvent.prototype.getNewY = function() {
  return this._newY;
};

/**
 * 
 */
DvtPanEvent.prototype.getOldX = function() {
  return this._oldX;
};

/**
 * 
 */
DvtPanEvent.prototype.getOldY = function() {
  return this._oldY;
};

/**
 * 
 */
DvtPanEvent.prototype.getAnimator = function() {
  return this._animator;
};
/**
 * A zoom event.
 * @param {string}  subType  subtype of the event; one of the constants 
 *        defined in this class
 * @param {number}  newZoom  new zoom factor
 * @param {number}  oldZoom  old zoom factor
 * @param {DvtAnimator}  animator  optional animator used to animate the zoom
 * @param {DvtRectangle}  zoomToFitBounds  bounds to use for zoom-to-fit
 * @param {DvtPoint}  centerPoint  center of zoom
 * @param {number}  tx  the horizontal translation applied after the zoom
 * @param {number}  ty  the vertical translation applied after the zoom
 * @class
 * @constructor
 */
var DvtZoomEvent = function(subType, newZoom, oldZoom, animator, zoomToFitBounds, centerPoint, tx, ty) {
  this.Init(DvtZoomEvent.TYPE);
  this.type = this.getType();
  this._subtype = subType;
  this._newZoom = newZoom;
  this._oldZoom = oldZoom;
  this._animator = animator;
  this._zoomToFitBounds = zoomToFitBounds;
  this._centerPoint = centerPoint;
  this._tx = tx;
  this._ty = ty;
};

DvtObj.createSubclass(DvtZoomEvent, DvtBaseComponentEvent, "DvtZoomEvent");

DvtZoomEvent.TYPE = "dvtZoom";

/**
 * Zoomed event - component might need to rerender on this event
 * @const
 */
DvtZoomEvent.SUBTYPE_ZOOMED = "zoomed";
/**
 * Zooming event - used to notify a component of zooming event 
 * @const
 */
DvtZoomEvent.SUBTYPE_ZOOMING = "zooming";
/**
 * ZoomEnd event - it is the end of the zoom event. The difference between "zoomed" and "zoomEnd" is on touch device.
 * A component gets "zoomed" events for appropriate "touchmove" events. When all touches are released on "touchend"  the component will get "zoomEnd" notification.
 * @const
 */
DvtZoomEvent.SUBTYPE_ZOOM_END = "zoomEnd";
DvtZoomEvent.SUBTYPE_DRAG_ZOOM_BEGIN = "dragZoomBegin";
DvtZoomEvent.SUBTYPE_DRAG_ZOOM_END = "dragZoomEnd";
DvtZoomEvent.SUBTYPE_ZOOM_AND_CENTER = "zoomAndCenter";
DvtZoomEvent.SUBTYPE_ZOOM_TO_FIT_CALC_BOUNDS = "zoomToFitCalcBounds";
DvtZoomEvent.SUBTYPE_ZOOM_TO_FIT_BEGIN = "zoomToFitBegin";
DvtZoomEvent.SUBTYPE_ZOOM_TO_FIT_END = "zoomToFitEnd";
DvtZoomEvent.SUBTYPE_ELASTIC_ANIM_BEGIN = "elasticAnimBegin";
DvtZoomEvent.SUBTYPE_ELASTIC_ANIM_END = "elasticAnimEnd";

/**
 * Subtype representing a zoom event fired before the SUBTYPE_ZOOMING event to give components a chance adjust the
 * pan constraints
 * 
 * @const
 */
DvtZoomEvent.SUBTYPE_ADJUST_PAN_CONSTRAINTS = "adjustPanConstraints";

/**
 * 
 */
DvtZoomEvent.prototype.getSubType = function() {
  return this._subtype;
};

/**
 * 
 */
DvtZoomEvent.prototype.getNewZoom = function() {
  return this._newZoom;
};

/**
 * 
 */
DvtZoomEvent.prototype.getOldZoom = function() {
  return this._oldZoom;
};

/**
 * 
 */
DvtZoomEvent.prototype.getAnimator = function() {
  return this._animator;
};

/**
 * 
 */
DvtZoomEvent.prototype.setZoomToFitBounds = function(bounds) {
  this._zoomToFitBounds = bounds;
};

/**
 * 
 */
DvtZoomEvent.prototype.getZoomToFitBounds = function() {
  return this._zoomToFitBounds;
};

/**
 * 
 */
DvtZoomEvent.prototype.getCenterPoint = function() {
  return this._centerPoint;
};

/**
 * Gets the horizontal translation applied after the zoom
 * 
 * @return {number} the horizontal translation applied after the zoom
 */
DvtZoomEvent.prototype.getTx = function() {
  return this._tx;
}

/**
 * Gets the vertical translation applied after the zoom
 * 
 * @return {number} the vertical translation applied after the zoom
 */
DvtZoomEvent.prototype.getTy = function() {
  return this._ty;
}
/**
 * An action event.
 * ActionEvent is generated by a AfCommandButton, AfCommandLink,  AfCommandImageLink or
 * AfCommandMenuItem when it is clicked
 * @param {string}  commandId
 * @param {string}  clientId
 * @class
 * @constructor
 * @export
 */
var DvtGraphActionEvent = function(actionEventType, commandId, clientId) {
  this.Init(DvtGraphActionEvent.TYPE);
  this.type = this.getType();
  this._subtype = actionEventType;
  this._commandId = commandId;
  this._clientId = clientId;
};

DvtObj.createSubclass(DvtGraphActionEvent, DvtBaseComponentEvent, "DvtGraphActionEvent");

/**
 * @export
 */
DvtGraphActionEvent.TYPE = "dvtAct";

/**
 * @export
 */
DvtGraphActionEvent.SUBTYPE_ACTION = "action";
//DvtGraphActionEvent.SUBTYPE_NAVIGATE_UP = "navigateUp";
DvtGraphActionEvent.SUBTYPE_SET_ANCHOR = "setAnchor";


DvtGraphActionEvent.prototype.getSubType = function() {
  return this._subtype;
};

DvtGraphActionEvent.prototype.getCommandId = function() {
  return this._commandId;
};

/**
 * @export
 */
DvtGraphActionEvent.prototype.getClientId = function() {
  return this._clientId;
};

DvtGraphActionEvent.prototype.setClientId = function(clientId) {
  this._clientId = clientId;
};

// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
var DvtEventDispatcher = {};

DvtObj.createSubclass(DvtEventDispatcher, DvtObj, "DvtEventDispatcher");

/**
 * Dispatches the event to the callback function.
 * @param {object} callback The function that should be called to dispatch component events.
 * @param {object} callbackObj The object context for the callback function
 * @param {object} component The component firing the event
 * @param {object} event The event to be dispatched.
 * @protected
 */
DvtEventDispatcher.dispatchEvent = function(callback, callbackObj, component, event) {
  if (!callback)
    return;
  else if (callback && callback.call) 
    callback.call(callbackObj, event, component);
  else {
    // THIS CODE PATH WILL NOT WORK AFTER JS OBFUSCATION!!!

    // Create the string to be called
    var result = callback + "(event, component)";
    
    // Evaluate the javascript string.  The callback is defined by the peer,
    // and never is the result of user input, so there is no security hole
    // in using eval here.
    eval(result);
  }
};
/**
 * A component level isolate event.
 * @param {array} isolatedRows The array of currently disclosed ids for the component.
 * @class
 * @constructor
 * @export
 */
var DvtIsolateEvent = function(isolatedRows) {
  this.Init(DvtIsolateEvent.TYPE);
  this._isolatedRows = isolatedRows;
}

DvtObj.createSubclass(DvtIsolateEvent, DvtBaseComponentEvent, "DvtIsolateEvent");

/**
 * @export
 */
DvtIsolateEvent.TYPE = "isolate";

/**
 * Returns the array of currently isolated ids for the component.
 * @return {array} The array of currently isolated ids for the component.
 * @export
 */
DvtIsolateEvent.prototype.getIsolatedRows = function() {
  return this._isolatedRows;
}
/**
 * A component level log event.
 * @param {string} logType the type of this log event: either DvtLogEvent.PERFORMANCE or DvtLogEvent.LOG
 * @param {int} level the level of this log event
 * @param {string} message the message to be logged
 * @class
 * @constructor
 * @export
 */
var DvtLogEvent = function(logType, level, message) {
  this.Init(DvtLogEvent.TYPE);
  this._logType = logType;
  this._level = level;
  this._message = message;
}

DvtObj.createSubclass(DvtLogEvent, DvtBaseComponentEvent, "DvtLogEvent");

/**
 * @export
 */
DvtLogEvent.TYPE = "log";

/**
 * @export
 */
DvtLogEvent.PERFORMANCE = "performance";

/**
 * @export
 */
DvtLogEvent.LOG = "log";


/**
 * Returns the type of this log event: either DvtLogEvent.PERFORMANCE or DvtLogEvent.LOG
 * @return {string} the type of this log event
 * @export
 */
DvtLogEvent.prototype.getLogType = function() {
  return this._logType;
}

/**
 * Returns the level of this log event
 * @return {int} the level of this log event
 * @export
 */
DvtLogEvent.prototype.getLevel = function() {
  return this._level;
}

/**
 * Returns the message to be logged
 * @return {string} the message to be logged
 * @export
 */
DvtLogEvent.prototype.getMessage = function() {
  return this._messaage;
}
/**
 * A component level set property event.
 * @class
 * @constructor
 * @export
 */
var DvtSetPropertyEvent = function() {
  this.Init(DvtSetPropertyEvent.TYPE);
}

DvtObj.createSubclass(DvtSetPropertyEvent, DvtBaseComponentEvent, "DvtSetPropertyEvent");

/**
 * @export
 */
DvtSetPropertyEvent.TYPE = "dvtSetPropertyEvent";
/**
 * @constructor
 * @export
 */
var DvtActiveElementChangeEvent = function(subtype, id) {
  this.Init(DvtActiveElementChangeEvent.TYPE);
  this._subtype = subtype;
  this._id = id;
}

DvtObj.createSubclass(DvtActiveElementChangeEvent, DvtBaseComponentEvent, "DvtActiveElementChangeEvent");

/**
 * @export
 */
DvtActiveElementChangeEvent.TYPE = "activeElementChangeEvent";

/**
 * @export
 */
DvtActiveElementChangeEvent.CHANGE = "change";

/**
 * @export
 */
DvtActiveElementChangeEvent.prototype.getId = function() {
  return this._id;
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*   DvtFlashContext         Flash implementation of DvtContext                */
/*-------------------------------------------------------------------------*/
/**
  *  @extends DvtContext
  *  @class DvtFlashContext
  *  @constructor
  *  @param {root}  
  *  @param {id}  
  */
var  DvtFlashContext = function(implFactory, root, id)
{
  this.Init(implFactory, root, id);
};

DvtObj.createSubclass(DvtFlashContext, DvtContext, "DvtFlashContext");
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/**
 * Document APIs.
 * @class DvtFlashDocumentUtils
 */
var DvtFlashDocumentUtils = function(){};

DvtObj.createSubclass(DvtFlashDocumentUtils, DvtDocumentUtils, "DvtFlashDocumentUtils");

// TODO JSDoc
// Return displayable
DvtFlashDocumentUtils.elementFromPoint = function(posX, posY) {
  if (DvtFlashDocumentUtils.stage) {
      var objects = DvtFlashDocumentUtils.stage.getObjectsUnderPoint(DvtFlashDocumentUtils.context.getImplFactory().newPoint(posX, posY));
      if (objects && objects.length > 0) {
          var domObj = objects[objects.length - 1];
          while (domObj) {
            if (domObj.hasOwnProperty("_obj")) {
                if (domObj._obj && domObj.getObj && domObj.getObj())
                    return domObj.getObj();
            }
            domObj = domObj.parent;
          }
      }
  }
  return null;
};

// TODO JSDoc
// Return coordinates relative to stage
DvtFlashDocumentUtils.getStageRelativePosition = function(stage, x, y) {
  var pos = DvtFlashDocumentUtils.getStageAbsolutePosition(stage);
  var xPos = x - pos.x;
  var yPos = y - pos.y;
  return new DvtPoint(xPos, yPos);
}

// TODO JSDoc
// Return absolute coordinates of stage
DvtFlashDocumentUtils.getStageAbsolutePosition = function(stage) {
  return new DvtPoint(0, 0);
}

// TODO JSDoc
// Return whether or not filters are supported
DvtFlashDocumentUtils.isFilterSupported = function(stage) {
    return false;
}

DvtFlashDocumentUtils.isStrokeTimeoutRequired = function() {
    return false;
}

/*
 * Whether or not events in SVG are received on regions that are visually clipped such that they are not visible
 */
DvtFlashDocumentUtils.isEventAvailableOutsideClip = function() {
    return false;
}

/*
 * Bug #12805402: In Firefox 4, horizontal/vertical polylines must be bent by 0.01 pixes to show up when fitlers are used
 */
DvtFlashDocumentUtils.isFilterStraightLineAdjustmentNeeded = function() {
    return false;
}

/**
 * Cancel a DOM event.
 * 
 * @param {object}  e  event object to cancel
 */
DvtFlashDocumentUtils.cancelDomEvent = function(e)
{

}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*   DvtSvgContext         SVG implementation of DvtContext                */
/*-------------------------------------------------------------------------*/
/**
  *  @extends DvtContext
  *  @class DvtSvgContext
  *  @constructor
  *  @param {root}  
  *  @param {id}
  *  @export
  */
var  DvtSvgContext = function(root, id)
{
  // Create the impl factory, defs, and stage

  this._implFactory = new DvtSvgImplFactory(this) ;
  this._defs = this._implFactory.newDefs();
  this._root = root ;
  DvtSvgUtils.appendChildElem(root, this._defs);

  // Create the tooltip manager

  // Pass to the superclass

  this.Init(this._implFactory, root, id);

  DvtSvgUtils.appendChildElem(root, this._stage.getImpl().getElem());
  
  //create the shared DvtText to use for measuring text
  if (DvtSvgContext._USE_MEASURE_TEXT) {
    this._dvtMeasureText = new DvtText(this, "", 0, 0);
    this._dvtMeasureText.setMouseEnabled(false);
    this._dvtMeasureText.setAlpha(0);
    this._stage.addChild(this._dvtMeasureText);
  }
};

DvtObj.createSubclass(DvtSvgContext, DvtContext, "DvtSvgContext");

//flag to toggle whether measuring text using a single, dedicated DvtText is allowed
DvtSvgContext._USE_MEASURE_TEXT = true;

/**
 * Append element(s) to the application global <defs> element.
 */
DvtSvgContext.prototype.appendDefs = function(elem)
{
  DvtSvgUtils.appendChildElem(this._defs, elem) ;
};


/**
  *   @returns {DOM_Element}  the global <defs> DOM element
  */
DvtSvgContext.prototype.getDefs = function()
{
  return this._defs ;
};

/**
  *   @returns {DvtSvgImplFactory}  the platform implementation factory.
  */
DvtSvgContext.prototype.getImplFactory = function()
{
  return this._implFactory ;
};


/**
  *   Removes the specified element from the global <defs> element.
  */
DvtSvgContext.prototype.removeDefs = function(elem)
{
  this._defs.removeChild(elem) ;
};

/**
  *   Removes the element with the specified id from the global <defs> element.
  */
DvtSvgContext.prototype.removeDefsById = function(id)
{
  var defs = this._defs.childNodes;
  var len = defs.length;
  for (var i=0; i<len; i++) {
    var def = defs[i];
    if (def.id === id) {
      this._defs.removeChild(def);
      return;
    }
  }
};

//BUG FIX 12566921.
/**  Chrome (as at version 12.0.742.91 6/12/11) honors the dir="rtl"
  *  on the top <html> for the page and causes text alignment rendering
  *  issues with the current MT generated XML (because the (x,y) are
  *  assumed to pertain to the right hand side) ;  IE9, Firefox and
  *  Safari currently do not. This fix overrides this locally for the
  *  svg element, and isolates the fix to the Dvt toolkit for BiDi only
  *  to avoid any undesirable external consequences in Adf.
  */
DvtSvgContext.prototype.fixBrowserTextRTL = function()
{
  // Bug 17776065: When html dir="rtl", Webkit and FF25+ treat the right side of the start as the start, and the left 
  // side of the text as end.  Note that the isTouchDevice check here doesn't make sense, but leaving it to minimize
  // risk because things appear to be working correctly.  This entire function will be removed and moved to graph once
  // the toolkit is refactored.
  if (!DvtAgent.isTouchDevice() && (DvtAgent.isPlatformWebkit() || (DvtAgent.isPlatformGecko() && DvtAgent.getVersion() >= 25)))
    DvtSvgUtils.setAttrNullNS(this._root, "text-anchor", "end");
  else{
    DvtSvgUtils.setAttrNullNS(this._root, "text-anchor", "start");
  }
};

DvtSvgContext.prototype.removeClipPath = function (id) {
  DvtSvgContext.superclass.removeClipPath.call(this, id);
  // remove clip path from the global defs if not used
  if (!this.hasClipPath(id))
    this.removeDefsById(id);
}

/**
 * @override
 */
DvtSvgContext.prototype.supportsMeasureText = function() {
  if (DvtSvgContext._USE_MEASURE_TEXT && this._dvtMeasureText) {
    return true;
  }
  return false;
};


/**
 * @override
 */
DvtSvgContext.prototype.measureText = function(textString, cssStyle, horizAlign, vertAlign) {
  if (this.supportsMeasureText()) {
    if (this._dvtMeasureText) {
      if (cssStyle) {
        if (!this._lastCssStyle|| 
            (this._lastCssStyle.getStyle(DvtCSSStyle.FONT_STYLE) != cssStyle.getStyle(DvtCSSStyle.FONT_STYLE) ||
             this._lastCssStyle.getStyle(DvtCSSStyle.FONT_WEIGHT) != cssStyle.getStyle(DvtCSSStyle.FONT_WEIGHT) ||
             this._lastCssStyle.getStyle(DvtCSSStyle.FONT_SIZE) != cssStyle.getStyle(DvtCSSStyle.FONT_SIZE) ||
             this._lastCssStyle.getStyle(DvtCSSStyle.FONT_FAMILY) != cssStyle.getStyle(DvtCSSStyle.FONT_FAMILY))) {
          this._dvtMeasureText.getImpl().setCSSStyle(cssStyle);
          this._lastCssStyle = cssStyle;
        }
      }
      
      // if horizAlign not provided, use the default alignment (start)
      if (!horizAlign)
        horizAlign = DvtText.H_ALIGN_DEFAULT;
      if (!this._lastHorizAlign || this._lastHorizAlign != horizAlign) {
        this._dvtMeasureText.setHorizAlignment(horizAlign);
        this._lastHorizAlign = horizAlign;
      }
      
      // if vertAlign not provided, use the default alignment (top)
      if (!vertAlign)
        vertAlign = DvtText.V_ALIGN_DEFAULT;
      if (!this._lastVertAlign || this._lastVertAlign != vertAlign) {
        this._dvtMeasureText.setVertAlignment(vertAlign);
        this._lastVertAlign = vertAlign;
      }
        
      this._dvtMeasureText.setTextString(textString);
      return this._dvtMeasureText.getDimensions();
    }
  }
  return null;
};

/**
 * Clears all unique seeds used for filter, clip path, gradient, and af component id generation.
 * Should only be called by Junit tests.
 * @export
 */
DvtSvgContext.resetUniqueSeeds = function () {
  if (typeof DvtAfComponent != 'undefined')
    DvtAfComponent._uniqueSeed = 0;
  DvtSvgFilterUtils._counter = 0;
  DvtGraphClipPath._uniqueSeed = 0;
  DvtSvgShapeUtils._uniqueSeed = 0;
}
// Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*  DvtSvgUtils()                                                          */
/*-------------------------------------------------------------------------*/

// SVG Utilities for Drawing

/**
 * @class
 * @export
 */
var DvtSvgUtils = new Object();

DvtObj.createSubclass(DvtSvgUtils, DvtObj, "DvtSvgUtils");

DvtSvgUtils.SVG_NS   = "http://www.w3.org/2000/svg";
DvtSvgUtils.XLINK_NS = "http://www.w3.org/1999/xlink";

/**
 * Creates and returns a new SVG document with the specified id.
 * @param {string} id The id for the new SVG document.
 * @return A new SVG document.
 * @export
 */
DvtSvgUtils.createSvgDocument = function(id) {
  var svg = document.createElementNS(DvtSvgUtils.SVG_NS, "svg");
  DvtSvgUtils.setAttrNullNS(svg, "id", id);
  DvtSvgUtils.setAttrNullNS(svg, "width", "100%");
  DvtSvgUtils.setAttrNullNS(svg, "height", "100%");
  return svg;
}

/**
 * Returns the drag feedback for the specified objects.
 * @param {object} displayables The DvtDisplayable or array of DvtDisplayables to display for drag feedback.
 * @return {object} An object containing drag feedback information.
 */
DvtSvgUtils.getDragFeedback = function (displayables, targetCoordinateSpace) { 
  // Wrap in an array if not already wrapped.
  if (!(displayables instanceof Array)) {
    displayables = [displayables];
  }
  var bounds = DvtDnDUtils.getDragFeedbackBounds(displayables, targetCoordinateSpace);
  var svg = DvtSvgUtils._getDragFeedbackSVG(displayables, bounds);

  var feedback = new Object();
  feedback.width = bounds.w + DvtDragSource.DRAG_FEEDBACK_MARGIN * 2;
  feedback.height = bounds.h + DvtDragSource.DRAG_FEEDBACK_MARGIN * 2;
  if (svg)
    feedback.svg = svg;

  return feedback;
}

/**
 * Returns an svg string representing the drag over feedback.
 * @param {object} displayables The DvtDisplayable or array of DvtDisplayables to display for drag feedback.
 * @param {DvtRectangle} bounds The bounds for the drag feedback displayables, used to translate the feedback to 0.
 * @private
 */
DvtSvgUtils._getDragFeedbackSVG = function(displayables, bounds) {
  /// BUG #12805155: A new svg document must be created programmatically and appended to the wrapping div later on.
  //  This is necessary for Safari 5.0 and earlier, as well as iOS.  innerHTML should not be used.
  var svgElem = DvtSvgUtils.createSvgDocument("dnd");
  
  // Create a group element to apply the top level translate to show the drag feedback at (0, 0)
  var tx = DvtDragSource.DRAG_FEEDBACK_MARGIN - bounds.x;
  var ty = DvtDragSource.DRAG_FEEDBACK_MARGIN - bounds.y;
  var translate = "translate(" + tx + "," + ty + ")";
  var container = DvtSvgShapeUtils.createElement("g");
  DvtSvgUtils.setAttrNullNS(container, 'transform', translate) ;
  DvtSvgUtils.appendChildElem(svgElem, container);
  
  // Loop through and add the displayables
  for (var i = 0;i < displayables.length;i++) {
    
    var src = displayables[i];
    // Clone the selected node and add
    var elem = src.getImpl().getElem();
    var clone = elem.cloneNode(true);
    
    // Remove filters if present, since these effects overflow the bounds and look broken
    if(DvtSvgUtils.getAttrNullNS(clone, 'filter'))
      DvtSvgUtils.setAttrNullNS(clone, 'filter', null);
    
    var cloneContainer = DvtSvgShapeUtils.createElement("g");
    DvtSvgUtils.appendChildElem(container, cloneContainer);
    var pathToStage = src.getPathToStage();
    var mat = null;
    for (var j=1; j<pathToStage.length; j++) {
      if (!mat)
        mat = pathToStage[j].getMatrix();
      else {
        mat = mat.clone();
        mat.concat(pathToStage[j].getMatrix());
      }
    }
    if (mat) {
      var sMat = 'matrix(' + mat.getA() + ',' + mat.getC() + ',' + mat.getB() + ',' + mat.getD() + ',' + mat.getTx() + ',' + mat.getTy() + ')';
      DvtSvgUtils.setAttrNullNS(cloneContainer, 'transform', sMat);
    }
    DvtSvgUtils.appendChildElem(cloneContainer, clone);
  }
  
  return svgElem;
}

/**
 * Add an event listener to a DOM element.
 * @param {object} elem DOM element
 * @param {string} type type of event
 * @param {function} listener the listener function
 * @param {boolean} useCapture true if the listener operates in the capture phase, false otherwise
 */
DvtSvgUtils.addDomEventListener = function(elem, type, listener, useCapture) {
  if (elem && elem.addEventListener) {
    elem.addEventListener(type, listener, useCapture);
  }
}

/**
 * Remove an event listener from a DOM element.
 * @param {object} elem DOM element
 * @param {string} type type of event
 * @param {function} listener the listener function
 * @param {boolean} useCapture true if the listener operates in the capture phase, false otherwise
 */
DvtSvgUtils.removeDomEventListener = function(elem, type, listener, useCapture) {
  if (elem && elem.removeEventListener) {
    elem.removeEventListener(type, listener, useCapture);
  }
}

/**
 * Wrapper for appendChild method
 * @param {object} parent DOM element
 * @param {object} child DOM element added to parent
 * @return {object} the appended element (child)
 */
DvtSvgUtils.appendChildElem = function(parent,child) {
    return parent.appendChild(child);
}

/**
 * Wrapper for getAttributeNS method
 * @param {object} elem DOM element
 * @param {String} namespace Namespace to use
 * @param {String} name Attribute name to get
 * @return {String} Value associated with given name
 */
DvtSvgUtils.getAttrNS = function(elem,namespace,name) {
    return elem.getAttributeNS(namespace,name);
}
/**
 * Wrapper for getAttributeNS method
 * @param {object} elem DOM element
 * @param {String} name Attribute name to get
 * @return {String} Value associated with given name
 */
DvtSvgUtils.getAttrNullNS = function(elem,name) {
    return elem.getAttributeNS(null, name);
}

/**
 * Wrapper for setAttributeNS method
 * @param {object} elem DOM element
 * @param {String} namespace Namespace to use
 * @param {String} name Attribute name to get
 * @param {String} value Value associated with given name
 */
DvtSvgUtils.setAttrNS = function(elem,namespace,name,value) {
    elem.setAttributeNS(namespace,name,value);
}
/**
 * Wrapper for setAttributeNS method
 * @param {object} elem DOM element
 * @param {String} name Attribute name to get
 * @param {String} value Value associated with given name
 */
DvtSvgUtils.setAttrNullNS = function(elem,name,value) {
    elem.setAttributeNS(null,name,value);
}

/**
 * Remove an attribute from a DOM element.
 * @param {object} elem DOM element
 * @param {string} namespace Namespace to use
 * @param {string} name Attribute name to remove
 */
DvtSvgUtils.removeAttrNS = function(elem, namespace, name) {
  elem.removeAttributeNS(namespace, name);
}

/**
 * Remove an attribute from a DOM element, using a null namespace.
 * @param {object} elem DOM element
 * @param {string} name Attribute name to remove
 */
DvtSvgUtils.removeAttrNullNS = function(elem, name) {
  DvtSvgUtils.removeAttrNS(elem, null, name);
}
/**
 * Utility resource bundle.
 * @class
 * @constructor
 * @extends {DvtUtilBundle}
 */
var DvtUtilBundle = function() {}

DvtObj.createSubclass(DvtUtilBundle, DvtBundle, "DvtUtilBundle");

DvtUtilBundle["_defaults"] = {
  'SCALING_SUFFIX_THOUSAND': 'K',
  'SCALING_SUFFIX_MILLION': 'M',
  'SCALING_SUFFIX_BILLION': 'B',
  'SCALING_SUFFIX_TRILLION': 'T',
  'SCALING_SUFFIX_QUADRILLION': 'Q',
  
  'MONTH_SHORT_JANUARY': 'Jan',
  'MONTH_SHORT_FEBRUARY': 'Feb',
  'MONTH_SHORT_MARCH': 'Mar',
  'MONTH_SHORT_APRIL': 'Apr',
  'MONTH_SHORT_MAY': 'May',
  'MONTH_SHORT_JUNE': 'Jun',
  'MONTH_SHORT_JULY': 'Jul',
  'MONTH_SHORT_AUGUST': 'Aug',
  'MONTH_SHORT_SEPTEMBER': 'Sep',
  'MONTH_SHORT_OCTOBER': 'Oct',
  'MONTH_SHORT_NOVEMBER': 'Nov',
  'MONTH_SHORT_DECEMBER': 'Dec',
  
  'CLEAR_SELECTION': 'Clear Selection'
}

/**
 * @override
 */
DvtUtilBundle.prototype.GetDefaultStringForKey = function(key) {
  return DvtUtilBundle["_defaults"][key];
}

/**
 * @override
 */
DvtUtilBundle.prototype.GetBundlePrefix = function() {
  return "DvtUtilBundle";
}
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/**
 * Document APIs.
 * @class DvtSvgDocumentUtils
 */
var DvtSvgDocumentUtils = function(){};

DvtObj.createSubclass(DvtSvgDocumentUtils, DvtDocumentUtils, "DvtSvgDocumentUtils");

// TODO JSDoc
// Return displayable
DvtSvgDocumentUtils.elementFromPoint = function(posX, posY) {
  var domObj = document.elementFromPoint(posX, posY); 
  while (domObj) {
    if (domObj._obj && domObj._obj.getObj && domObj._obj.getObj()) {
        return domObj._obj.getObj();
    }
    domObj = domObj.parentNode;
  }
  return null;
};

/**
 * Wrapper for the elementFromPoint function that uses clientX and clientY coordinates 
 * from the touch to find a corresponding displayable object
 * @param {DvtTouch} touch The touch being passed in to evaluate the element it corresponds to
 * @return {DvtDisplayable} displayable The dom object the touch corresponds to
 */
DvtSvgDocumentUtils.elementFromTouch = function(touch) {
  return this.elementFromPoint(touch.clientX, touch.clientY);
};

// TODO JSDoc
// Return coordinates relative to stage
DvtSvgDocumentUtils.getStageRelativePosition = function(stage, x, y) {
  var svgPos = DvtSvgDocumentUtils.getStageAbsolutePosition(stage);
  var xPos = x - svgPos.x;
  var yPos = y - svgPos.y;
  return new DvtPoint(xPos, yPos);
}

/**
 * Return coordinates relative to the page
 * @param {DvtStage} stage
 * @param {Number} x
 * @param {Number} y
 */
DvtSvgDocumentUtils.getPageRelativePosition = function(stage, x, y)
{
  var svgPos = DvtSvgDocumentUtils.getStageAbsolutePosition(stage);
  var xPos = x + svgPos.x;
  var yPos = y + svgPos.y;
  return new DvtPoint(xPos, yPos);
}

/**
 * Returns the absolute page coordinates of the stage.
 * @param {DvtStage}
 * @return {DvtPoint}
 */
DvtSvgDocumentUtils.getStageAbsolutePosition = function(stage) {
  var svgRoot = stage.getImpl().getSVGRoot();
  
  // Use a reference element at the same position at the SVG whenever possible.  The browser functions used in DvtAgent's
  // getElementPosition do not always return the correct values for SVG elements, especially in Firefox.  Note that we 
  // can ensure the presence of a parent div at the same coordinates across all our supported frameworks, so this
  // issue will never occur in the real product.
  var referenceElem = svgRoot;
  if (window && window.AdfAgent) 
    referenceElem = svgRoot.parentNode;
  else if (svgRoot.parentNode && svgRoot.parentNode.firstChild == svgRoot) // Otherwise require element to be the first child      
    referenceElem = svgRoot.parentNode;
  
  // Note: As mentioned above, this returns the wrong position in Firefox for SVG elements.
  var svgPos = DvtAgent.getElementPosition(referenceElem);
  
  return new DvtPoint(parseInt(svgPos.x), parseInt(svgPos.y));
}

/**
 * Returns true if SVG filters are supported.
 * @return {boolean}
 */
DvtSvgDocumentUtils.isFilterSupported = function(stage) {
  // Filters not supported in IE, Safari < 6, Android
  return !DvtAgent.isPlatformIE() && 
         !(DvtAgent.isBrowserSafari() && DvtAgent.getVersion() < 536) &&
         !(DvtAgent.isTouchDevice() && !DvtAgent.isBrowserSafari());
}

DvtSvgDocumentUtils.isStrokeTimeoutRequired = function() {
    return DvtAgent.isBrowserChrome();
}

/*
 * Whether or not events in SVG are received on regions that are visually clipped such that they are not visible
 */
DvtSvgDocumentUtils.isEventAvailableOutsideClip = function() {
    return DvtAgent.isBrowserSafari();
}

/*
 * Bug #12805402: In Firefox 4, horizontal/vertical polylines must be bent by 0.01 pixes to show up when fitlers are used
 */
DvtSvgDocumentUtils.isFilterStraightLineAdjustmentNeeded = function() {
    return DvtAgent.isPlatformGecko();
}

/**
 * Work around bug 16575172, where removing filters from children or 
 * removing children that have filters can leave behind rendering artifacts 
 * in Firefox.
 * @param displayable The displayable to refresh.
 */
DvtSvgDocumentUtils.fixGeckoContainerForFilter = function(displayable) {
  if (displayable && DvtAgent.isPlatformGecko()) {
    var elem = displayable.getImpl().getElem();
    if (elem) {
      var transformAttr = DvtSvgUtils.getAttrNullNS(elem, 'transform');
      //error message logged to Firefox console if transformAttr is null,
      //so initialize it to identity matrix instead
      if (!transformAttr) {
        transformAttr = "matrix(1 0 0 1 0 0)";
      }
      DvtSvgUtils.setAttrNullNS(elem, 'transform', transformAttr);
    }
  }
}

/**
 * Cancel a DOM event.
 * 
 * @param {object}  e  event object to cancel
 */
DvtSvgDocumentUtils.cancelDomEvent = function(e)
{
  //if (!e)
  //{
  //  e = window.event;
  //}
  if (e)
  {
    if (e.stopPropagation)
      e.stopPropagation();
    if (e.preventDefault)
      e.preventDefault();
    e.cancelBubble = true;
    e.cancel = true;
    e.returnValue = false;
  }
}
/**
 * DvtSvgImageLoader.loadImage("pic.png", function(image) {
 *   alert(image.width);
 *   alert(image.height);
 * });
 * 
 */

/*
 * DvtSvgImageLoader
 */
var DvtSvgImageLoader = { _cache: {} };

DvtObj.createSubclass(DvtSvgImageLoader, DvtObj, "DvtSvgImageLoader");

/**
 * Copied from AdfIEAgent and AdfAgent
 * Adds an event listener that fires in the non-Capture phases for the specified
 * eventType.  There is no ordering guaranteee, nor is there a guarantee
 * regarding the number of times that an event listener will be called if
 * it is added to the same element multiple times.
 */
DvtSvgImageLoader.addBubbleEventListener = function(element, type, listener) 
{
  if (window.addEventListener) {
    DvtSvgUtils.addDomEventListener(element, type, listener, false);
    return true;
  } 
  // Internet Explorer
  else if (window.attachEvent) {
    element.attachEvent("on" + type, listener);
    return true;
  }
  else {
    return false;
  }
};


/**
 * @this {DvtSvgImageLoader}
 * Load an image.
 * 
 * @param src URL of the image to load
 * @param onComplete function to call when the image is loaded
 * 
 * @return image if image is already loaded
 *         otherwise null
 */
DvtSvgImageLoader.loadImage = function(src, onComplete) {
  //first look for a cached copy of the image
  var entry = this._cache[src];

  //if cached image found, use it
  if (entry) {
    // if image is loading, add listener to queue
    if (entry._image) {
      DvtSvgImageLoader._addListenerToQueue(entry._listeners, onComplete);
    }
    // if image is loaded, call onComplete function
    else {
      if (onComplete) {
        onComplete(entry);
      }
      // no handler, just return image width and height
      return entry;
    }
  }
  //if cached image not found, load the new image
  else {
    this.loadNewImage(src, onComplete);
  }
  return null;
};

/**
 * @this {DvtSvgImageLoader}
 * Load a new image.
 * 
 * @param src URL of the image to load
 * @param onComplete function to call when the image is loaded 
 */
DvtSvgImageLoader.loadNewImage = function(src, onComplete) {

  // create img element
  var image = document.createElement('img');

  // add a new entry to the image cached
  // depending on the state, entry value contains different attributes
  // when image is loading, entry contains image element and listeners
  // when image is loaded, entry contains image width and height
  var newEntry = {
    _listeners:[], 
    url: src,
    _image: image
  };
  if (onComplete) {
//    newEntry._listeners.push(onComplete);
    DvtSvgImageLoader._addListenerToQueue(newEntry._listeners, onComplete);
  }

  this._cache[src] = newEntry;

  DvtSvgImageLoader.addBubbleEventListener(image, 'load', function(e) {
      // copy width and height to entry and delete image element
      newEntry.width = image.width;
      newEntry.height = image.height;
      delete newEntry._image;

      // notify all listeners image loaded and delete all listeners
      var i;
      var len = newEntry._listeners.length;
      for (i = 0; i < len; i++) {
        // if there is a listener
        if (newEntry._listeners[i]) {
        newEntry._listeners[i](newEntry);
        }
      }
      delete newEntry._listeners;
    });

  image.src = src;

};


// add a listener to the queue only if it doesn't already exist
DvtSvgImageLoader._addListenerToQueue = function(queue, listener) {
  if (listener) {
    for (var i = 0; i < queue.length; i++) {
      if (queue[i] === listener)
        return;
    }
    queue.push(listener);
  }
};

/**
 * @constructor
 * Wrapper class providing access to SVG Events.
 * @extends DvtObj
 * @class DvtSvgBaseEvent
 * <p>The supported fields are:
 * <ul>
 * <li>target</li>
 * <li>type</li>
 * </ul>
 * <p>
 */
var DvtSvgBaseEvent = function(event) {
  this.Init(event);
}

DvtObj.createSubclass(DvtSvgBaseEvent, DvtObj, "DvtSvgBaseEvent");

/**
 * @protected
 * @param {MouseEvent} the DOM Mouse Event
 */
DvtSvgBaseEvent.prototype.Init = function(event) {
  // Save the event
  this._event = event;
  this._isPropagationStopped = false;

  // Find the DvtObj corresponding to the event target
  this.target = DvtSvgBaseEvent.FindDisplayable(event.target);

  this.type = event.type;
}

// TODO JSDoc
DvtSvgBaseEvent.prototype.getNativeEvent = function() {
  return this._event;
}

DvtSvgBaseEvent.prototype.preventDefault = function() {
    this._event.preventDefault();
} 

DvtSvgBaseEvent.prototype.stopPropagation = function() {
    if (this._event.stopPropagation)
      this._event.stopPropagation();
    this._event.cancelBubble = true;
    this._event.cancel = true;
    this._event.returnValue = false;
    this._isPropagationStopped = true;
} 

DvtSvgBaseEvent.prototype.isPropagationStopped = function() {
  return this._isPropagationStopped;
} 

/**
 * Given an SVG DOM target, returns the corresponding DvtDisplayable.
 * @return {DvtDisplayable} The corresponding displayable, if any.
 */
DvtSvgBaseEvent.FindDisplayable = function(target) {
  while(target) {
    // If this object has a displayable, return it
    if(target._obj && target._obj.getObj && target._obj.getObj())
      return target._obj.getObj();
    else // Otherwise look at the parent
      target = target.parentNode;
  }
  
  // Displayable not found, return null
  return null;
}
var DvtSvgEventFactory = new Object();

DvtObj.createSubclass(DvtSvgEventFactory, DvtObj, "DvtSvgEventFactory");

// Note: this doesn't need to live in the factory because it will always be called
// by impl specific code looking to wrap the event.
// TODO JSDoc
DvtSvgEventFactory.newEvent = function(nativeEvent, context) {
  // TODO detect the event type and perform wrapping as needed
  var eventType = nativeEvent.type;
  if (eventType == DvtTouchEvent.TOUCHSTART || eventType == DvtTouchEvent.TOUCHMOVE || eventType == DvtTouchEvent.TOUCHEND || eventType == DvtTouchEvent.TOUCHCANCEL) {
      return new DvtSvgTouchEvent(nativeEvent, context);
  } 
  else if(eventType == DvtKeyboardEvent.KEYDOWN || eventType == DvtKeyboardEvent.KEYUP) {
      return new DvtSvgKeyboardEvent(nativeEvent);
  }  
  else {
      // TODO check for type
      //BUG FIX 14187937: if the native event is the same as the last one, return the stored logical event,
      //otherwise create a new logical event and store the pair of events
      if (context._nativeEvent != nativeEvent) {
        context._nativeEvent = nativeEvent;
        context._logicalEvent = new DvtSvgMouseEvent(nativeEvent);
      }
      return context._logicalEvent;
  }
};

/**
 * Returns a DvtSvgMouseEvent that wraps the given keyboard event.  The given stageX and stageY coordinates are used to
 * compute the DvtSvgMouseEvent's pageX and pageY fields
 * 
 * @param {DvtKeyboardEvent} keyboardEvent
 * @param {DvtContext} context
 * @param {String} eventType
 * @param {DvtStage} stage
 * @param {Number} stageX
 * @param {Number} stageY
 * @return {DvtSvgMouseEvent}
 */
DvtSvgEventFactory.generateMouseEventFromKeyboardEvent = function(keyboardEvent, context, eventType, stage, stageX, stageY)
{
  var nativeEvent = null;
  
  if (document.createEvent) 
  {
    nativeEvent = document.createEvent("MouseEvents");
    
    var pageCoord = DvtSvgDocumentUtils.getPageRelativePosition(stage, stageX, stageY);
    
    nativeEvent.initMouseEvent(eventType, true, true, window, 1, pageCoord.x, pageCoord.y, pageCoord.x, pageCoord.y,
                         keyboardEvent.ctrlKey, keyboardEvent.altKey, keyboardEvent.shiftKey, keyboardEvent.metaKey,
                         0, null);
  }    
  
  if(nativeEvent)
  {
    var mouseEvent = DvtSvgEventFactory.newEvent(nativeEvent, context);
    mouseEvent.target = keyboardEvent.target;
    return mouseEvent;
  }
  else
    return null;
}
/**
 * @constructor
 * Wrapper class providing access to SVG Keyboard Events.
 * @extends DvtKeyboardEvent
 * @class DvtSvgKeyboardEvent
 * <p>The supported fields are:
 * <ul>
 * <li>altKey</li>
 * <li>ctrlKey</li>
 * <li>shiftKey</li>
 * <li>charCode</li>
 * <li>keyCode</li>
 * </ul>
 * <p>
 */
// TODO: add support for a source and target fields
var DvtSvgKeyboardEvent = function(event) {
  this.Init(event);
}

DvtObj.createSubclass(DvtSvgKeyboardEvent, DvtKeyboardEvent, "DvtSvgKeyboardEvent");


/**
 * @protected
 * @param {KeyboardEvent} the DOM Keyboard Event
 */
DvtSvgKeyboardEvent.prototype.Init = function(event) {
  DvtSvgKeyboardEvent.superclass.Init.call( this, 
                                            event.type, 
                                            event.bubbles,
                                            event.cancelable,
                                            event.view,
                                            event.charCode,
                                            event.keyCode,
                                            event.keyLocation,
                                            event.ctrlKey,
                                            event.altKey,
                                            event.shiftKey,
                                            event.repeat,
                                            event.locale );
                                            
  // TODO - what about event.target?                                            
  this._event = event;
}

/**
 * @override
 */
DvtSvgKeyboardEvent.prototype.getNativeEvent = function()
{
  return this._event;
}

/**
 * @override
 */
DvtSvgKeyboardEvent.prototype.preventDefault = function() {
    this._event.preventDefault();
} 

/**
 * @override
 */
DvtSvgKeyboardEvent.prototype.stopPropagation = function() {
    if (this._event.stopPropagation)
      this._event.stopPropagation();
    this._event.cancelBubble = true;
    this._event.cancel = true;
    this._event.returnValue = false;
} 
/**
 * @constructor
 * Wrapper class providing access to SVG Mouse Events.
 * @extends DvtSvgBaseEvent
 * @class DvtSvgMouseEvent
 * <p>The supported fields are:
 * <ul>
 * <li>ctrlKey</li>
 * <li>relatedTarget</li>
 * <li>target</li>
 * <li>type</li>
 * </ul>
 * <p>
 */
var DvtSvgMouseEvent = function(event) {
  this.Init(event);
}

DvtObj.createSubclass(DvtSvgMouseEvent, DvtSvgBaseEvent, "DvtSvgMouseEvent");

/**
 * @protected
 * @param {MouseEvent} the DOM Mouse Event
 */
DvtSvgMouseEvent.prototype.Init = function(event) {

  DvtSvgMouseEvent.superclass.Init.call(this, event);
  
  // Find the DvtObj corresponding to the event target
  if(event.relatedTarget != null)
    this.relatedTarget = DvtSvgBaseEvent.FindDisplayable(event.relatedTarget);  
  
  // Copy the remaining information
  this.button = event.button;
  this.ctrlKey = event.ctrlKey || event.metaKey;
  this.shiftKey = event.shiftKey;
  this.pageX = event.pageX;
  this.pageY = event.pageY;
  //Bug 17369581: Flag indicates if the event is modified for Internet Explorer
  this._isEventModifiedForIE = false;

  if (event.wheelDelta != null)
    this.wheelDelta = event.wheelDelta/40;
  else if(event.deltaY != null) {
    this.deltaMode = event.deltaMode;
    this.deltaY = event.deltaY;
    
    // Approximate the wheel delta from the deltaY and deltaMode
    if(event.deltaMode == event.DOM_DELTA_LINE)
      this.wheelDelta = -event.deltaY;
    else if(event.deltaMode == event.DOM_DELTA_PIXEL)
      this.wheelDelta = -event.deltaY/15;  
  }
  else
    this.wheelDelta = event.detail;
};

/**
 * Modify the event for IE with the new event type and new target element
 * 
 * @public
 * @param {string} newType  New Mouse Event type
 * @param {DOMElement} newTargetElement  New SVG DOM Target Element
 */
DvtSvgMouseEvent.prototype.modifyEventForIE = function(newType, newTargetElement) {
  //Bug 17369581: Set the event type as newType, relatedTarget as current target and target as displayable of newTargetElement
  //This will target the event to newTargetElement and change the event type 
  //Modify the event target only once because every DvtSvgMouseEvent instance has unique native event.
  //Doing this more than once will make relatedTarget and target same as displayable of newTargetElement
  if(!this._isEventModifiedForIE) {
    this.type = newType;
    this.relatedTarget = this.target;
    this.target = DvtSvgBaseEvent.FindDisplayable(newTargetElement);
    this._isEventModifiedForIE = true;
  }
};

/**
 * @constructor
 * Wrapper class providing access to SVG Touch Events.
 * @extends DvtSvgBaseEvent
 * @class DvtSvgTouchEvent
 * <p>The supported fields are:
 * <ul>
 * <li>touches</li>
 * <li>targetTouches</li>
 * <li>changedTouches</li>
 * <li>target</li>
 * <li>type</li>
 * </ul>
 * <p>
 */
var DvtSvgTouchEvent = function(event, context) {
  this.Init(event, context);
}

DvtObj.createSubclass(DvtSvgTouchEvent, DvtSvgBaseEvent, "DvtSvgTouchEvent");

/**
 * @protected
 * @param {TouchEvent} the DOM Touch Event
 * @param {DvtContext} context 
 */
DvtSvgTouchEvent.prototype.Init = function(event, context) {

  DvtSvgTouchEvent.superclass.Init.call(this, event);
  // Convert touchcancel to touchend
  if (event.type == DvtTouchEvent.TOUCHCANCEL) {
      this.type = DvtTouchEvent.TOUCHEND;
  }
  this._nativeTouches = event.touches;
  this._nativeTargetTouches = event.targetTouches;
  this._nativeChangedTouches = event.changedTouches;
  this.touches = DvtSvgTouchEvent.createTouchArray(event.touches);
  this.targetTouches = DvtSvgTouchEvent.createTouchArray(event.targetTouches);
  this.changedTouches = DvtSvgTouchEvent.createTouchArray(event.changedTouches);
  this._context = context;
  this._touchManager = null;
}

DvtSvgTouchEvent.prototype.blockTouchHold = function() {
  this._event._touchHoldBlocked = true;
}

DvtSvgTouchEvent.prototype.isInitialTouch = function() {
   return (this.touches.length - this.changedTouches.length) == 0;
}

DvtSvgTouchEvent.prototype.isTouchHoldBlocked = function() {
  return (this._event._touchHoldBlocked) ? true : false;
}

DvtSvgTouchEvent.createTouchArray = function(nativeTouchArray) {
  var touches = new Array();
  for (var i=0; i<nativeTouchArray.length; i++) {
      var nativeTouch = nativeTouchArray[i];
      var touch = new DvtTouch(nativeTouch);
      touches.push(touch);
  }
  return touches;
}

/**
 * Prevents further propagation of the current event
 */
DvtSvgTouchEvent.prototype.stopPropagation = function() {
    DvtSvgTouchEvent.superclass.stopPropagation.call(this);
    if (this._touchManager)
      this._touchManager.postEventBubble(this);
}

/**
 * Sets a touch manager object
 * @param {DvtTouchManager} touch manager object for the event
 */
DvtSvgTouchEvent.prototype.setTouchManager = function (touchManager) {
  this._touchManager = touchManager;
}
// Used for rendering SVG content in to an HTML div wrapper
/**
 * @param {DvtContext} context
 * @param {string} domElementId
 * @constructor
 */
var DvtHtmlRichTooltipManager = function(context, domElementId) {
    this.Init(context, domElementId);
};

DvtObj.createSubclass(DvtHtmlRichTooltipManager, DvtHtmlTooltipManager, "DvtHtmlRichTooltipManager");

/**
 * @param {DvtContext} context
 * @param {string} domElementId
 */
DvtHtmlRichTooltipManager.prototype.Init = function(context, domElementId) 
{
  this._storedContexts = new Object();
  DvtHtmlRichTooltipManager.superclass.Init.call(this, context, domElementId);
}

DvtHtmlRichTooltipManager.prototype.InitContent = function(tooltip) {
      var svg = DvtSvgUtils.createSvgDocument("DvtCustomTooltip");
      // For an svg custom tooltip, the context is the new svg context for the svg document overlay.
      var context = new DvtSvgContext(svg, 'tooltipStage');
      this._storedContexts[this._domElementId] = context;
      tooltip.innerHTML = "";
      DvtSvgUtils.appendChildElem(tooltip, svg);
}

DvtHtmlRichTooltipManager.prototype.GetStoredContext = function() {
    return this._storedContexts[this._domElementId];
}

DvtHtmlRichTooltipManager.prototype.showRichElement = function(x, y, renderable, useOffset)
{
    this.showRichElementAtPosition(x, y, renderable, useOffset, false, DvtHtmlTooltipManager.UNSTYLED_POPUP_CLASS);
};

DvtHtmlRichTooltipManager.prototype.showRichElementAtPosition = function(x, y, renderable, useOffset, noEvents, popupClass) {

    var tooltip = this.GetTooltipElem();
    
    if (!popupClass)
      popupClass = DvtHtmlTooltipManager.UNSTYLED_POPUP_CLASS;
      
    tooltip.className = popupClass;
    
    var context = this.GetStoredContext();
    if (context){
      var stage = context.getStage();
      stage.removeChildren();
      var rootDisplayable = renderable.getRootDisplayable();
      stage.addChild(rootDisplayable);
      
      if (renderable && renderable.Render) {
          renderable.Render();
      }
      
      var svg = stage.getImpl().getSVGRoot();
      DvtSvgUtils.setAttrNullNS(svg, "width", renderable.getDisplayWidth());
      DvtSvgUtils.setAttrNullNS(svg, "height", renderable.getDisplayHeight());
      
      this.PostElement(tooltip, x, y, noEvents, useOffset);
    }
}

DvtHtmlRichTooltipManager.prototype.hideTooltip = function()
{
    DvtHtmlRichTooltipManager.superclass.hideTooltip.call(this);
    var context = this.GetStoredContext();
    if (context) {
        var stage = context.getStage();
        stage.removeChildren();
    }
}
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.

/*---------------------------------------------------------------------*/
/*   DvtSvgImplFactory                                                 */
/*---------------------------------------------------------------------*/
/**
  * A factory class for SVG to create object implementations.
  * @extends  DvtHtmlImplFactory
  * @class DvtSvgImplFactory  A factory class to create SVG implementation objects.
  * @constructor  Creates SVG inplementation objects.
  */
var DvtSvgImplFactory = function(context) {
  this.Init(context) ;
};

DvtObj.createSubclass(DvtSvgImplFactory, DvtHtmlImplFactory, "DvtSvgImplFactory");

/*---------------------------------------------------------------------*/
/*     Base implementations                                            */
/*---------------------------------------------------------------------*/

/**
 *  Returns a new SVG <defs> element
 *  @private  
 */
DvtSvgImplFactory.prototype.newDefs = function() {
  return  DvtSvgShapeUtils.createElement('defs') ;
};

/**
 *  Returns a new DvtStage implementation object.
 */
DvtSvgImplFactory.prototype.newStage = function(root, id) {
  return  new DvtSvgStage(root, id) ;
};


/**
 * Returns a new DvtSvgContainer implementation.
 * @param {String} id  Optional ID for the object (see also {@link DvtDisplayable#setId}) 
 * @override
 * @type DvtContainer
 */
DvtSvgImplFactory.prototype.newContainer = function (id) {
  return new DvtSvgContainer(id, 'g');
};

/**
  *  Returns a new DvtSvgArc implementation object for an arc of a circle or ellipse.
  *  @param {number} cx  The center x position.
  *  @param {number} cy  The center y position.
  *  @param {number} rx  The horizontal radius of the ellipse/circle.
  *  @param {number} ry  The vertical radius of the ellipse/circle.
  *  @param {number} sa  The starting angle in degrees (following the normal anti-clockwise is positive convention).
  *  @param {number} ae  The angle extent in degrees (following the normal anti-clockwise is positive convention).
  *  @param {String} clos  An optional closure type for the arc. Closures are "OPEN" (the default), "SEGMENT", and "CHORD".
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *  @returns {DvtSvgArc}  A new {@link DvtArc} object.
 *   @override
 */
DvtSvgImplFactory.prototype.newArc = function (cx, cy, rx, ry, sa, ae, clos, id) {
  return new DvtSvgArc(cx, cy, rx, ry, sa, ae, clos, id);
};


/**
  *  Creates a new DvtSvgCircle implementation for a circle.
  *  @param {number} cx  The center x position.
  *  @param {number} cy  The center y position.
  *  @param {number} r   The radius of the circle.
  *  @param {String} id  Optional ID for the object (see also {@link DvtDisplayable#setId}) 
  *  <p>
  *  <b>Example:</b><br><br> The following code creates a red circle of radius 100
  *  and line width 5, centered at (10,10).
  *  <br><br><code>
  *  var circle = factory.newCircle(10, 10, 100, 'myCircle') ;<br>
  *  circle.setStroke(new DvtStroke("red", 5)) ;<br>
  *  </code><br>
  *  @returns {DvtSvgCircle} A new DvtSvgCircle object.
  *  @override
 */
DvtSvgImplFactory.prototype.newCircle = function (cx, cy, r, id) {
  return  new DvtSvgCircle(cx, cy, r, id);
};

/**
 *  Returns a new DvtSvgImage implementation object for a displayable image.
 *  @param {String} imgSrc  The image URL.
 *  @param {number} x  The x position of the top left corner of the image.
 *  @param {number} y  The y position of the top left corner of the image.
 *  @param {number} w  The width of the image (optional).
 *  @param {number} h  The height of the image (optional).
 *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
 *  <p>
 *  Example:<br><br><code>
 *  var img = factory.newImage('pic.png', 10, 10, 100, 150, 'myImg') ;<br>
 *  </code>
 *  @returns  {@link DvtImage}  A new DvtSvgImage object.
 *  @override
 */
DvtSvgImplFactory.prototype.newImage = function (imgSrc, x, y, w, h, id) {
  return new DvtSvgImage(imgSrc, x, y, w, h, id);
};

/**
  *  Creates a new DvtSvgLine implementation object which for a displayable line
  *  segment drawn from (x1, y1) to (x2, y2).
  *  @param {number} x1  An x end position of the line.
  *  @param {number} y1  The associated y end point (for x1).
  *  @param {number} x2  The other x end point.
  *  @param {number} y2  The associated y end point (for x2).
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *  <p>Example:<br><br><code>
  *  var line = factory.newLine(10, 10, 100, 150);</code><br>
  *  @returns  {DvtSvgLine}  A new DvtSvgLine object.
  *  @override
  */
DvtSvgImplFactory.prototype.newLine = function (x1, y1, x2, y2, id) {
  return  new DvtSvgLine(x1, y1, x2, y2, id);
};


/**
 *  Returns a new DvtSvgMarker implementation object for a graph or legend marker.
 *  @param {number} type The type of the marker (see {@link DvtMarker}).
 *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
 *  @param {String} etype  Optional element type for the shape (see {@link  DvtMarker}).
 *  <p>
 *  Example:<br><br><code>
 *  var rect = factory.newMarker(type, 'myRect') ;<br>
 *  </code>
 *  @returns {DvtSvgMarker}  A new DvtSvgMarker object.
 *  @override
 */
DvtSvgImplFactory.prototype.newMarker = function (type, id, etype) {
    return  new DvtSvgMarker(type, id, etype);
};


/**
  *  Returns a new DvtSvgOval implementation object which for a displayable ellipse.
  *  @param {number} cx  The center x position.
  *  @param {number} cy  The center y position.
  *  @param {number} rx  The horizontal radius of the ellipse.
  *  @param {number} ry  The vertical radius of the ellipse.
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *  @returns  {DvtSvgOval}  A new DvtSvgOval object.
 * @override
 */
DvtSvgImplFactory.prototype.newOval = function (cx, cy, rx, ry, id) {
  return  new DvtSvgOval(cx, cy, rx, ry, id);
};

/**
  * Returns a new DvtSvgPath implementation object for a displayable shape
  * defined by SVG path commands.
  * @param {Object} cmds  Optional string of SVG path commands (see comment for
  *                       {@link DvtPath#setCmds}), or an array containing
  *                       consecutive command and coordinate entries (see comment
  *                       for {@link DvtPath#setCommands}).
  * @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  * @returns {DvtSvgPath}  A new DvtSvgPath object.
  * @override
 */
DvtSvgImplFactory.prototype.newPath = function (cmds, id) {
  return  new DvtSvgPath(cmds, id);
};

/**
 * Returns a new DvtSvgPolygon implementation object for a displayable shape composed of 
 * connected multi-line segments.
 * @param {array} ar  An array of consecutive x,y coordinate pairs.
 * @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
 * @returns {DvtSvgPolygon}  A new DvtSvgPolygon object.
 * @override
 */
DvtSvgImplFactory.prototype.newPolygon = function (arPoints, id) {
  return  new DvtSvgPolygon(arPoints, id);
};


/**
 *  Returns a new DvtSvgPolyline object for a line composed of multiple line segments.
 *  @param {array} ar  An array of consecutive x,y coordinate pairs.
 *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
 *  @return {DvtSvgPolyline}  A new  DvtSvgPolyline object.
 *  @override
 */
DvtSvgImplFactory.prototype.newPolyline = function (ar, id) {
  return  new DvtSvgPolyline(ar, id);
};

/**
 *  Returns a new DvtSvgRect implementation object for a displayable rectangle.
 *  @param {number} x  The x position of the top left corner of the rectangle.
 *  @param {number} y  The y position of the top left corner of the rectangle.
 *  @param {number} w  The width of the rectangle.
 *  @param {number} h  The height of the rectangle.
 *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
 *  <p>
 *  Example:<br><br><code>
 *  var rect = factory.newRect(10, 10, 100, 150, 'myRect') ;<br>
 *  </code>
 *  @returns {DvtSvgRect} A new DvtSvgRect object.
 *  @override
 */
DvtSvgImplFactory.prototype.newRect = function (x, y, w, h, id) {
  return  new DvtSvgRect(x, y, w, h, id);
};

/**
 * Returns a new DvtSvgText implementation object for displayable text.
 * @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
 * @returns {DvtSvgText}  A new DvtSvgText object.
 * @override
 */
DvtSvgImplFactory.prototype.newText = function (textStr, x, y, id) {
  return  new DvtSvgText(this.GetContext(), textStr, x, y, id);
};

/**
 * Returns a new DvtSvgTextArea implementation object for displayable text.
 * @param x {number} x the x position
 * @param y {number} y the y position
 * @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
 * @returns {DvtSvgTextArea}   A new DvtSvgTextArea object.
 * @override
 */
DvtSvgImplFactory.prototype.newTextArea = function (x, y, id) {
  return new DvtSvgTextArea(this.GetContext(), x, y, id);
};


/**
 * Returns a new DvtSvgTextFormatted implementation object for displayable text.
 * @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
 * @returns {DvtSvgTextFormatted}  A new DvtSvgTextFormatted object.
 * @type DvtTextFormatted
 * @override
 */
DvtSvgImplFactory.prototype.newTextFormatted = function (x, y, id) {
  return  new DvtSvgTextFormatted(this.GetContext(), x, y, id);
};

/**
 * Obtain imageLoader singleton
 * @override
 */
DvtSvgImplFactory.prototype.getImageLoader = function () {
  if (DvtAgent.isEnvironmentBrowser())
    return DvtSvgImageLoader;
  else
    return DvtJavaImageLoader;
};

/**
 * @override
 */
DvtSvgImplFactory.prototype.getDocumentUtils = function () {
  return DvtSvgDocumentUtils;
};
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*---------------------------------------------------------------------*/
/*   DvtSvgJsExt            SVG implementation of DvtJsExt             */
/*---------------------------------------------------------------------*/


//  Placeholder - revist if/when callbacks are used.


/**
  *  Provides a gateway for access to ADF functionality for SVG. 
  *  @class DvtSvgJsExt
  *  @extends DvtJsExt
  *  @constructor
  */
var  DvtSvgJsExt = function()
{
};

DvtObj.createSubclass(DvtSvgJsExt, DvtJsExt, "DvtSvgJsExt");



/*---------------------------------------------------------------------*/
/*                                 ADF                                 */
/*---------------------------------------------------------------------*/
/**  
  *  Sets a PPR property value.
  *  @param {String}  id  An id to be substituted in the function call.
  *  @param {String)  prop a property such as {@link DvtJsExt#PPR_ATTRIB_HSS}
  *  @param {String}  value  the property value.
  */
DvtSvgJsExt.prototype.sendPPRProperty = function(id, prop, value)
{
/*
    var fun = DvtJsExt.JS_FP_SET_PPR_PROPERTY.replace("X-ID-X", id);
    fun = fun.replace("X-ID-X", id);
    var js  = fun + "('" + prop + "','" + value + "')";
    eval(js) ;
*/

  try {
         AdfPage.PAGE.findComponent(id).getPeer().setDVTPPRAttribute(prop,value) ;
  }
  catch (err) {} 
};


/**
  *   @param {String} id
  */
DvtSvgJsExt.prototype.sendPPR  = function(id)
{
/*
    var js  =  DvtJsExt.JS_FP_SEND.replace("X-ID-X", id) ;
    js = js.replace("X-ID-X", id) ;
    eval(js) ;
*/

  try {
    AdfPage.PAGE.findComponent(id).getPeer().sendToServerPPR({source:id}) ;
  }
  catch (err) {} 
};

/**
  *   @param {String} id
  */
DvtSvgJsExt.prototype.sendPPRNonBlocking = function(id)
{
/*
    var js  =  DvtJsExt.JS_FP_SEND_PPR_NON_BLOCKING.replace("X-ID-X", id) ;
    js = js.replace("X-ID-X", id) ;
    eval(js) ;
*/

  try {
        AdfPage.PAGE.findComponent(id).getPeer().sendToServerPPR3({source:id}) ;
  }
  catch (err) {} 
};



/*---------------------------------------------------------------------*/
/*                    External Javascript invocation                   */
/*---------------------------------------------------------------------*/

/**
  *   Performs the specified Javascript.
  *   @returns {unknown} the result, if any, of the Javascript execution.
  */
DvtSvgJsExt.prototype.callJs = function(js)
{
   //BUG 34184877 - MAKE DVT COMPATIBLE WITH CSP STANDARDS
   var jsArray = js.match(/'(.*?)'/g);
   var src= jsArray[0].replace(/\'/g, "");
   var eventData= jsArray[2].replace(/\'/g, "");
   var biData= jsArray[3].replace(/\'/g, "");
   return AdfPage.PAGE.findComponent(src).getPeer().sendToServerPPR({source:src,event:eventData,bi_gdzA:biData});
};

/**
  *   Performs the specified Javascript.
  *   @param {String} js the js function to call
  *   @param {unknown} param the param to pass to the function
  *   @returns {unknown} the result, if any, of Javascript.
  */
DvtSvgJsExt.prototype.callJsWithParam = function(js, param)
{
  var func = eval(js);
  return func(param);
};


/*---------------------------------------------------------------------*/
/*                            Logging                                  */
/*---------------------------------------------------------------------*/

/**
  *   Returns the current ADF log level.
  *   @returns {number} the current ADF log level (such as {DvtJsExt#LOG_WARNING}).
  */
DvtSvgJsExt.prototype.getLogLevel = function()
{
    return  adf_dvt_fpGetLogLevel() ;
};


/**
  *   Forwards a message to the ADF logger.
  *   @param  {String} msg the message to be logged.
  *   @param  {number} level the log level of the messsage.  If omitted
  *                          a value of {DvtJsExt#LOG_INFO} is assumed.
  */
DvtSvgJsExt.prototype.log = function(msg, level)
{
    adf_dvt_fpLogger(msg, (level === undefined? DvtJsExt.LOG_INFO : level)) ;
};


/**
  *   Forwards a message string to ADF DVT.
  *   @param  {String} msg the message to be sent.
  */
DvtSvgJsExt.prototype.msg = function(msg)
{
    adf_dvt_fpMsg(msg) ;
};



/*---------------------------------------------------------------------*/
/*                           Performance                               */
/*---------------------------------------------------------------------*/

/**
  *   Forwards a performance message.
  *   @param  {String} msg the performance message to be sent.
  *   @param  {number} type  the performance type (such as {DvtJsExt#PERF_TIMING}).
  */

DvtSvgJsExt.prototype.perf = function(s, type)
{
    adf_dvt_fpPerfLogger('' + type + s) ;     // Due to original issues with args in
                                              // Flash the type is currently sent at
                                              // the start of the string.
};



/*---------------------------------------------------------------------*/
/*                            Miscellaneous                            */
/*---------------------------------------------------------------------*/

/**
  *        TDO - JRM  is this still needed ?
  */
DvtSvgJsExt.prototype.requestRefresh = function()
{
    adf_dvt_fpRefresh() ;
};


DvtSvgJsExt.prototype.dispatchEvent = function(callback, callbackObj, component, event) {
  DvtEventDispatcher.dispatchEvent(callback, callbackObj, component, event);
}
// Copyright (c) 2011, 2023, Oracle and/or its affiliates.
/**
 * @constructor
 */
var DvtSvgFilterContext = function()
{
  this.Init();
};

DvtObj.createSubclass(DvtSvgFilterContext, DvtObj, "DvtSvgFilterContext");

DvtSvgFilterContext.prototype.Init = function()
{
  this._regionPctRect = new DvtRectangle(-10, -10, 120, 120);
  this._counter = 0;
  this._resultIdsUnder = [];
  this._resultIdsOver = [];
};

DvtSvgFilterContext.prototype.getRegionPctRect = function()
{
  return this._regionPctRect;
};

DvtSvgFilterContext.prototype.getResultIdsUnder = function()
{
  return this._resultIdsUnder;
};

DvtSvgFilterContext.prototype.getResultIdsOver = function()
{
  return this._resultIdsOver;
};

DvtSvgFilterContext.prototype.createResultId = function(id)
{
  if (!id)
  {
    id = "filtRes";
  }
  return (id + (this._counter++));
};
// Copyright (c) 2011, 2023, Oracle and/or its affiliates.

/**
  *   Static SVG filter routines.
  *   @class DvtSvgFilterUtils 
  *   @constructor
  */
var  DvtSvgFilterUtils = function()
{} ;

DvtObj.createSubclass(DvtSvgFilterUtils, DvtObj, "DvtSvgFilterUtils");

DvtSvgFilterUtils._counter = 0;

DvtSvgFilterUtils.createFilter = function(effects, svgDisplayable)
{
  var filt = DvtSvgShapeUtils.createElement('filter', DvtSvgFilterUtils.CreateFilterId());
  var filtContext = new DvtSvgFilterContext();
  for (var i = 0; i < effects.length; i++)
  {
    var effect = effects[i];
    if (effect)
    {
      DvtSvgFilterUtils.CreateFilterPrimitives(filt, effect, svgDisplayable, filtContext);
    }
  }
  
  // BUG #12805402: When bounding box of svg element has zero height or width, userSpaceOnUse must be used
  // Otherwise, the element will disappear
  var userSpaceOnUse = false;
  if (svgDisplayable) {
      var obj = svgDisplayable.getObj();
      var boundsRect;
      if (obj && obj._bInitFx && obj._origRect)
        boundsRect = obj._origRect;
      else
        boundsRect = svgDisplayable.getElem().getBBox();
      if (boundsRect) {
          var width = boundsRect.width;
          var height = boundsRect.height;
          if (height == 0 || width == 0) {
            var stroke;
            //if displayable is not a shape, for example a container, then it doesn't have a stroke
            if (svgDisplayable.getStroke) {
              stroke = svgDisplayable.getStroke();
            }
            var adjustWidth = 10;
            if (stroke) {
                adjustWidth = stroke.getWidth();
            }
            var x = boundsRect.x;
            var y = boundsRect.y;
    
            // Firefox 4 requires the line to be bend slightly when feColorMatrix is used
            if (DvtSvgDocumentUtils.isFilterStraightLineAdjustmentNeeded()) {
                if (svgDisplayable instanceof DvtSvgPolyline) {
                   var points = DvtArrayUtils.copy(svgDisplayable.getPoints());
                   if (width == 0) {
                       points[0] = points[0]+0.01;
                   }
                   if (height == 0) {
                       points[1] = points[1]+0.01;
                   }
        
                   var bentPoints = DvtSvgShapeUtils.convertPointsArray(points);
                   DvtSvgUtils.setAttrNullNS(svgDisplayable.getElem(), 'points', bentPoints) ;
                }
            }
    
            if (height == 0) {
                height = 2*adjustWidth;
                y -= adjustWidth;
            }
            if (width == 0) {
                width = 2*adjustWidth;
                x -= adjustWidth;
            }
    
            DvtSvgUtils.setAttrNullNS(filt, 'x', x) ;
            DvtSvgUtils.setAttrNullNS(filt, 'y', y) ;
            DvtSvgUtils.setAttrNullNS(filt, 'width', width) ;
            DvtSvgUtils.setAttrNullNS(filt, 'height', height) ;
            DvtSvgUtils.setAttrNullNS(filt, 'filterUnits', 'userSpaceOnUse') ;
            userSpaceOnUse = true;
          }
      }
  }
  if (!userSpaceOnUse) {
      if (filtContext.getRegionPctRect().x != -10 && filtContext.getRegionPctRect().x != "Infinity" && filtContext.getRegionPctRect().x != "-Infinity")
      {
        DvtSvgUtils.setAttrNullNS(filt, 'x', filtContext.getRegionPctRect().x + '%') ;
      }
      if (filtContext.getRegionPctRect().y != -10 && filtContext.getRegionPctRect().y != "Infinity" && filtContext.getRegionPctRect().y != "-Infinity")
      {
        DvtSvgUtils.setAttrNullNS(filt, 'y', filtContext.getRegionPctRect().y + '%') ;
      }
      if (filtContext.getRegionPctRect().w != 120 && filtContext.getRegionPctRect().w != "Infinity" && filtContext.getRegionPctRect().w != "-Infinity")
      {
        DvtSvgUtils.setAttrNullNS(filt, 'width', filtContext.getRegionPctRect().w + '%') ;
      }
      if (filtContext.getRegionPctRect().h != 120 && filtContext.getRegionPctRect().h != "Infinity" && filtContext.getRegionPctRect().h != "-Infinity")
      {
        DvtSvgUtils.setAttrNullNS(filt, 'height', filtContext.getRegionPctRect().h + '%') ;
      }
        
  }

  
  var numResultsUnder = filtContext.getResultIdsUnder().length;
  var numResultsOver = filtContext.getResultIdsOver().length;
  var elemMN;
  var j;
  
  var elemM  = DvtSvgShapeUtils.createElement('feMerge') ;
  if (numResultsUnder > 0)
  {
    for (j = 0; j < numResultsUnder; j++)
    {
      elemMN = DvtSvgShapeUtils.createElement('feMergeNode') ;
      DvtSvgUtils.setAttrNullNS(elemMN, 'in', filtContext.getResultIdsUnder()[j]) ;
      DvtSvgUtils.appendChildElem(elemM, elemMN) ;
    }
  }
  elemMN = DvtSvgShapeUtils.createElement('feMergeNode') ;
  DvtSvgUtils.setAttrNullNS(elemMN, 'in', 'SourceGraphic') ;
  DvtSvgUtils.appendChildElem(elemM, elemMN) ;
  if (numResultsOver > 0)
  {
    for (j = 0; j < numResultsOver; j++)
    {
      elemMN = DvtSvgShapeUtils.createElement('feMergeNode') ;
      DvtSvgUtils.setAttrNullNS(elemMN, 'in', filtContext.getResultIdsOver()[j]) ;
      DvtSvgUtils.appendChildElem(elemM, elemMN) ;
    }
  }
  DvtSvgUtils.appendChildElem(filt, elemM) ;
  
  return filt;
};

DvtSvgFilterUtils.CreateFilterPrimitives = function(filter, effect, svgDisplayable, filtContext)
{
  if (effect instanceof DvtShadow)
  {
    DvtSvgShadowUtils.createFilterPrimitives(filter, effect, svgDisplayable, filtContext);
  }
  else if (effect instanceof DvtGlow)
  {
    DvtSvgGlowUtils.createFilterPrimitives(filter, effect, svgDisplayable, filtContext);
  }
  else if (effect instanceof DvtBevel)
  {
    DvtSvgBevelUtils.createFilterPrimitives(filter, effect, svgDisplayable, filtContext);
  }
  
  return null;
};

DvtSvgFilterUtils.CreateFilterId = function()
{
  return ("filt" + (DvtSvgFilterUtils._counter++));
};
// Copyright (c) 2011, 2023, Oracle and/or its affiliates.
/*---------------------------------------------------------------------------*/
/*   DvtSvgBevelUtils    A static class for SVG bevel effect property        */
/*                      manipulation.                                        */
/*---------------------------------------------------------------------------*/
/**
  *   DvtSvgBevelUtils    A static class for SVG bevel effect property manipulation.
  *   @class DvtSvgBevelUtils 
  *   @constructor
  */
var  DvtSvgBevelUtils = function()
{} ;

DvtObj.createSubclass(DvtSvgBevelUtils, DvtObj, "DvtSvgBevelUtils");

/**
  *  Static method to create an SVG filter element and apply bevel properties to it.
  *  @param {Dvtbevel}  bevel  Dvtbevel object
  *  @param {DvtSvgDisplayable}  svgDisplayable  display object to apply bevel to
  *  @returns an SVG  &lt;filter&gt; element
  */
DvtSvgBevelUtils.createFilter = function(bevel, svgDisplayable)
{
   //  The following outer bevel filter is created:
   
   
   //   The following inner bevel filter is created:
   
   
   var filt = DvtSvgBevelUtils.CreateFilterElem(bevel);
   var filtContext = new DvtSvgFilterContext();
   
   DvtSvgBevelUtils.createFilterPrimitives(filt, bevel, svgDisplayable, filtContext);
   
  var elemM  = DvtSvgShapeUtils.createElement('feMerge') ;
  var elemMN;
  if (filtContext.getResultIdsUnder().length > 0)
  {
    elemMN = DvtSvgShapeUtils.createElement('feMergeNode') ;
    DvtSvgUtils.setAttrNullNS(elemMN, 'in', filtContext.getResultIdsUnder()[0]) ;
    DvtSvgUtils.appendChildElem(elemM, elemMN) ;
  }
  elemMN = DvtSvgShapeUtils.createElement('feMergeNode') ;
  DvtSvgUtils.setAttrNullNS(elemMN, 'in', 'SourceGraphic') ;
  DvtSvgUtils.appendChildElem(elemM, elemMN) ;
  if (filtContext.getResultIdsOver().length > 0)
  {
    elemMN = DvtSvgShapeUtils.createElement('feMergeNode') ;
    DvtSvgUtils.setAttrNullNS(elemMN, 'in', filtContext.getResultIdsOver()[0]) ;
    DvtSvgUtils.appendChildElem(elemM, elemMN) ;
  }
  DvtSvgUtils.appendChildElem(filt, elemM) ;
  
  if (filtContext.getRegionPctRect().x != -10)
  {
    DvtSvgUtils.setAttrNullNS(filt, 'x', filtContext.getRegionPctRect().x + '%') ;
  }
  if (filtContext.getRegionPctRect().y != -10)
  {
    DvtSvgUtils.setAttrNullNS(filt, 'y', filtContext.getRegionPctRect().y + '%') ;
  }
  if (filtContext.getRegionPctRect().w != 120)
  {
    DvtSvgUtils.setAttrNullNS(filt, 'width', filtContext.getRegionPctRect().w + '%') ;
  }
  if (filtContext.getRegionPctRect().h != 120)
  {
    DvtSvgUtils.setAttrNullNS(filt, 'height', filtContext.getRegionPctRect().h + '%') ;
  }
   
   return filt;
};

DvtSvgBevelUtils.createFilterPrimitives = function(filt, effect, svgDisplayable, filtContext)
{
  if (effect._type === DvtBevel.TYPE_INNER)
  {
    var boundsRect = svgDisplayable.getDimensions(svgDisplayable.getParent());
    DvtSvgBevelUtils.CreateInnerFilterPrimitives2(filt, effect, filtContext, boundsRect);
  }
  else if (effect._type === DvtBevel.TYPE_OUTER)
  {
    //TODO
  }
  else if (effect._type === DvtBevel.TYPE_FULL)
  {
    //TODO
  }
};

/**
  * @protected
  */
DvtSvgBevelUtils.CreateFilterElem = function(bevel)
{
  var filt = DvtSvgShapeUtils.createElement('filter', bevel._Id) ;
  return filt;
};

/**
  * @protected
  */
/*DvtSvgBevelUtils.CreateInnerFilterPrimitives = function(filt, bevel, filtContext)
{
  //var distance = bevel._distance;
  var angle = bevel._angle;
  var hilightRed   = DvtColorUtils.getRed(bevel._hilightRgba) ;
  var hilightGreen = DvtColorUtils.getGreen(bevel._hilightRgba) ;
  var hilightBlue  = DvtColorUtils.getBlue(bevel._hilightRgba) ;
  var hilightAlpha = DvtColorUtils.getAlpha(bevel._hilightRgba) ;
  //var hilightRgb = DvtColorUtils.makeRGB(hilightRed, hilightGreen, hilightBlue);
  var shadowRed   = DvtColorUtils.getRed(bevel._shadowRgba) ;
  var shadowGreen = DvtColorUtils.getGreen(bevel._shadowRgba) ;
  var shadowBlue  = DvtColorUtils.getBlue(bevel._shadowRgba) ;
  var shadowAlpha = DvtColorUtils.getAlpha(bevel._shadowRgba) ;
  //var shadowRgb = DvtColorUtils.makeRGB(shadowRed, shadowGreen, shadowBlue);
  //var strength = bevel._strength;
  var blurX = bevel._blurX;
  var blurY = bevel._blurY;
  
  //create bump map for specular lighting
  var elemGB = DvtSvgShapeUtils.createElement('feGaussianBlur') ;
  DvtSvgUtils.setAttrNullNS(elemGB, 'in', 'SourceAlpha') ;
  DvtSvgUtils.setAttrNullNS(elemGB, 'stdDeviation', blurX + " " + blurY) ;
  var elemGBResult = filtContext.createResultId('blur');
  DvtSvgUtils.setAttrNullNS(elemGB, 'result', elemGBResult) ;
  
  //specular highlight
  var elemSL = DvtSvgShapeUtils.createElement('feSpecularLighting') ;
  DvtSvgUtils.setAttrNullNS(elemSL, 'in', elemGBResult) ;
  DvtSvgUtils.setAttrNullNS(elemSL, 'surfaceScale', 5) ;
  DvtSvgUtils.setAttrNullNS(elemSL, 'specularConstant', 1) ;//.85);
  DvtSvgUtils.setAttrNullNS(elemSL, 'specularExponent', 20) ;
  //use pure white, and later convert to specified color
  DvtSvgUtils.setAttrNullNS(elemSL, 'lighting-color', '#ffffff');//hilightRgb) ;
  var elemSLResult = filtContext.createResultId('specLight');
  DvtSvgUtils.setAttrNullNS(elemSL, 'result', elemSLResult) ;
  
  var elemDL = DvtSvgShapeUtils.createElement('feDistantLight') ;
  DvtSvgUtils.setAttrNullNS(elemDL, 'azimuth', -angle) ;
  //elemDL , 'elevation', 0) ;
  
  //only keep part of specular highlight within original shape
  var elemC1 = DvtSvgShapeUtils.createElement('feComposite') ;
  DvtSvgUtils.setAttrNullNS(elemC1, 'operator', 'in') ;
  DvtSvgUtils.setAttrNullNS(elemC1, 'in', elemSLResult) ;
  DvtSvgUtils.setAttrNullNS(elemC1, 'in2', 'SourceAlpha') ;
  var elemC1Result = filtContext.createResultId('comp');
  DvtSvgUtils.setAttrNullNS(elemC1, 'result', elemC1Result) ;

  //convert white specular hilight to correct color
  var elemCM = DvtSvgShapeUtils.createElement('feColorMatrix') ;
  DvtSvgUtils.setAttrNullNS(elemCM, 'in', elemC1Result) ;
  DvtSvgUtils.setAttrNullNS(elemCM, 'type', 'matrix') ;
  DvtSvgUtils.setAttrNullNS(elemCM, 'values', (hilightRed/255) + ' 0 0 0 0 ' +
                                       '0 ' + (hilightGreen/255) + ' 0 0 0 ' +
                                       '0 0 ' + (hilightBlue/255) + ' 0 0 ' +
                                       //'0 0 0 ' + hilightAlpha + ' 0') ;
                                       '0 0 0 1 0') ;
  var elemCMResult = filtContext.createResultId('colMat');
  DvtSvgUtils.setAttrNullNS(elemCM, 'result', elemCMResult) ;
  
  //specular shadow
  var elemSL2 = DvtSvgShapeUtils.createElement('feSpecularLighting') ;
  DvtSvgUtils.setAttrNullNS(elemSL2, 'in', elemGBResult) ;
  DvtSvgUtils.setAttrNullNS(elemSL2, 'surfaceScale', 5) ;
  DvtSvgUtils.setAttrNullNS(elemSL2, 'specularConstant', 1) ;//.85);
  DvtSvgUtils.setAttrNullNS(elemSL2, 'specularExponent', 20) ;
  //use pure white, and later convert to specified color
  DvtSvgUtils.setAttrNullNS(elemSL2, 'lighting-color', '#ffffff');//shadowRgb) ;
  var elemSL2Result = filtContext.createResultId('specLight');
  DvtSvgUtils.setAttrNullNS(elemSL2, 'result', elemSL2Result) ;
  
  var elemDL2 = DvtSvgShapeUtils.createElement('feDistantLight') ;
  DvtSvgUtils.setAttrNullNS(elemDL2, 'azimuth', -(angle + 180)) ;
  //DvtSvgUtils.setAttrNullNS(elemDL2, 'elevation', 0) ;
  
  //only keep part of specular shadow within original shape
  var elemC2 = DvtSvgShapeUtils.createElement('feComposite') ;
  DvtSvgUtils.setAttrNullNS(elemC2, 'operator', 'in') ;
  DvtSvgUtils.setAttrNullNS(elemC2, 'in', elemSL2Result) ;
  DvtSvgUtils.setAttrNullNS(elemC2, 'in2', 'SourceAlpha') ;
  var elemC2Result = filtContext.createResultId('comp');
  DvtSvgUtils.setAttrNullNS(elemC2, 'result', elemC2Result) ;

  //convert white specular hilight to correct color
  var elemCM2 = DvtSvgShapeUtils.createElement('feColorMatrix') ;
  DvtSvgUtils.setAttrNullNS(elemCM2, 'in', elemC2Result) ;
  DvtSvgUtils.setAttrNullNS(elemCM2, 'type', 'matrix') ;
  DvtSvgUtils.setAttrNullNS(elemCM2, 'values', (shadowRed/255) + ' 0 0 0 0 ' +
                                        '0 ' + (shadowGreen/255) + ' 0 0 0 ' +
                                        '0 0 ' + (shadowBlue/255) + ' 0 0 ' +
                                        //'0 0 0 ' + shadowAlpha + ' 0') ;
                                        '0 0 0 1 0') ;
  var elemCM2Result = filtContext.createResultId('colMat');
  DvtSvgUtils.setAttrNullNS(elemCM2, 'result', elemCM2Result) ;
  
  filtContext.getResultIdsOver().push(elemCMResult);
  filtContext.getResultIdsOver().push(elemCM2Result);
  
  DvtSvgUtils.appendChildElem(filt, elemGB) ;
  DvtSvgUtils.appendChildElem(filt, elemSL) ;
  DvtSvgUtils.appendChildElem(elemSL, elemDL) ;
  DvtSvgUtils.appendChildElem(filt, elemC1) ;
  DvtSvgUtils.appendChildElem(filt, elemCM);
  DvtSvgUtils.appendChildElem(filt, elemSL2) ;
  DvtSvgUtils.appendChildElem(elemSL2, elemDL2) ;
  DvtSvgUtils.appendChildElem(filt, elemC2) ;
  DvtSvgUtils.appendChildElem(filt, elemCM2);
};*/

/**
  * @protected
  */
DvtSvgBevelUtils.CreateInnerFilterPrimitives2 = function(filt, bevel, filtContext, boundsRect)
{
  var distance = bevel._distance;
  var angle = bevel._angle;
  var hilightRed   = DvtColorUtils.getRed(bevel._hilightRgba) ;
  var hilightGreen = DvtColorUtils.getGreen(bevel._hilightRgba) ;
  var hilightBlue  = DvtColorUtils.getBlue(bevel._hilightRgba) ;
  var hilightAlpha = DvtColorUtils.getAlpha(bevel._hilightRgba) ;
  var hilightRgb = DvtColorUtils.makeRGB(hilightRed, hilightGreen, hilightBlue);
  var shadowRed   = DvtColorUtils.getRed(bevel._shadowRgba) ;
  var shadowGreen = DvtColorUtils.getGreen(bevel._shadowRgba) ;
  var shadowBlue  = DvtColorUtils.getBlue(bevel._shadowRgba) ;
  var shadowAlpha = DvtColorUtils.getAlpha(bevel._shadowRgba) ;
  var shadowRgb = DvtColorUtils.makeRGB(shadowRed, shadowGreen, shadowBlue);
  //var strength = bevel._strength;
  var blurX = bevel._blurX * .5;
  var blurY = bevel._blurY * .5;
  
  //if we have a boundsRect, increase the size of the filter so
  //that the temporary outer glow has room to display outside the shape
  if (boundsRect)
  {
    var xRatio = (2 * blurX / boundsRect.w) * 100;
    var yRatio = (2 * blurY / boundsRect.h) * 100;
    if (filtContext.getRegionPctRect().x > -xRatio)
    {
      filtContext.getRegionPctRect().x = -xRatio;
    }
    if (filtContext.getRegionPctRect().y > -yRatio)
    {
      filtContext.getRegionPctRect().y = -yRatio;
    }
    if (filtContext.getRegionPctRect().w < (100 + 2 * xRatio))
    {
      filtContext.getRegionPctRect().w = (100 + 2 * xRatio);
    }
    if (filtContext.getRegionPctRect().h < (100 + 2 * yRatio))
    {
      filtContext.getRegionPctRect().h = (100 + 2 * yRatio);
    }
  }
  
  //create an outer glow on the shape
  var elemGB  = DvtSvgShapeUtils.createElement('feGaussianBlur') ;
  DvtSvgUtils.setAttrNullNS(elemGB, 'in', 'SourceAlpha') ;
  DvtSvgUtils.setAttrNullNS(elemGB, 'stdDeviation', blurX + " " + blurY) ;
  var elemGBResult = filtContext.createResultId('blur');
  DvtSvgUtils.setAttrNullNS(elemGB, 'result', elemGBResult) ;
  
  //shift the outer glow to expose some of the original shape along
  //the edge we want to hilight
  var angleRads = angle * Math.PI / 180;
  var dx = distance * Math.cos(angleRads);
  var dy = distance * Math.sin(angleRads);
  var elemO = DvtSvgShapeUtils.createElement('feOffset') ;
  DvtSvgUtils.setAttrNullNS(elemO, 'dx', dx) ;
  DvtSvgUtils.setAttrNullNS(elemO, 'dy', dy) ;
  DvtSvgUtils.setAttrNullNS(elemO, 'in', elemGBResult) ;
  var elemOResult = filtContext.createResultId('offset');
  DvtSvgUtils.setAttrNullNS(elemO, 'result', elemOResult) ;
  
  //create a flood of the hilight color
  var elemF1 = DvtSvgShapeUtils.createElement('feFlood') ;
  DvtSvgUtils.setAttrNullNS(elemF1, 'in', 'SourceAlpha') ;
  DvtSvgUtils.setAttrNullNS(elemF1, 'flood-opacity', hilightAlpha / 3) ;
  DvtSvgUtils.setAttrNullNS(elemF1, 'flood-color', hilightRgb) ;
  var elemF1Result = filtContext.createResultId('flood');
  DvtSvgUtils.setAttrNullNS(elemF1, 'result', elemF1Result) ;
  
  //keep the part of the flood outside the shifted glow
  var elemC1 = DvtSvgShapeUtils.createElement('feComposite') ;
  DvtSvgUtils.setAttrNullNS(elemC1, 'operator', 'out') ;
  DvtSvgUtils.setAttrNullNS(elemC1, 'in', elemF1Result) ;
  DvtSvgUtils.setAttrNullNS(elemC1, 'in2', elemOResult) ;
  var elemC1Result = filtContext.createResultId('comp');
  DvtSvgUtils.setAttrNullNS(elemC1, 'result', elemC1Result) ;
  
  //keep the part inside the original shape
  var elemC2 = DvtSvgShapeUtils.createElement('feComposite') ;
  DvtSvgUtils.setAttrNullNS(elemC2, 'operator', 'in');
  DvtSvgUtils.setAttrNullNS(elemC2, 'in', elemC1Result) ;
  DvtSvgUtils.setAttrNullNS(elemC2, 'in2', 'SourceGraphic') ;
  var elemC2Result = filtContext.createResultId('comp');
  DvtSvgUtils.setAttrNullNS(elemC2, 'result', elemC2Result) ;
  
  //shift the outer glow to expose some of the original shape along
  //the edge we want to shadow
  var elemO2 = DvtSvgShapeUtils.createElement('feOffset') ;
  DvtSvgUtils.setAttrNullNS(elemO2, 'dx', -dx) ;
  DvtSvgUtils.setAttrNullNS(elemO2, 'dy', -dy) ;
  DvtSvgUtils.setAttrNullNS(elemO2, 'in', elemGBResult) ;
  var elemO2Result = filtContext.createResultId('offset');
  DvtSvgUtils.setAttrNullNS(elemO2, 'result', elemO2Result) ;
  
  //create a flood of the shadow color
  var elemF2 = DvtSvgShapeUtils.createElement('feFlood') ;
  DvtSvgUtils.setAttrNullNS(elemF2, 'in', 'SourceAlpha') ;
  DvtSvgUtils.setAttrNullNS(elemF2, 'flood-opacity', shadowAlpha * 2) ;
  DvtSvgUtils.setAttrNullNS(elemF2, 'flood-color', shadowRgb) ;
  var elemF2Result = filtContext.createResultId('flood');
  DvtSvgUtils.setAttrNullNS(elemF2, 'result', elemF2Result) ;
  
  //keep the part of the flood outside the shifted glow
  var elemC3 = DvtSvgShapeUtils.createElement('feComposite') ;
  DvtSvgUtils.setAttrNullNS(elemC3, 'operator', 'out') ;
  DvtSvgUtils.setAttrNullNS(elemC3, 'in', elemF2Result) ;
  DvtSvgUtils.setAttrNullNS(elemC3, 'in2', elemO2Result) ;
  var elemC3Result = filtContext.createResultId('comp');
  DvtSvgUtils.setAttrNullNS(elemC3, 'result', elemC3Result) ;
  
  //keep the part inside the original shape
  var elemC4 = DvtSvgShapeUtils.createElement('feComposite') ;
  DvtSvgUtils.setAttrNullNS(elemC4, 'operator', 'in');
  DvtSvgUtils.setAttrNullNS(elemC4, 'in', elemC3Result) ;
  DvtSvgUtils.setAttrNullNS(elemC4, 'in2', 'SourceGraphic') ;
  var elemC4Result = filtContext.createResultId('comp');
  DvtSvgUtils.setAttrNullNS(elemC4, 'result', elemC4Result) ;
  
  filtContext.getResultIdsOver().push(elemC2Result);
  filtContext.getResultIdsOver().push(elemC4Result);

  DvtSvgUtils.appendChildElem(filt, elemGB) ;
  DvtSvgUtils.appendChildElem(filt, elemO) ;
  DvtSvgUtils.appendChildElem(filt, elemF1) ;
  DvtSvgUtils.appendChildElem(filt, elemC1) ;
  DvtSvgUtils.appendChildElem(filt, elemC2) ;
  DvtSvgUtils.appendChildElem(filt, elemO2) ;
  DvtSvgUtils.appendChildElem(filt, elemF2) ;
  DvtSvgUtils.appendChildElem(filt, elemC3) ;
  DvtSvgUtils.appendChildElem(filt, elemC4) ;
};
// Copyright (c) 2011, 2023, Oracle and/or its affiliates.
/*---------------------------------------------------------------------------*/
/*   DvtSvgGlowUtils    A static class for SVG glow effect property          */
/*                      manipulation.                                        */
/*---------------------------------------------------------------------------*/
/**
  *   DvtSvgGlowUtils    A static class for SVG glow effect property manipulation.
  *   @class DvtSvgGlowUtils 
  *   @constructor
  */
var  DvtSvgGlowUtils = function()
{} ;

DvtObj.createSubclass(DvtSvgGlowUtils, DvtObj, "DvtSvgGlowUtils");

/**
  *  Static method to create an SVG filter element and apply glow properties to it.
  *  @param {DvtGlow}  glow  DvtGlow object
  *  @param {DvtSvgDisplayable}  svgDisplayable  display object to apply glow to
  *  @returns an SVG  &lt;filter&gt; element
  */
DvtSvgGlowUtils.createFilter = function(glow, svgDisplayable)
{
   //  The following outer glow filter is created:

   //   <filter id="dg1">
   //     <feColorMatrix type="matrix" values="0 0 0 red 0
   //                                          0 0 0 green 0
   //                                          0 0 0 blue 0
   //                                          0 0 0 alpha 0"/>
   //     <feGaussianBlur stdDeviation="6 6" result="blur1"/>
   //     <feMerge>
   //       <feMergeNode in="blur1"/>
   //       <feMergeNode in="SourceGraphic"/>
   //     </feMerge>
   //   </filter>
   
   
   //   The following inner glow filter is created:
   
   //   <filter id="dg1">
   //     <feGaussianBlur stdDeviation="3" result="blur1"/>
   //     <feComposite operator="in" in="SourceGraphic" in2="blur1" result="comp1"/>
   //     <feComposite operator="in" in2="comp1" result="comp2"/>
   //     <feFlood in="comp2" flood-opacity="1" flood-color="rgb(255,255,255)" result="flood1"/>
   //     <feBlend mode="normal" in="comp2" in2="flood1" result="blend1"/>
   //     <feComposite operator="in" in="blend1" in2="SourceGraphic" result="comp3"/>
   //   </filter>
   
   var filt = DvtSvgGlowUtils.CreateFilterElem(glow);
   var filtContext = new DvtSvgFilterContext();
   
   DvtSvgGlowUtils.createFilterPrimitives(filt, glow, svgDisplayable, filtContext);
   
  var elemM  = DvtSvgShapeUtils.createElement('feMerge') ;
  var elemMN;
  if (filtContext.getResultIdsUnder().length > 0)
  {
    elemMN = DvtSvgShapeUtils.createElement('feMergeNode') ;
    DvtSvgUtils.setAttrNullNS(elemMN, 'in', filtContext.getResultIdsUnder()[0]) ;
    DvtSvgUtils.appendChildElem(elemM, elemMN) ;
  }
  elemMN = DvtSvgShapeUtils.createElement('feMergeNode') ;
  DvtSvgUtils.setAttrNullNS(elemMN, 'in', 'SourceGraphic') ;
  DvtSvgUtils.appendChildElem(elemM, elemMN) ;
  if (filtContext.getResultIdsOver().length > 0)
  {
    elemMN = DvtSvgShapeUtils.createElement('feMergeNode') ;
    DvtSvgUtils.setAttrNullNS(elemMN, 'in', filtContext.getResultIdsOver()[0]) ;
    DvtSvgUtils.appendChildElem(elemM, elemMN) ;
  }
  DvtSvgUtils.appendChildElem(filt, elemM) ;
   
   if (filtContext.getRegionPctRect().x != -10)
  {
    DvtSvgUtils.setAttrNullNS(filt, 'x', filtContext.getRegionPctRect().x + '%') ;
  }
  if (filtContext.getRegionPctRect().y != -10)
  {
    DvtSvgUtils.setAttrNullNS(filt, 'y', filtContext.getRegionPctRect().y + '%') ;
  }
  if (filtContext.getRegionPctRect().w != 120)
  {
    DvtSvgUtils.setAttrNullNS(filt, 'width', filtContext.getRegionPctRect().w + '%') ;
  }
  if (filtContext.getRegionPctRect().h != 120)
  {
    DvtSvgUtils.setAttrNullNS(filt, 'height', filtContext.getRegionPctRect().h + '%') ;
  }
   
   return filt;
};

DvtSvgGlowUtils.createFilterPrimitives = function(filt, effect, svgDisplayable, filtContext)
{
  if (effect._bInner)
  {
    DvtSvgGlowUtils.CreateInnerFilterPrimitives2(filt, effect, filtContext);
  }
  else
  {
    var boundsRect = svgDisplayable.getDimensions(svgDisplayable.getParent());
    DvtSvgGlowUtils.CreateOuterFilterPrimitives(filt, effect, filtContext, boundsRect);
  }
};

/**
  * @protected
  */
DvtSvgGlowUtils.CreateFilterElem = function(glow)
{
  var filt = DvtSvgShapeUtils.createElement('filter', glow._Id) ;
  return filt;
};

/**
  * @protected
  */
DvtSvgGlowUtils.CreateOuterFilterPrimitives = function(filt, glow, filtContext, boundsRect)
{
  var rgba  = glow._rgba ;
  var red   = DvtColorUtils.getRed(rgba)/255 ;
  var green = DvtColorUtils.getGreen(rgba)/255 ;
  var blue  = DvtColorUtils.getBlue(rgba)/255 ;
  var alpha = DvtColorUtils.getAlpha(rgba) ;
  var strength = glow._strength;
  //adjust blur values so that SVG behaves more like Flash
  var blurX = glow._blurX / 3;
  var blurY = glow._blurY / 3;

  //if we have a boundsRect, increase the size of the filter so
  //that the glow has room to display outside the shape
  if (boundsRect)
  {
    var xRatio = (2 * blurX / boundsRect.w) * 100;
    var yRatio = (2 * blurY / boundsRect.h) * 100;
    if (filtContext.getRegionPctRect().x > -xRatio)
    {
      filtContext.getRegionPctRect().x = -xRatio;
    }
    if (filtContext.getRegionPctRect().y > -yRatio)
    {
      filtContext.getRegionPctRect().y = -yRatio;
    }
    if (filtContext.getRegionPctRect().w < (100 + 2 * xRatio))
    {
      filtContext.getRegionPctRect().w = (100 + 2 * xRatio);
    }
    if (filtContext.getRegionPctRect().h < (100 + 2 * yRatio))
    {
      filtContext.getRegionPctRect().h = (100 + 2 * yRatio);
    }
  }

  var elemCM  = DvtSvgShapeUtils.createElement('feColorMatrix') ;
  DvtSvgUtils.setAttrNullNS(elemCM, 'in', 'SourceGraphic') ;
  DvtSvgUtils.setAttrNullNS(elemCM, 'type', 'matrix') ;
  DvtSvgUtils.setAttrNullNS(elemCM, 'values', '0 0 0 ' + red   + ' 0 ' +
                                       '0 0 0 ' + green + ' 0 ' +
                                       '0 0 0 ' + blue  + ' 0 ' +
                                       '0 0 0 ' + alpha + ' 0') ;
  
  var elemGB  = DvtSvgShapeUtils.createElement('feGaussianBlur') ;
  DvtSvgUtils.setAttrNullNS(elemGB, 'stdDeviation', blurX + " " + blurY) ;
  var elemGBResult = filtContext.createResultId('blur');
  DvtSvgUtils.setAttrNullNS(elemGB, 'result', elemGBResult) ;
  
  //attempt to use the glow strength as multiplier for alpha
  //value in blur filter
  var elemCT;
  var elemCTResult;
  if (strength != 1) {
    //BUG FIX 14394122: use feComponentTransfer instead of feColorMatrix,
    //because Chrome has issues rendering the color matrix
    elemCT = DvtSvgShapeUtils.createElement('feComponentTransfer');
    DvtSvgUtils.setAttrNullNS(elemCT, 'in', elemGBResult) ;
    elemCTResult = filtContext.createResultId('compTran');
    DvtSvgUtils.setAttrNullNS(elemCT, 'result', elemCTResult) ;
    
    var elemFA = DvtSvgShapeUtils.createElement('feFuncA');
    DvtSvgUtils.setAttrNullNS(elemFA, 'type', 'linear') ;
    DvtSvgUtils.setAttrNullNS(elemFA, 'slope', "" + strength) ;
    DvtSvgUtils.appendChildElem(elemCT, elemFA);
  }
  
  if (elemCTResult) {
    filtContext.getResultIdsUnder().push(elemCTResult);
  }
  else {
    filtContext.getResultIdsUnder().push(elemGBResult);
  }

  DvtSvgUtils.appendChildElem(filt, elemCM) ;
  DvtSvgUtils.appendChildElem(filt, elemGB) ;
  if (elemCT) {
    DvtSvgUtils.appendChildElem(filt, elemCT) ;
  }
};

/**
  * @protected
  */
/*DvtSvgGlowUtils.CreateInnerFilterPrimitives = function(filt, glow, filtContext)
{
  var red   = DvtColorUtils.getRed(glow._rgba) ;
  var green = DvtColorUtils.getGreen(glow._rgba) ;
  var blue  = DvtColorUtils.getBlue(glow._rgba) ;
  var alpha = DvtColorUtils.getAlpha(glow._rgba) ;
  var rgb = DvtColorUtils.makeRGB(red, green, blue);
  
  //filtContext.getRegionPctRect().x = 0;
  //filtContext.getRegionPctRect().y = 0;
  //filtContext.getRegionPctRect().width = 100;
  //filtContext.getRegionPctRect().height = 100;
  
  var elemGB  = DvtSvgShapeUtils.createElement('feGaussianBlur') ;
  DvtSvgUtils.setAttrNullNS(elemGB, 'in', 'SourceGraphic') ;
  DvtSvgUtils.setAttrNullNS(elemGB, 'stdDeviation', glow._blurX + " " + glow._blurY) ;
  var elemGBResult = filtContext.createResultId('blur');
  DvtSvgUtils.setAttrNullNS(elemGB, 'result', elemGBResult) ;
  
  var elemC1  = DvtSvgShapeUtils.createElement('feComposite') ;
  DvtSvgUtils.setAttrNullNS(elemC1, 'operator', 'in') ;
  DvtSvgUtils.setAttrNullNS(elemC1, 'in', 'SourceGraphic') ;
  DvtSvgUtils.setAttrNullNS(elemC1, 'in2', elemGBResult) ;
  //if we add elemC2 below back in, then change the result
  //of this feComposite back to 'comp1'
  var elemC1Result = filtContext.createResultId('comp');
  /DvtSvgUtils.setAttrNullNS(/elemC1, 'result', elemC1Result) ;
  var elemC2Result = filtContext.createResultId('comp');
  DvtSvgUtils.setAttrNullNS(elemC1, 'result', elemC2Result) ;
  
  //can add this filter back in in order to strengthen the effect
  //(that's what it appears to do...)
  //var elemC2  = DvtSvgShapeUtils.createElement('feComposite') ;
  //DvtSvgUtils.setAttrNullNS(elemC2, 'operator', 'in') ;
  //DvtSvgUtils.setAttrNullNS(elemC2, 'in2', elemC1Result) ;
  //DvtSvgUtils.setAttrNullNS(elemC2, 'result', elemC2Result) ;
  
  var elemF1  = DvtSvgShapeUtils.createElement('feFlood') ;
  DvtSvgUtils.setAttrNullNS(elemF1, 'in', elemC2Result) ;
  DvtSvgUtils.setAttrNullNS(elemF1, 'flood-opacity', alpha) ;
  DvtSvgUtils.setAttrNullNS(elemF1, 'flood-color', rgb) ;
  var elemF1Result = filtContext.createResultId('flood');
  DvtSvgUtils.setAttrNullNS(elemF1, 'result', elemF1Result) ;
  
  var elemB1  = DvtSvgShapeUtils.createElement('feBlend') ;
  DvtSvgUtils.setAttrNullNS(elemB1, 'mode', 'normal') ;
  DvtSvgUtils.setAttrNullNS(elemB1, 'in', elemC2Result) ;
  DvtSvgUtils.setAttrNullNS(elemB1, 'in2', elemF1Result) ;
  var elemB1Result = filtContext.createResultId('blend');
  DvtSvgUtils.setAttrNullNS(elemB1, 'result', elemB1Result) ;
  
  var elemC3  = DvtSvgShapeUtils.createElement('feComposite') ;
  DvtSvgUtils.setAttrNullNS(elemC3, 'operator', 'in') ;
  DvtSvgUtils.setAttrNullNS(elemC3, 'in', elemB1Result) ;
  DvtSvgUtils.setAttrNullNS(elemC3, 'in2', 'SourceGraphic') ;
  //var elemC3Result = filtContext.createResultId('comp');
  //DvtSvgUtils.setAttrNullNS(elemC3, 'result', elemC3Result) ;
  
  DvtSvgUtils.appendChildElem(filt, elemGB) ;
  DvtSvgUtils.appendChildElem(filt, elemC1) ;
  //DvtSvgUtils.appendChildElem(filt, elemC2) ;
  DvtSvgUtils.appendChildElem(filt, elemF1) ;
  DvtSvgUtils.appendChildElem(filt, elemB1) ;
  DvtSvgUtils.appendChildElem(filt, elemC3) ;
};*/

/**
  * @protected
  */
DvtSvgGlowUtils.CreateInnerFilterPrimitives2 = function(filt, glow, filtContext)
{
  var red   = DvtColorUtils.getRed(glow._rgba) ;
  var green = DvtColorUtils.getGreen(glow._rgba) ;
  var blue  = DvtColorUtils.getBlue(glow._rgba) ;
  var alpha = DvtColorUtils.getAlpha(glow._rgba) ;
  var rgb = DvtColorUtils.makeRGB(red, green, blue);
  var strength = glow._strength;
  //adjust blur values so that SVG behaves more like Flash
  //NOTE: changed to factor of 2 for graph selection, because 
  //then the two inner glows use blurs of 1 and 2 instead of 
  //0.5 and 1 (SVG appears to treat 0.5 and 1 the same)
  var blurX = glow._blurX / 2;//4;
  var blurY = glow._blurY / 2;//4;
  
  var elemF1  = DvtSvgShapeUtils.createElement('feFlood') ;
  DvtSvgUtils.setAttrNullNS(elemF1, 'in', 'SourceGraphic') ;
  DvtSvgUtils.setAttrNullNS(elemF1, 'flood-opacity', alpha) ;
  DvtSvgUtils.setAttrNullNS(elemF1, 'flood-color', rgb) ;
  var elemF1Result = filtContext.createResultId('flood');
  DvtSvgUtils.setAttrNullNS(elemF1, 'result', elemF1Result) ;
  
  var elemC1  = DvtSvgShapeUtils.createElement('feComposite') ;
  DvtSvgUtils.setAttrNullNS(elemC1, 'operator', 'out') ;
  DvtSvgUtils.setAttrNullNS(elemC1, 'in', elemF1Result) ;
  DvtSvgUtils.setAttrNullNS(elemC1, 'in2', 'SourceGraphic') ;
  var elemC1Result = filtContext.createResultId('comp');
  DvtSvgUtils.setAttrNullNS(elemC1, 'result', elemC1Result) ;
  
  var elemGB  = DvtSvgShapeUtils.createElement('feGaussianBlur') ;
  DvtSvgUtils.setAttrNullNS(elemGB, 'in', elemC1Result) ;
  DvtSvgUtils.setAttrNullNS(elemGB, 'stdDeviation', blurX + " " + blurY) ;
  var elemGBResult = filtContext.createResultId('blur');
  DvtSvgUtils.setAttrNullNS(elemGB, 'result', elemGBResult) ;
  
  //attempt to use the glow strength as multiplier for alpha
  //value in blur filter
  var elemCT;
  var elemCTResult;
  if (strength != 1) {
    //BUG FIX 14394122: use feComponentTransfer instead of feColorMatrix,
    //because Chrome has issues rendering the color matrix
    elemCT = DvtSvgShapeUtils.createElement('feComponentTransfer');
    DvtSvgUtils.setAttrNullNS(elemCT, 'in', elemGBResult) ;
    elemCTResult = filtContext.createResultId('compTran');
    DvtSvgUtils.setAttrNullNS(elemCT, 'result', elemCTResult) ;
    
    var elemFA = DvtSvgShapeUtils.createElement('feFuncA');
    DvtSvgUtils.setAttrNullNS(elemFA, 'type', 'linear') ;
    DvtSvgUtils.setAttrNullNS(elemFA, 'slope', "" + strength) ;
    DvtSvgUtils.appendChildElem(elemCT, elemFA);
  }
  
  var elemC2  = DvtSvgShapeUtils.createElement('feComposite') ;
  //DvtSvgUtils.setAttrNullNS(elemC2, 'operator', 'atop') ;
  DvtSvgUtils.setAttrNullNS(elemC2, 'operator', 'in');
  if (elemCTResult) {
    DvtSvgUtils.setAttrNullNS(elemC2, 'in', elemCTResult) ;
  }
  else {
    DvtSvgUtils.setAttrNullNS(elemC2, 'in', elemGBResult) ;
  }
  DvtSvgUtils.setAttrNullNS(elemC2, 'in2', 'SourceGraphic') ;
  var elemC2Result = filtContext.createResultId('comp');
  DvtSvgUtils.setAttrNullNS(elemC2, 'result', elemC2Result) ;
  
  filtContext.getResultIdsOver().push(elemC2Result);
  
  //if elemC2 above uses operator='in' instead of 'atop', 
  //then can do one of these afterwards to get same result:
  //var elemM  = DvtSvgShapeUtils.createElement('feMerge') ;
  //var elemMN1 = DvtSvgShapeUtils.createElement('feMergeNode') ;
  //DvtSvgUtils.setAttrNullNS(elemMN1, 'in', 'SourceGraphic') ;
  //var elemMN2 = DvtSvgShapeUtils.createElement('feMergeNode') ;
  //DvtSvgUtils.setAttrNullNS(elemMN2, 'in', elemC2Result) ;
  //OR
  //var elemC3  = DvtSvgShapeUtils.createElement('feComposite') ;
  //DvtSvgUtils.setAttrNullNS(elemC3, 'operator', 'atop') ;
  //DvtSvgUtils.setAttrNullNS(elemC3, 'in', elemC2Result) ;
  //DvtSvgUtils.setAttrNullNS(elemC3, 'in2', 'SourceGraphic') ;
  
  DvtSvgUtils.appendChildElem(filt, elemF1) ;
  DvtSvgUtils.appendChildElem(filt, elemC1) ;
  DvtSvgUtils.appendChildElem(filt, elemGB) ;
  if (elemCT) {
    DvtSvgUtils.appendChildElem(filt, elemCT);
  }
  DvtSvgUtils.appendChildElem(filt, elemC2) ;
  
  //DvtSvgUtils.appendChildElem(filt, elemM) ;
  //DvtSvgUtils.appendChildElem(elemM, elemMN1) ;
  //DvtSvgUtils.appendChildElem(elemM, elemMN2) ;
  //OR
  //DvtSvgUtils.appendChildElem(filt, elemC3) ;
};
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*---------------------------------------------------------------------------*/
/*    DvtSvgGradientUtils    A static class for SVG gradient property manip- */
/*                           ulation.                                        */
/*---------------------------------------------------------------------------*/
/**
  *   DvtSvgGradientUtils    A static class for SVG gradient property manipulation.
  *   @class DvtSvgGradientUtils 
  *   @constructor
  */
var  DvtSvgGradientUtils = function()
{} ;

DvtObj.createSubclass(DvtSvgGradientUtils, DvtObj, "DvtSvgGradientUtils");

/**
  *  Static method to create an SVG element and apply gradient properties to it.
  *  @param {Object}  Either a DvtGradientFill or DvtGradientStroke derivative to apply.
  */
DvtSvgGradientUtils.createElem = function(grad, id)
{
   var bLinear = ((grad instanceof DvtLinearGradientFill) || (grad instanceof DvtLinearGradientStroke));
   var elemGrad = DvtSvgShapeUtils.createElement((bLinear? 'linearGradient' : 'radialGradient'), id) ;

   var i ;
   var arColors = grad.getColors() ;
   var arAlphas = grad.getAlphas() ;
   var arStops  = grad.getStops() ;
   var arBounds = grad.getBounds() ;
   var len      = arColors.length ;

   for (i = 0; i < len; i++) {
      var elem = DvtSvgShapeUtils.createElement('stop') ;
      DvtSvgUtils.setAttrNullNS(elem, 'offset', '' + (arStops[i] * 100) + '%') ;
      var color = arColors[i];
      if (color) {
        var alpha = arAlphas[i];
        // Workaround for Safari where versions < 5.1 show rgba values as black
        if (DvtAgent.isBrowserSafari() && color.indexOf("rgba") !==  - 1) {
          DvtSvgUtils.setAttrNullNS(elem, 'stop-color', DvtColorUtils.getRGB(color));
          // Use alpa in rgba value as a multiplier to the alpha set on the object as this is what svg does.
          if (alpha != null)
            DvtSvgUtils.setAttrNullNS(elem, 'stop-opacity', DvtColorUtils.getAlpha(color) * alpha);
          else 
            DvtSvgUtils.setAttrNullNS(elem, 'stop-opacity', DvtColorUtils.getAlpha(color));
        }
        else {
          DvtSvgUtils.setAttrNullNS(elem, 'stop-color', color);
          if (alpha != null)
            DvtSvgUtils.setAttrNullNS(elem, 'stop-opacity', alpha);
        }
      }
      DvtSvgUtils.appendChildElem(elemGrad, elem) ;
   }

   //  If no gradient bounding box specified, will use the object's boundary box.

   var bUseObjBBox = (! arBounds ||
                    ((arBounds[0]==0) && (arBounds[1]==0) && (arBounds[2]==0) && (arBounds[3]==0)) );

   //  The angle of rotation for SVG is clockwise, so must convert from the standard
   //  anti-clockwise convention used by the middle-tier xml. Rotation is
   //  at the mid-point of the bounding box.

   if (bLinear) {
     var  angle = grad.getAngle() ;

     var  x1 = '0%' ;
     var  y1 = '0%' ;
     var  x2 = '100%' ;
     var  y2 = '0%' ;

     var setGradientVector = true;
      
     if (bUseObjBBox) {

       // Set gradient vector for gradientUnits = "objectBoundingBox"
       // (the default value for gradientUnits).

       if (angle === 45) {
         y1 = '100%' ;
         x2 = '100%' ;
       }
       else if (angle === 90) {
         y1 = '100%' ;
         x2 = '0%' ;
       }
       else if (angle === 135) {
         x1 = '100%' ;
         x2 = '0%' ;
         y2 = '100%' ;
       }
       else if (angle === 270) {
         x2 = '0%' ;
         y2 = '100%' ;
       }
       else if (angle !== 0) {
         angle = -angle ;
         DvtSvgUtils.setAttrNullNS(elemGrad, 'gradientTransform', 'rotate(' + angle + ' ' + '.5 .5)' ) ;
         setGradientVector = false;  // no need to change the default gradient vector, since we are rotating the 
                                     // gradient via gradientTransform
       }
     } 
     else  {

      //  Apply specified bounding box

      // use gradientUnits = "userSpaceOnUse"; for when we want to account for bounding box
      // first rotate the gradient by the specified angle
      // then scale this gradient to the width and bounds specifed in arBounds
      // then translate the gradient
      // note that if the width and height of the gradient are not equal, then the actual angle of the
      // gradient is different from the specified angle argument
      // However, this is the same behavior as how Flash processes the bounding box argument
      // Finally, note that in SVG, the order of transform operations is right to left

      DvtSvgUtils.setAttrNullNS(elemGrad, 'gradientUnits', 'userSpaceOnUse') ;

      // set gradient vector to span the middle of the unit square
      x1 = '0';
      y1 = '0.5';
      x2 = '1';
      y2 = '0.5';

      var scaleX     = arBounds[2];
      var scaleY     = arBounds[3];
      var translateX = arBounds[0];
      var translateY = arBounds[1];
        
      angle = -angle;
      var rotateTransformStr = 'rotate(' + angle + ' ' + '.5 .5)'
      var scaleTransformStr = 'scale('+ scaleX + ' ' + scaleY + ')';
      var translateTransformStr = 'translate(' + translateX + ' ' + translateY + ')';
      var boundingBoxTransformStr = scaleTransformStr + ' ' + rotateTransformStr;
      
      if (translateX != 0 || translateY != 0) {
        boundingBoxTransformStr = translateTransformStr + ' ' + boundingBoxTransformStr;
      } 
        
      // in the case of a bounding box, to set up the gradient, we need both gradientTransform and
      // a gradient vector centered in the unit square

      DvtSvgUtils.setAttrNullNS(elemGrad, 'gradientTransform', boundingBoxTransformStr ) ;
     }

     if(setGradientVector)   {
       DvtSvgUtils.setAttrNullNS(elemGrad, 'x1', x1) ;
       DvtSvgUtils.setAttrNullNS(elemGrad, 'y1', y1) ;
       DvtSvgUtils.setAttrNullNS(elemGrad, 'x2', x2) ;
       DvtSvgUtils.setAttrNullNS(elemGrad, 'y2', y2) ;
     }
   }
   else {    //  Radial gradient

     if (! bUseObjBBox) {

       DvtSvgUtils.setAttrNullNS(elemGrad, 'gradientUnits', 'userSpaceOnUse') ;

       DvtSvgUtils.setAttrNullNS(elemGrad, 'cx', grad.getCx()) ;
       DvtSvgUtils.setAttrNullNS(elemGrad, 'cy', grad.getCy()) ;
       DvtSvgUtils.setAttrNullNS(elemGrad, 'fx', grad.getCx()) ;
       DvtSvgUtils.setAttrNullNS(elemGrad, 'fy', grad.getCy()) ;
       DvtSvgUtils.setAttrNullNS(elemGrad, 'r',  grad.getRadius()) ;
    }
   }
   return elemGrad ;
};
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*---------------------------------------------------------------------------*/
/*    DvtSvgImageFillUtils    A static class for SVG image fill property     */
/*                           manipulation.                                   */
/*---------------------------------------------------------------------------*/
/**
  *   A static class for SVG image fill property manipulation.
  *   @class DvtSvgImageFillUtils
  *   @constructor
  */
var  DvtSvgImageFillUtils = function()
{} ;

DvtObj.createSubclass(DvtSvgImageFillUtils, DvtObj, "DvtSvgImageFillUtils");

/**
  *  Static method to create an SVG element and apply image fill properties 
  *  to it.
  *  @param {DvtImageFill}
  */
DvtSvgImageFillUtils.createElem = function(imageFill, id)
{
   /* Example:
      <defs>
        <pattern id="img1" patternUnits="userSpaceOnUse" width="20" height="20" >
          <image xlink:href="400.png" x="0" y="0"  width="20" height="20" />
        </pattern>
      </defs>

      <rect x="0" y="0" height="100" width="100" fill="url(#img1)"/>
   */

   var elemPat = DvtSvgShapeUtils.createElement("pattern", id) ;
   var elemImg  = DvtSvgShapeUtils.createElement("image", id);

   var src = imageFill.getSrc() ;
   var bound = imageFill.getBound() ;
   var repeat = imageFill.getRepeat();

   if (bound) {
      DvtSvgUtils.setAttrNullNS(elemPat, 'x', bound.x);
      DvtSvgUtils.setAttrNullNS(elemPat, 'y', bound.y);

      DvtSvgUtils.setAttrNullNS(elemImg, "x", bound.x);
      DvtSvgUtils.setAttrNullNS(elemImg, "y", bound.y);

      DvtSvgUtils.setAttrNullNS(elemPat, 'width', bound.w);
      DvtSvgUtils.setAttrNullNS(elemPat, 'height', bound.h);

      DvtSvgUtils.setAttrNullNS(elemImg, "width", bound.w);
      DvtSvgUtils.setAttrNullNS(elemImg, "height", bound.h);
      DvtSvgUtils.setAttrNullNS(elemPat, "patternUnits", "userSpaceOnUse");

      /*
      // tile the background image
      if (! repeat || repeat != "no-repeat") {
      }
      // stretch out the background image
      else {
        DvtSvgUtils.setAttrNullNS(elemPat, "patternUnits", "objectBoundingBox");
        DvtSvgUtils.setAttrNullNS(elemPat, 'width', "100%");
        DvtSvgUtils.setAttrNullNS(elemPat, 'height', "100%");
      }
      */

      if (src) {
         DvtSvgUtils.setAttrNS(elemImg, DvtSvgImage.XLINK_NS, 'xlink:href', src) ;
      }
   }

   DvtSvgUtils.appendChildElem(elemPat, elemImg) ;

   return elemPat ;
};
// Copyright (c) 2011, 2023, Oracle and/or its affiliates.
/*---------------------------------------------------------------------------*/
/*    DvtSvgPatternFillUtils    A static class for SVG pattern fill property */
/*                              manipulation.                                */
/*---------------------------------------------------------------------------*/
/**
  *   A static class for creating SVG pattern fills.
  *   @class DvtSvgPatternFillUtils
  *   @constructor
  */
var  DvtSvgPatternFillUtils = function()
{} ;

DvtObj.createSubclass(DvtSvgPatternFillUtils, DvtObj, "DvtSvgPatternFillUtils");

/**   @private @final @type String  */
DvtSvgPatternFillUtils._SM_WIDTH       = 8 ;
/**   @private @final @type String  */
DvtSvgPatternFillUtils._SM_HEIGHT      = 8 ;
/**   @private @final @type String  */
DvtSvgPatternFillUtils._LG_WIDTH       = 16 ;
/**   @private @final @type String  */
DvtSvgPatternFillUtils._LG_HEIGHT      = 16 ;

/**
  *  Static method to create an SVG pattern element.
  *  @param {DvtPatternFill}  patternFill  pattern fill object
  *  @param {string}  id  pattern identifier
  */
DvtSvgPatternFillUtils.createElem = function(patternFill, id)
{
  var elemPat = DvtSvgShapeUtils.createElement("pattern", id) ;

  var pattern = patternFill.getPattern();
  var bSmall = DvtSvgPatternFillUtils.IsSmallPattern(pattern);
  var ww;
  var hh;
  if (bSmall)
  {
    ww = DvtSvgPatternFillUtils._SM_WIDTH ;
    hh = DvtSvgPatternFillUtils._SM_HEIGHT ;
  }
  else
  {
    ww = DvtSvgPatternFillUtils._LG_WIDTH ;
    hh = DvtSvgPatternFillUtils._LG_HEIGHT ;
  }

  DvtSvgUtils.setAttrNullNS(elemPat, "x", 0);
  DvtSvgUtils.setAttrNullNS(elemPat, "y", 0);
  DvtSvgUtils.setAttrNullNS(elemPat, "width", ww);
  DvtSvgUtils.setAttrNullNS(elemPat, "height", hh);
  DvtSvgUtils.setAttrNullNS(elemPat, "patternUnits", "userSpaceOnUse");
  var mat = patternFill.getMatrix();
  if (mat) {
    var sMat = 'matrix(' + mat.getA() + ',' + mat.getC() + ',' + mat.getB() + ',' + mat.getD() + ',' + mat.getTx() + ',' + mat.getTy() + ')';
    DvtSvgUtils.setAttrNullNS(elemPat, "patternTransform", sMat);
  }
  
  DvtSvgPatternFillUtils.CreatePatternElems(patternFill, elemPat);

  return elemPat ;
};

/**
  * Determine if the pattern is large or small.
  * 
  * @param {string}  pattern  constant representing the pattern
  * @protected
  */
DvtSvgPatternFillUtils.IsSmallPattern = function(pattern)
{
  return (pattern.charAt(0) === 's');
};

/**
  * Determine if the pattern is large or small.
  * 
  * @param {DvtPatternFill}  patternFill  pattern fill object
  * @param {object}  parentElem  parent pattern DOM element
  * @protected
  */
DvtSvgPatternFillUtils.CreatePatternElems = function(patternFill, parentElem)
{
  var rightX ;
  var bottomY ;
  var w ;
  var h ;
  var halfW ;  
  var halfH ; 
  var elem ;

  var pattern = patternFill.getPattern();
  var sColor = patternFill.getColor();
  var color = DvtColorUtils.getRGB(sColor);
  var alpha = DvtColorUtils.getAlpha(sColor);
  var sBackgroundColor = patternFill.getBackgroundColor();
  var backgroundColor = DvtColorUtils.getRGB(sBackgroundColor);
  var backgroundAlpha = DvtColorUtils.getAlpha(sBackgroundColor);
  
  var bSmall = DvtSvgPatternFillUtils.IsSmallPattern(pattern);
  if (bSmall)
  {
    rightX  = DvtSvgPatternFillUtils._SM_WIDTH  ;
    bottomY = DvtSvgPatternFillUtils._SM_HEIGHT  ;
    w       = DvtSvgPatternFillUtils._SM_WIDTH ;
    h       = DvtSvgPatternFillUtils._SM_HEIGHT ;
  }
  else
  {
    rightX  = DvtSvgPatternFillUtils._LG_WIDTH  ;
    bottomY = DvtSvgPatternFillUtils._LG_HEIGHT  ;
    w       = DvtSvgPatternFillUtils._LG_WIDTH ;
    h       = DvtSvgPatternFillUtils._LG_HEIGHT ;
  }
  
  //if a background color is specified, then fill a rect with that color
  //before drawing the pattern elements on top of it
  if (backgroundColor && (backgroundAlpha > 0))
  {
    elem = DvtSvgShapeUtils.createElement("rect");
    
    DvtSvgUtils.setAttrNullNS(elem, "stroke", backgroundColor);
    DvtSvgUtils.setAttrNullNS(elem, "stroke-opacity", backgroundAlpha);
    DvtSvgUtils.setAttrNullNS(elem, "stroke-width", 1);
    DvtSvgUtils.setAttrNullNS(elem, "fill", backgroundColor);
    DvtSvgUtils.setAttrNullNS(elem, "fill-opacity", backgroundAlpha);
    
    DvtSvgUtils.setAttrNullNS(elem, "x", 0);
    DvtSvgUtils.setAttrNullNS(elem, "y", 0);
    DvtSvgUtils.setAttrNullNS(elem, "width", rightX);
    DvtSvgUtils.setAttrNullNS(elem, "height", bottomY);
    
    DvtSvgUtils.appendChildElem(parentElem, elem);
  }

  if (pattern === DvtPatternFill.SM_DIAG_UP_LT || 
      pattern === DvtPatternFill.LG_DIAG_UP_LT)
  {
    elem = DvtSvgShapeUtils.createElement("path");
    DvtSvgUtils.setAttrNullNS(elem, "stroke", color);
    DvtSvgUtils.setAttrNullNS(elem, "stroke-opacity", alpha);
    DvtSvgUtils.setAttrNullNS(elem, "stroke-width", 1);
    DvtSvgUtils.setAttrNullNS(elem, "stroke-linecap", "square");
    
    DvtSvgUtils.setAttrNullNS(elem, "d", DvtPathUtils.moveTo(0, -h/2) + DvtPathUtils.lineTo(3*w/2, h) + 
                                   DvtPathUtils.moveTo(-w/2, 0) + DvtPathUtils.lineTo(w/2, h)); 
    
    DvtSvgUtils.appendChildElem(parentElem, elem);
  }
  else if (pattern === DvtPatternFill.SM_DIAG_UP_RT || 
           pattern === DvtPatternFill.LG_DIAG_UP_RT)
  {
    elem = DvtSvgShapeUtils.createElement("path");
    DvtSvgUtils.setAttrNullNS(elem, "stroke", color);
    DvtSvgUtils.setAttrNullNS(elem, "stroke-opacity", alpha);
    DvtSvgUtils.setAttrNullNS(elem, "stroke-width", 1);
    DvtSvgUtils.setAttrNullNS(elem, "stroke-linecap", "square");
    
    DvtSvgUtils.setAttrNullNS(elem, "d", DvtPathUtils.moveTo(-w/2, h) + DvtPathUtils.lineTo(w, -h/2) + 
                                   DvtPathUtils.moveTo(0, 3*h/2) + DvtPathUtils.lineTo(3*w/2, 0)); 
    
    DvtSvgUtils.appendChildElem(parentElem, elem);
  }
  else if (pattern === DvtPatternFill.SM_CROSSHATCH || 
           pattern === DvtPatternFill.LG_CROSSHATCH)
  {
    //use path instead of two lines because it's more compact (uses one
    //DOM element instead of two)
    elem = DvtSvgShapeUtils.createElement("path");

//  g.lineStyle((bSmall? 1 : 2), color) ;
    DvtSvgUtils.setAttrNullNS(elem, "stroke", color);
    DvtSvgUtils.setAttrNullNS(elem, "stroke-opacity", alpha);
    DvtSvgUtils.setAttrNullNS(elem, "stroke-width", 1);
    
    DvtSvgUtils.setAttrNullNS(elem, "d", "M0,0" + 
                                   "L" + rightX + "," + bottomY + 
                                   "M" + rightX + ",0" + 
                                   "L0," + bottomY);
    
    DvtSvgUtils.appendChildElem(parentElem, elem);
  }
  else if (pattern === DvtPatternFill.SM_CHECK || 
           pattern === DvtPatternFill.LG_CHECK)
  {
    halfW = w/2 ;  
    halfH = h/2 ;  
    
    //use path instead of two rects because it's more compact (uses one
    //DOM element instead of two)
    elem = DvtSvgShapeUtils.createElement("path");
    
    DvtSvgUtils.setAttrNullNS(elem, "stroke", color);
    DvtSvgUtils.setAttrNullNS(elem, "stroke-opacity", alpha);
    DvtSvgUtils.setAttrNullNS(elem, "stroke-width", 1);
    DvtSvgUtils.setAttrNullNS(elem, "fill", color);
    DvtSvgUtils.setAttrNullNS(elem, "fill-opacity", alpha);
    
    DvtSvgUtils.setAttrNullNS(elem, "d", "M" + halfW + ",0" + 
                                   "L" + w + ",0" + 
                                   "L" + w + "," + halfH + 
                                   "L" + halfW + "," + halfH + "Z" + 
                                   "M0," + halfH + 
                                   "L" + halfW + "," + halfH + 
                                   "L" + halfW + "," + h + 
                                   "L0," + h + "Z");
    
    DvtSvgUtils.appendChildElem(parentElem, elem);
  }
  else if (pattern === DvtPatternFill.SM_TRIANGLE_CHECK || 
           pattern === DvtPatternFill.LG_TRIANGLE_CHECK)
  {
    elem = DvtSvgShapeUtils.createElement("polygon");
    
    DvtSvgUtils.setAttrNullNS(elem, "stroke", color);
    DvtSvgUtils.setAttrNullNS(elem, "stroke-opacity", alpha);
    DvtSvgUtils.setAttrNullNS(elem, "stroke-width", 1);
    DvtSvgUtils.setAttrNullNS(elem, "fill", color);
    DvtSvgUtils.setAttrNullNS(elem, "fill-opacity", alpha);
    
    DvtSvgUtils.setAttrNullNS(elem, "points", "0," + bottomY + " " + 
                                        rightX + ",0 " + 
                                        rightX + "," + bottomY);
    
    DvtSvgUtils.appendChildElem(parentElem, elem);
  }
  else if (pattern === DvtPatternFill.SM_DIAMOND_CHECK || 
           pattern === DvtPatternFill.LG_DIAMOND_CHECK)
  {
    halfW = w/2 ;  
    halfH = h/2 ;  
    
    elem = DvtSvgShapeUtils.createElement("polygon");
    
    DvtSvgUtils.setAttrNullNS(elem, "stroke", color);
    DvtSvgUtils.setAttrNullNS(elem, "stroke-opacity", alpha);
    DvtSvgUtils.setAttrNullNS(elem, "stroke-width", 1);
    DvtSvgUtils.setAttrNullNS(elem, "fill", color);
    DvtSvgUtils.setAttrNullNS(elem, "fill-opacity", alpha);
    
    DvtSvgUtils.setAttrNullNS(elem, "points", "0," + halfH + " " + 
                                        halfW + ",0 " + 
                                        rightX + "," + halfW + " " + 
                                        halfW + "," + bottomY);
    
    DvtSvgUtils.appendChildElem(parentElem, elem);
  }
};
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*---------------------------------------------------------------------------*/
/*   DvtSvgShadowUtils    A static class for SVG drop shadow property manip- */
/*                        ulation.                                           */
/*---------------------------------------------------------------------------*/
/**
  *  A static class for SVG drop shadow property manipulation.
  *  @class DvtSvgShadowUtils
  *  @constructor
  */
var  DvtSvgShadowUtils = function()
{} ;

DvtObj.createSubclass(DvtSvgShadowUtils, DvtObj, "DvtSvgShadowUtils");

/**
 * @private
 * @final
 */
DvtSvgShadowUtils.RADS_PER_DEGREE = (Math.PI / 180);


/**
  *  Static method to create an SVG filter element and apply shadow properties to it.
  *  @param {DvtShadow}
  *  @returns {DOM_Element}  An SVG  &lt;filter&gt; element
  */
DvtSvgShadowUtils.createFilter = function(shadow, displayable)
{
   //  The following filter is created:

   //   <filter id="ds1">
   //     <feColorMatrix type="matrix" values="0 0 0 red 0
   //                                          0 0 0 green 0
   //                                          0 0 0 blue 0
   //                                          0 0 0 alpha 0">
   //     </feColorMatrix>
   //     <feGaussianBlur stdDeviation="5.166666666666666" result="blur1"></feGaussianBlur>
   //     <feOffset dx="5.75" dy="5.75" in="blur1" result="offset"></feOffset>
   //     <feMerge>
   //       <feMergeNode in="offset"></feMergeNode>
   //       <feMergeNode in="SourceGraphic"></feMergeNode>
   //     </feMerge>
   //   </filter>

   var filt ;

   if (! shadow.isLocked()) {
     var filtContext = new DvtSvgFilterContext();
     filt = DvtSvgShapeUtils.createElement('filter', shadow._Id) ;
     DvtSvgShadowUtils.createFilterPrimitives(filt, shadow, displayable, filtContext);
     
     var elemM  = DvtSvgShapeUtils.createElement('feMerge') ;
     var elemMN1 = DvtSvgShapeUtils.createElement('feMergeNode') ;
     DvtSvgUtils.setAttrNullNS(elemMN1, 'in', filtContext.getResultIdsUnder()[0]) ;
     var elemMN2 = DvtSvgShapeUtils.createElement('feMergeNode') ;
     DvtSvgUtils.setAttrNullNS(elemMN2, 'in', 'SourceGraphic') ;
     
     DvtSvgUtils.appendChildElem(filt, elemM) ;
     DvtSvgUtils.appendChildElem(elemM, elemMN1) ;
     DvtSvgUtils.appendChildElem(elemM, elemMN2) ;
   }
   
   return filt;
};

DvtSvgShadowUtils.createFilterPrimitives = function(filt, shadow, svgDisplayable, filtContext)
{
   //BUG FIX #12407873: create inner shadow if necessary
   if (shadow._bInner)
   {
     DvtSvgShadowUtils.CreateInnerFilterPrimitives(filt, shadow, svgDisplayable, filtContext);
   }
   else
   {
     DvtSvgShadowUtils.CreateOuterFilterPrimitives(filt, shadow, svgDisplayable, filtContext);
   }
};

/**
 * @protected
 */
DvtSvgShadowUtils.CreateOuterFilterPrimitives = function(filt, shadow, svgDisplayable, filtContext)
{
   //if (! shadow.isLocked()) {

     var rgba  = shadow._rgba ;
     var red   = DvtColorUtils.getRed(rgba)/255 ;
     var green = DvtColorUtils.getGreen(rgba)/255 ;
     var blue  = DvtColorUtils.getBlue(rgba)/255 ;
     var alpha = DvtColorUtils.getAlpha(rgba) ;
     var strength = shadow._strength;
     
     var origBlurX = shadow._blurX;
     var origBlurY = shadow._blurY;
     //adjust blur values so that SVG behaves more like Flash
     //BUG FIX #12661565: only calculate blur if blur is not set to 0
     var blurX = 0;
     var blurY = 0;
     if (shadow._blurX > 0)
     {
       blurX = Math.max(shadow._blurX / 3, 1);
     }
     if (shadow._blurY > 0)
     {
       blurY = Math.max(shadow._blurY / 3, 1);
     }
     var distance = shadow._distance;
     var angleDegs = shadow._angle;
     
     var angleRads = angleDegs * DvtSvgShadowUtils.RADS_PER_DEGREE;
     var dx = Math.cos(angleRads) * distance;
     var dy = Math.sin(angleRads) * distance;

     //BUG FIX #12427741: 
     //if we have a boundsRect, increase the size of the filter so
     //that the shadow has room to display outside the shape
     var boundsRect = svgDisplayable.getDimensions(svgDisplayable.getParent());
     if (boundsRect)
     {
       //try to optimize based on how much of the shadow falls
       //on each side of the bounding box
       var absDistance = Math.abs(distance);
       //use two times the blur by default, because using it directly
       //still clips the shadow
       var padLeft = 2 * origBlurX;
       var padRight = 2 * origBlurX;
       var padTop = 2 * origBlurY;
       var padBottom = 2 * origBlurY;
       if (distance > 0)
       {
         padLeft -= absDistance;
         padTop -= absDistance;
         padRight += absDistance;
         padBottom += absDistance;
       }
       else if (distance < 0)
       {
         padLeft += absDistance;
         padTop += absDistance;
         padRight -= absDistance;
         padBottom -= absDistance;
       }
       if (padLeft < 0)
       {
         padLeft = 0;
       }
       if (padTop < 0)
       {
         padTop = 0;
       }
       if (padRight < 0)
       {
         padRight = 0;
       }
       if (padBottom < 0)
       {
         padBottom = 0;
       }
       var ratioLeft = (padLeft / boundsRect.w) * 100;
       var ratioRight = (padRight / boundsRect.w) * 100;
       var ratioTop = (padTop / boundsRect.h) * 100;
       var ratioBottom = (padBottom / boundsRect.h) * 100;
       if (filtContext.getRegionPctRect().x > -ratioLeft)
       {
         filtContext.getRegionPctRect().x = -ratioLeft;
       }
       if (filtContext.getRegionPctRect().y > -ratioTop)
       {
         filtContext.getRegionPctRect().y = -ratioTop;
       }
       if (filtContext.getRegionPctRect().w < (100 + ratioLeft + ratioRight))
       {
         filtContext.getRegionPctRect().w = (100 + ratioLeft + ratioRight);
       }
       if (filtContext.getRegionPctRect().h < (100 + ratioTop + ratioBottom))
       {
         filtContext.getRegionPctRect().h = (100 + ratioTop + ratioBottom);
       }
     }
     // The bounding box does not take into accoutn stroke-width. Need to adjust for DvtLine objects
     if (svgDisplayable instanceof DvtSvgPolyline) {
         var strokeWidth = svgDisplayable.getStroke().getWidth();
         // If stroke is wider than 1 pixel, adjust y coordinate and height of shadow bounding box
         if (strokeWidth > 1) {
             filtContext.getRegionPctRect().h = filtContext.getRegionPctRect().h + strokeWidth;
             filtContext.getRegionPctRect().y = filtContext.getRegionPctRect().y - strokeWidth;
         }
     }

     var elemCM  = DvtSvgShapeUtils.createElement('feColorMatrix') ;
     DvtSvgUtils.setAttrNullNS(elemCM, 'in', 'SourceGraphic') ;
     DvtSvgUtils.setAttrNullNS(elemCM, 'type', 'matrix') ;
     DvtSvgUtils.setAttrNullNS(elemCM, 'values', '0 0 0 ' + red   + ' 0 ' +
                                          '0 0 0 ' + green + ' 0 ' +
                                          '0 0 0 ' + blue  + ' 0 ' +
                                          '0 0 0 ' + alpha*strength + ' 0') ;
     //var elemCMResult = filtContext.createResultId('colorMatrix');
     //DvtSvgUtils.setAttrNullNS(elemCM, 'result', elemCMResult) ;

     var elemGB  = DvtSvgShapeUtils.createElement('feGaussianBlur') ;
     //include both numbers for stdDev when necessary
     var stdDev = blurX;
     if (blurX !== blurY)
     {
       stdDev += " " + blurY;
     }
     DvtSvgUtils.setAttrNullNS(elemGB, 'stdDeviation', stdDev) ;
     var elemGBResult = filtContext.createResultId('blur');
     DvtSvgUtils.setAttrNullNS(elemGB, 'result', elemGBResult) ;

     var elemO  = DvtSvgShapeUtils.createElement('feOffset') ;
     DvtSvgUtils.setAttrNullNS(elemO, 'dx', dx) ;
     DvtSvgUtils.setAttrNullNS(elemO, 'dy', dy) ;
     DvtSvgUtils.setAttrNullNS(elemO, 'in', elemGBResult) ;
     var elemOResult = filtContext.createResultId('offset');
     DvtSvgUtils.setAttrNullNS(elemO, 'result', elemOResult) ;
     
     filtContext.getResultIdsUnder().push(elemOResult);

     DvtSvgUtils.appendChildElem(filt, elemCM) ;
     DvtSvgUtils.appendChildElem(filt, elemGB) ;
     DvtSvgUtils.appendChildElem(filt, elemO) ;


// TDO <feMorphology operator="erode" in="blurred" radius="3" result="eroded"/>
   //}
};

/**
 * @protected
 */
DvtSvgShadowUtils.CreateInnerFilterPrimitives = function(filt, shadow, svgDisplayable, filtContext)
{
   var rgba  = shadow._rgba ;
   var red   = DvtColorUtils.getRed(rgba)/255 ;
   var green = DvtColorUtils.getGreen(rgba)/255 ;
   var blue  = DvtColorUtils.getBlue(rgba)/255 ;
   var rgb   = DvtColorUtils.makeRGB(red, green, blue);
   var alpha = DvtColorUtils.getAlpha(rgba) ;
   
   var origBlurX = shadow._blurX;
   var origBlurY = shadow._blurY;
   //adjust blur values so that SVG behaves more like Flash
   var blurX = Math.max(shadow._blurX / 2, 1);
   var blurY = Math.max(shadow._blurY / 2, 1);
   var distance = shadow._distance;
   var angleDegs = shadow._angle;
   var strength = shadow._strength;
   
   var angleRads = angleDegs * DvtSvgShadowUtils.RADS_PER_DEGREE;
   var dx = Math.cos(angleRads) * distance;
   var dy = Math.sin(angleRads) * distance;
  
   var elemF1  = DvtSvgShapeUtils.createElement('feFlood') ;
   DvtSvgUtils.setAttrNullNS(elemF1, 'in', 'SourceGraphic') ;
   DvtSvgUtils.setAttrNullNS(elemF1, 'flood-opacity', alpha) ;
   DvtSvgUtils.setAttrNullNS(elemF1, 'flood-color', rgb) ;
   var elemF1Result = filtContext.createResultId('flood');
   DvtSvgUtils.setAttrNullNS(elemF1, 'result', elemF1Result) ;
   
   var elemC1  = DvtSvgShapeUtils.createElement('feComposite') ;
   DvtSvgUtils.setAttrNullNS(elemC1, 'operator', 'out') ;
   DvtSvgUtils.setAttrNullNS(elemC1, 'in', elemF1Result) ;
   DvtSvgUtils.setAttrNullNS(elemC1, 'in2', 'SourceGraphic') ;
   //var elemC1Result = filtContext.createResultId('comp');
   //DvtSvgUtils.setAttrNullNS(elemC1, 'result', elemC1Result) ;

/*
   var elemCM  = DvtSvgShapeUtils.createElement('feColorMatrix') ;
   DvtSvgUtils.setAttrNullNS(elemCM, 'in', 'SourceGraphic') ;
   DvtSvgUtils.setAttrNullNS(elemCM, 'type', 'matrix') ;
   DvtSvgUtils.setAttrNullNS(elemCM, 'values', '0 0 0 ' + red   + ' 0 ' +
                                        '0 0 0 ' + green + ' 0 ' +
                                        '0 0 0 ' + blue  + ' 0 ' +
                                        '0 0 0 ' + alpha + ' 0') ;
   //var elemCMResult = filtContext.createResultId('colorMatrix');
   //DvtSvgUtils.setAttrNullNS(elemCM, 'result', elemCMResult) ;
*/

   var elemGB  = DvtSvgShapeUtils.createElement('feGaussianBlur') ;
   //include both numbers for stdDev when necessary
   var stdDev = blurX;
   if (blurX !== blurY)
   {
     stdDev += " " + blurY;
   }
   DvtSvgUtils.setAttrNullNS(elemGB, 'stdDeviation', stdDev) ;
   var elemGBResult = filtContext.createResultId('blur');
   DvtSvgUtils.setAttrNullNS(elemGB, 'result', elemGBResult) ;
   
   //attempt to use the shadow strength as multiplier for alpha
   //value in blur filter
   var elemCM2  = DvtSvgShapeUtils.createElement('feColorMatrix') ;
   DvtSvgUtils.setAttrNullNS(elemCM2, 'in', elemGBResult) ;
   DvtSvgUtils.setAttrNullNS(elemCM2, 'type', 'matrix') ;
   DvtSvgUtils.setAttrNullNS(elemCM2, 'values', '1 0 0 0 0 ' +
                                        '0 1 0 0 0 ' +
                                        '0 0 1 0 0 ' +
                                        '0 0 0 ' + strength + ' 0') ;
   var elemCM2Result = filtContext.createResultId('colMat');
   DvtSvgUtils.setAttrNullNS(elemCM2, 'result', elemCM2Result) ;

   var elemO  = DvtSvgShapeUtils.createElement('feOffset') ;
   DvtSvgUtils.setAttrNullNS(elemO, 'dx', dx) ;
   DvtSvgUtils.setAttrNullNS(elemO, 'dy', dy) ;
   DvtSvgUtils.setAttrNullNS(elemO, 'in', elemCM2Result) ;
   var elemOResult = filtContext.createResultId('offset');
   DvtSvgUtils.setAttrNullNS(elemO, 'result', elemOResult) ;
   
   var elemC2  = DvtSvgShapeUtils.createElement('feComposite') ;
   DvtSvgUtils.setAttrNullNS(elemC2, 'operator', 'in') ;
   DvtSvgUtils.setAttrNullNS(elemC2, 'in', elemOResult) ;
   DvtSvgUtils.setAttrNullNS(elemC2, 'in2', 'SourceGraphic') ;
   var elemC2Result = filtContext.createResultId('comp');
   DvtSvgUtils.setAttrNullNS(elemC2, 'result', elemC2Result) ;
   
   filtContext.getResultIdsOver().push(elemC2Result);

   DvtSvgUtils.appendChildElem(filt, elemF1) ;
   DvtSvgUtils.appendChildElem(filt, elemC1) ;
   //DvtSvgUtils.appendChildElem(filt, elemCM) ;
   DvtSvgUtils.appendChildElem(filt, elemGB) ;
   DvtSvgUtils.appendChildElem(filt, elemCM2) ;
   DvtSvgUtils.appendChildElem(filt, elemO) ;
   DvtSvgUtils.appendChildElem(filt, elemC2) ;
};
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*---------------------------------------------------------------------*/
/*  DvtSvgDisplayable      Svg implementation of DvtDisplayable        */
/*---------------------------------------------------------------------*/
/**
  *  SVG implementation of base displayable object DvtDisplayable.
  *  @extends DvtObj
  *  @class DvtSvgDisplayable is the SVG implementation base class for all displayable
  *  objects (derived from {@link DvtSvgDisplayable}).
  *  Must be superclassed  - do not use directly - all derivatives of DvtSvgDisplayable
  *  should be created via a specific platform factory implementation.
  *  @constructor  
  *  @param {String} id    The object ID (if undefined or null, '?' is used.
  *  @param {String} type  The SVG DOM element type.
  */
var   DvtSvgDisplayable = function(id, type)
{
  this._Init(id) ; 
};


DvtObj.createSubclass(DvtSvgDisplayable, DvtObj, "DvtSvgDisplayable") ;

/**
 * Array of SVG attributes that should be transferred to the outer element.
 * @private
 */
DvtSvgDisplayable._ATTRS_TRANSFERABLE_TO_OUTER = [/*'filter',*/ 'clip-path']

/**
 * A mapping between event types and the string needed to create a new event for dispatch
 * @private
 */
DvtSvgDisplayable._CREATE_EVENT_TYPE_MAP = {click:DvtMouseEvent.CLICK,
                                            mouseover:DvtMouseEvent.MOUSEOVER,
                                            mouseout:DvtMouseEvent.MOUSEOUT,
                                            mousemove:DvtMouseEvent.MOUSEMOVE,
                                            keydown:DvtKeyboardEvent.KEYDOWN,
                                            keyup:DvtKeyboardEvent.KEYUP
                                           };

/*---------------------------------------------------------------------*/
/*   _Init()                                                           */
/*---------------------------------------------------------------------*/
/**
  *  Object initializer.
  *  @protected
  */
DvtSvgDisplayable.prototype._Init = function(id, type)
{
  if (id) {
    this._id = id ;
  }
  this._Parent  = null ;
  this._elem  = DvtSvgShapeUtils.createElement(type, this._id) ;
  this._elem._obj = this;    //  pointer back to this object
  
  this._bVisible      = true;
  this._filter        = null;
  this._pixelHinting  = false ;
  this._cursor        = null;
  this._bMouseEnabled = true;
  this._alpha         = 1;
  this._matrix        = null;
  
  this._bubbleClosure = null;
  this._captureClosure = null;
  this._desc = null;
} ;




/*---------------------------------------------------------------------*/
/*    getElem()         Return the SVG element for this object         */
/*---------------------------------------------------------------------*/
/**
  *  Returns the SVG DOM element representing this displayable object.
  *  @returns {DOM_element}  An SVG DOM element representing this displayable object.
  */
DvtSvgDisplayable.prototype.getElem = function()
{
   return  this._elem ;
} ;

/**
 * Returns the outermost SVG DOM element of this displayable.  This should be used when
 * removing this displayable from the DOM.
 * @return {DvtSvgDisplayable}
 */
DvtSvgDisplayable.prototype.getOuterElem = function() {
  return this._outerElem ? this._outerElem : this.getElem();
}

/*---------------------------------------------------------------------*/
/*   get/setId()                                                       */
/*---------------------------------------------------------------------*/
/**
  *   @returns {String}  the id of this shape.
  */
DvtSvgDisplayable.prototype.getId = function()
{
  return this._id;
};

/**
  *   Sets the id of this shape.
  *   @param {String} id  The id for the shape.
  */
DvtSvgDisplayable.prototype.setId = function(id)
{
  if (this._id !== id) {
    if (id && id.length === 0) {
      id = null;
    }
    this._id = id;
     
    if (id)
      DvtSvgUtils.setAttrNullNS(this._elem, 'id', id);
    else
      DvtSvgUtils.removeAttrNullNS(this._elem, 'id');
  }
};


/*---------------------------------------------------------------------*/
/*   getObj()/setObj     Get/Set the controlling (js) object for this  */
/*                       implementation object.                        */
/*---------------------------------------------------------------------*/
/**
  *  @returns {Object}  the controlling (js) object.
  */
DvtSvgDisplayable.prototype.getObj = function()
{
   return this._obj ;
} ;

DvtSvgDisplayable.prototype.setObj = function(obj)
{
   this._obj = obj ;
} ;

/*---------------------------------------------------------------------*/
/*   get/setParent()                                                   */
/*---------------------------------------------------------------------*/
/**
  *  @returns {Object} the (js) parent of this shape.
  */
DvtSvgDisplayable.prototype.getParent = function()
{
   return this._obj.getParent() ;
} ;

/**
  *  Sets the (js) parent of this shape.
  *  @param {Object} parent
  */
DvtSvgDisplayable.prototype.setParent = function(parent)
{
   this._obj.setParent(parent) ;
} ;


/*---------------------------------------------------------------------*/
/*   get/setPixelHinting()                                             */
/*---------------------------------------------------------------------*/
/**
 *  @returns {boolean} the current pixel hinting state.
 */
DvtSvgDisplayable.prototype.getPixelHinting = function () {
  return this._pixelHinting;
};

/**
 * Enables/disables pixel hinting.
 * @param {boolean}  bHint  true if pixel hinting should be interpreted/applied by the
 * implementation platform.
 */
DvtSvgDisplayable.prototype.setPixelHinting = function (bHint) {
  this._pixelHinting = bHint ;
  if (bHint) {
    DvtSvgUtils.setAttrNullNS(this._elem, 'shape-rendering', 'crispEdges') ;
  }
  else {
    DvtSvgUtils.removeAttrNullNS(this._elem, 'shape-rendering') ;
  }
};

/*---------------------------------------------------------------------*/
/*   get/setVisible()                                                  */
/*---------------------------------------------------------------------*/
/**
  *  Gets the visibility of this object.
  *  @returns {boolean}  True if the object is visible, else false.
  */
DvtSvgDisplayable.prototype.getVisible = function()
{
   return this._bVisible ;
};

/**
  *  Enables/disables the visibility of this object.
  *  @param {boolean}  bVis  True if the object is to be visible, else false if
  *  it is to be hidden.
  */
DvtSvgDisplayable.prototype.setVisible = function(bVis)
{
   if (this._bVisible !== bVis) {
     this._bVisible = bVis ;
     var elem = (this instanceof DvtSvgMarker) ? this._elem : this.getElem();

     //Bug 16005714 - HTML5: VISIBILITY="VISIBLE" LEFT ON ELEMENT AFTER ANIMATION
     //Since the default value for visibility is true, just remove the 'visibility' attribute.
     //This change is needed to avoid the attribute visibility="visible" left on
     //this element after animation.
     //Note: in SVG, the visibility attribute doesn't behave like Flash, 
     //Ex: if a child has visibility="visible" and its container has visibility=hidden
     //the child is still visible
     if (bVis) {
       DvtSvgUtils.removeAttrNullNS(elem, 'visibility');
     }
     else {
       DvtSvgUtils.setAttrNullNS(elem, 'visibility', 'hidden') ;
     }
   }

};


/*---------------------------------------------------------------------*/
/*   setClipPath()                                                     */
/*---------------------------------------------------------------------*/
/**
 *  Sets a clipping region for this object.
 *  @param {DvtGraphClipPath}  cp  the DvtGraphClipPath object specifying the clipping region.
 */
DvtSvgDisplayable.prototype.setClipPath = function(cp) {
  // Bug 14764452 - PANELCARDS NOT CLIPPING IN DIAGRAM
  // if cp is null, clear the clip-path property, 
  // ideally the clipPath should also be removed from the global defs
  // if it's not used.
  if (! cp) {
    DvtSvgUtils.removeAttrNullNS(this.getOuterElem(), 'clip-path');
    return;
  }

  // Create an outer group if there is a matrix defined, since SVG applies transforms before clip paths.
  if(!this._outerElem && this._matrix)
    this._createOuterGroupElem();

  var id = cp.getId() ;
  if (id) {     // essential to have an id to reference
    var context  = this.getObj().getCtx() ;

    if (DvtSvgShapeUtils.addClipPath(cp, context))  {    // add to global defs
      // Set the clip path on the outer element of the shape
      DvtSvgUtils.setAttrNullNS(this.getOuterElem(), 'clip-path', 'url(#' + id + ')');
    }
  }    
};

DvtSvgDisplayable.prototype.addClipPath = function(cp, context) {
    return DvtSvgShapeUtils.addClipPath(cp, context);
}

/**
 * Returns the bounds of the displayable relative to its coordinate space.
 * @return {DvtRectangle} The bounds of the displayable relative to its coordinate space.
 */
DvtSvgDisplayable.prototype.getDimensions = function() {
  try {
     var bbox = this.getElem().getBBox();
  } catch(e) {
     return null;
  }
  //don't return bbox directly because we don't want calling code
  //to depend on platform-specific API, so instead turn it into 
  //a DvtRectangle
  return new DvtRectangle(bbox.x, bbox.y, bbox.width, bbox.height);
};

//BUG FIX 13842194:
/**
 * Returns the bounds of the displayable relative to its coordinate space,
 * including its stroke width.
 * @return {DvtRectangle} The bounds of the displayable relative to its coordinate space, including its stroke width.
 */
DvtSvgDisplayable.prototype.getDimensionsWithStroke = function() {
  return this.GetDimensionsWithStroke();
};

//BUG FIX 13842194:
DvtSvgDisplayable.prototype.GetDimensionsWithStroke = function(targetCoordinateSpace) {
  //get dims for the shape elem, and change coord space if necessary
  var dims = this.GetElemDimensionsWithStroke();
  if (!targetCoordinateSpace || targetCoordinateSpace === this.getObj())
    return dims;
  else { // Calculate the bounds relative to the target space
    return this.getObj().ConvertCoordSpaceRect(dims, targetCoordinateSpace);
  }
};

//BUG FIX 13842194:
DvtSvgDisplayable.prototype.GetElemDimensionsWithStroke = function() {
  var bbox = null;
  try {
    //always use this._elem, which should point to a concrete shape element
    bbox = this._elem.getBBox();
  } catch(e) {
    return null;
  }
  //don't return bbox directly because we don't want calling code
  //to depend on platform-specific API, so instead turn it into 
  //a DvtRectangle
  return new DvtRectangle(bbox.x, bbox.y, bbox.width, bbox.height);
};

/**
 * Returns true if any of the specified attribute names are present on the element.
 * @param {object} elem  The SVG DOM element.
 * @param {array} attrNames The array of attribute names to look for.
 * @protected
  */
DvtSvgDisplayable.HasAttributes = function(elem, attrNames) {
  if (attrNames)
  {
    var numAttrs = attrNames.length;
    for (var i = 0; i < numAttrs; i++)
    {
      if(DvtSvgUtils.getAttrNullNS(elem, attrNames[i]))
        return true;
    }
  }
  return false;
}

/**
 * Transfer relevant attributes from the original SVG DOM element to the new SVG DOM element.
 * @param {object} fromElem  The SVG DOM element.
 * @param {object} toElem  The new SVG DOM element.
 * @param {array} attrNames The array of attribute names to transfer.
 * @protected
  */
DvtSvgDisplayable.TransferAttributes = function(fromElem, toElem, attrNames)
{
  if (attrNames)
  {
    var attrName;
    var attrValue;
    var numAttrs = attrNames.length;
    for (var i = 0; i < numAttrs; i++)
    {
      attrName = attrNames[i];
      attrValue = DvtSvgUtils.getAttrNullNS(fromElem, attrName);
      if (attrValue)
      {
        DvtSvgUtils.removeAttrNullNS(fromElem, attrName);
        DvtSvgUtils.setAttrNullNS(toElem, attrName, attrValue);
        if (DvtAgent.isPlatformIE()) { // bug 14605036, prev removeAttributeNS()
          DvtSvgUtils.setAttrNullNS(fromElem, attrName, null);                 // fails for IE9 (9.0.8112.16421)
        }
      }
    }
  }
};

/**
 * Creates an outer group element to workaround issues with filters and clip paths.
 * @private
 */
DvtSvgDisplayable.prototype._createOuterGroupElem = function()
{
  if(this._outerElem) 
    return;
    
  var outerId = this._id ? this._id + '_outer' : null;  
  this._outerElem = DvtSvgShapeUtils.createElement('g', outerId);
    
  // Reparent the DOM elements
  var parent = this.getParent();
  if(parent) {
    var parentElem = parent.getImpl().getElem();
    parentElem.replaceChild(this._outerElem, this.getElem());
  }
  DvtSvgUtils.appendChildElem(this._outerElem, this.getElem());
  
  // Transfer attributes from the old outermost SVG element to the new outer element
  DvtSvgDisplayable.TransferAttributes(this.getElem(), this._outerElem, DvtSvgDisplayable._ATTRS_TRANSFERABLE_TO_OUTER)
}

/*---------------------------------------------------------------------*/
/*   Event Handling Support                                            */
/*---------------------------------------------------------------------*/

/**
 * Adds an event listener.
 * @param {String} type the event type
 * @param {String} useCapture whether the listener operates in the capture phase
 */
DvtSvgDisplayable.prototype.addListener = function(type, useCapture) {
  var listener = this._getListener(useCapture);
  // on keyboard events, add the listener to the component's wrapping div, since SVG 
  // DOM elements don't support keystrokes. 
  if(type == DvtKeyboardEvent.KEYUP || type == DvtKeyboardEvent.KEYDOWN)
  {
    if(this.getObj())
    {
      var context = this.getObj().getCtx();
      var stage = context.getStage();
      var wrappingDiv = stage.getImpl().getSVGRoot().parentNode;
      // allow support for multiple displayables to receive keyboard events
      // TODO: replace this with a singular compound event manager that
      //       will dispatch keyboard events to individual event managers
      if(!wrappingDiv._obj)
      {
        wrappingDiv._obj = []
      }
      if(DvtArrayUtils.getIndex(wrappingDiv._obj, this) == -1)
      {
        wrappingDiv._obj.push(this);
      }
      listener = DvtHtmlKeyboardListenerUtils.getListener(useCapture);
      DvtSvgUtils.addDomEventListener(wrappingDiv, type, listener, useCapture);
    }
  }
  else 
  {
    this.AddElemListener(type, listener, useCapture);
  }
}

DvtSvgDisplayable.prototype.AddElemListener = function(type, listener, useCapture) {
  DvtSvgUtils.addDomEventListener(this.getElem(), type, listener, useCapture);
  if (type == DvtTouchEvent.TOUCHEND) {
      DvtSvgUtils.addDomEventListener(this.getElem(), DvtTouchEvent.TOUCHCANCEL, listener, useCapture);
  }
};

/**
 * Removes an event listener.
 * @param {string} type the event type
 * @param {function} listener the function to call
 * @param {String} useCapture whether the listener operates in the capture phase
 */
DvtSvgDisplayable.prototype.removeListener = function(type, useCapture) {
  var listener = this._getListener(useCapture);
  if(type == DvtKeyboardEvent.KEYUP || type == DvtKeyboardEvent.KEYDOWN)
  {
    if(this.getObj())
    {
      var context = this.getObj().getCtx();
      var stage = context.getStage();
      var wrappingDiv = stage.getImpl().getSVGRoot().parentNode;
      DvtSvgUtils.removeDomEventListener(wrappingDiv, type, listener, useCapture);
      var index = DvtArrayUtils.getIndex(wrappingDiv._obj, this);
      if(index !== -1)
      {
        wrappingDiv._obj.splice(index);
      }
    }
  }
  else 
  {
    DvtSvgUtils.removeDomEventListener(this.getElem(), type, listener, useCapture);
  }
}

// TODO JSdoc
DvtSvgDisplayable.prototype._getListener = function(useCapture) {
  var thisRef = this;
  if(useCapture) {
    if (!this._captureClosure) {
      this._captureClosure = function (event) {
        thisRef._captureListener(event);
      };
    }
    return this._captureClosure;
  }
  else {
    if (!this._bubbleClosure) {
      this._bubbleClosure = function (event) {
        thisRef._bubbleListener(event);
      };
    }
    return this._bubbleClosure;
  }
}

/**
 * The event listener that is called by the implementation object's bubble phase listeners.
 * This function will wrap the event and delegate to the real event listeners.  
 * @param {object} event the DOM event object
 * @this {object} the platform object actively processing the event object with an event listener
 * @private
 */
DvtSvgDisplayable.prototype._bubbleListener = function(event) {
  if (DvtAgent.isPlatformIE()) {
      // Moving an object in the dom for IE causes mouse over events to fire if the moved item is under the mouse
      // Ensure mouse over is not repeatedly called in such a case
      if (event.type == DvtMouseEvent.MOUSEOVER) {
          if (this._bubbleHoverItem) {
              if (this._bubbleHoverItem == event.target) {
                  // Prevent infinite loop
                  return;
              } else {
                  //MOUSEOVER event on a DOM element stores the element in _bubbleHoverItem and MOUSEOUT event clears _bubbleHoverItem.
                  //If a MOUSEOVER event is not followed by MOUSEOUT event, _bubbleHoverItem won't be cleared.
                  //During the subsequent MOUSEOVER event if the event target is different than _bubbleHoverItem, 
                  //that indicates the browser have missed to fire MOUSEOUT event for previous DOM element stored in _bubbleHoverItem.
                  //So building a new MOUSEOUT event for _bubbleHoverItem.
                  var dvtEvent = DvtSvgEventFactory.newEvent(event, this.getObj().getCtx());
                  //Bug 17369581: Set the event as MOUSEOUT event, target as _bubbleHoverItem, relatedTarget as event.target
                  //This is equivalent to firing new MOUSEOUT event for _bubbleHoverItem
                  dvtEvent.modifyEventForIE(DvtMouseEvent.MOUSEOUT, this._bubbleHoverItem);
                  this.getObj().FireListener(dvtEvent, false);
              }
          }
          this._bubbleHoverItem = event.target;
      } else if (event.type == DvtMouseEvent.MOUSEOUT) {
          this._bubbleHoverItem = null;
      }
  }

  var dvtEvent = DvtSvgEventFactory.newEvent(event, this.getObj().getCtx());
  this.getObj().FireListener(dvtEvent, false);
}

/**
 * The event listener that is called by the implementation object's capture phase listeners.
 * This function will wrap the event and delegate to the real event listeners.  
 * @param {object} event the DOM event object
 * @this {object} the platform object actively processing the event object with an event listener
 * @private
 */
DvtSvgDisplayable.prototype._captureListener = function(event) {
  if (DvtAgent.isPlatformIE()) {
      // Moving an object in the dom for IE causes mouse over events to fire if the moved item is under the mouse
      // Ensure mouse over is not repeatedly called in such a case
      if (event.type == DvtMouseEvent.MOUSEOVER) {
          if (this._captureHoverItem) {
              if (this._captureHoverItem == event.target) {
                  // Prevent infinite loop
                  return;
              } else {
                  //MOUSEOVER event on a DOM element stores the element in _captureHoverItem and MOUSEOUT event clears _captureHoverItem.
                  //If a MOUSEOVER event is not followed by MOUSEOUT event, _captureHoverItem won't be cleared.
                  //During the subsequent MOUSEOVER event if the event target is different than _captureHoverItem, 
                  //that indicates the browser have missed to fire MOUSEOUT event for previous DOM element stored in _captureHoverItem.
                  //So building a new MOUSEOUT event for _captureHoverItem.
                  var dvtEvent = DvtSvgEventFactory.newEvent(event, this.getObj().getCtx());
                  //Bug 17369581: Set the event as MOUSEOUT event, target as _captureHoverItem, relatedTarget as event.target
                  //This is equivalent to firing new MOUSEOUT event for _captureHoverItem
                  dvtEvent.modifyEventForIE(DvtMouseEvent.MOUSEOUT, this._captureHoverItem);
                  this.getObj().FireListener(dvtEvent, true);
              }
          }
          this._captureHoverItem = event.target;
      } else if (event.type == DvtMouseEvent.MOUSEOUT) {
          this._captureHoverItem = null;
      }
  }
  var dvtEvent = DvtSvgEventFactory.newEvent(event, this.getObj().getCtx());
  this.getObj().FireListener(dvtEvent, true);
}


/*-------------------------------------------------------------------------*/
/*   CSS Style Support                                                     */
/*-------------------------------------------------------------------------*/

/**
 * @returns {DvtCssStyle} the DvtCSSStyle of this object.
 */ 
DvtSvgDisplayable.prototype.getCSSStyle = function()
{
  return this._cssStyle;
}

/**
 * Sets the DvtCSSStyle of this object.
 * @param {DvtCssStyle} style The DvtCSSStyle of this object.
 */
DvtSvgDisplayable.prototype.setCSSStyle = function(style)
{
  this._cssStyle = style;
}


/**
 * Sets the cursor on this object.
 * @param {String} cursor type
 */
DvtSvgDisplayable.prototype.setCursor = function(cursorType)
{
  this._cursor = cursorType;
  if (cursorType)
  {
    DvtSvgUtils.setAttrNullNS(this.getElem(), "cursor", cursorType);
  }
  else
  {
    DvtSvgUtils.removeAttrNullNS(this.getElem(), "cursor");
  }
};


/**
 * Gets the cursor used on this object.
 * @type {String}
 */
DvtSvgDisplayable.prototype.getCursor = function()
{
  return this._cursor;
};


/**
 * Sets whether mouse events are enabled on this object.
 * @param {boolean} whether mouse events are enabled
 */
DvtSvgDisplayable.prototype.setMouseEnabled = function(bEnabled)
{
  this._bMouseEnabled = bEnabled;
  var val;
  if (bEnabled)
  {
    val = "visiblePainted";
  }
  else
  {
    val = "none";
  }
  DvtSvgUtils.setAttrNullNS(this.getElem(), "pointer-events", val);
};
    

/**
 * Gets whether mouse events are enabled on this object.
 * @type {boolean}
 */
DvtSvgDisplayable.prototype.isMouseEnabled = function()
{
  return this._bMouseEnabled;
};


/*---------------------------------------------------------------------*/
/*   get/setAlpha()                                                    */
/*---------------------------------------------------------------------*/
/**
  *  Returns the alpha channel value.
  *  @type number  
  *  @returns A value between 0 (invisible) and 1 (opaque).
  */
DvtSvgDisplayable.prototype.getAlpha = function()
{
   return this._alpha;
};

/**
  *  Sets the alpha.
  *  @param {number} alpha  A value between 0 (invisible) and 1 (opaque).
  *  @returns nothing
  */
DvtSvgDisplayable.prototype.setAlpha = function(alpha)
{
   //when animating alpha, small values are turned into strings like
   //"3.145e-8", which SVG incorrectly clamps to 1, so just cut off
   //small values here and make them 0
   if (alpha < 0.00001)
     alpha = 0;
   
   if (alpha !== this._alpha)
   {
     this._alpha = alpha;
     DvtSvgUtils.setAttrNullNS(this.getElem(), 'opacity', this._alpha) ;
   }
};



/**
  *   Set the matrix object to use to transform this container.
  *   @param {DvtMatrix} mat  The matrix object to apply.
  */
DvtSvgDisplayable.prototype.setMatrix = function(mat)
{
  // Create an outer elem if needed
  if(!this._outerElem && DvtSvgDisplayable.HasAttributes(this.getElem(), DvtSvgDisplayable._ATTRS_TRANSFERABLE_TO_OUTER)) 
    this._createOuterGroupElem();

  this._matrix = mat ;
  //apply the new matrix if it's non-null
  if (mat)
  {
    var sMat = 'matrix(' + mat.getA() + ',' + mat.getC() + ',' + mat.getB() + ',' + mat.getD() + ',' + mat.getTx() + ',' + mat.getTy() + ')';
    
    //set the transform attribute on the outer SVG element of this shape
    this.SetElemMatrix(sMat);
    //set the vector-effect attribute to prevent the stroke-width
    //from scaling with the transform
    //NOTE: this is commented out for now because it causes strange rendering
    //artifacts when scaling to a small value
    //DvtSvgUtils.setAttrNullNS(this.getElem(), 'vector-effect', 'non-scaling-stroke') ;
  }
  //if clearing the existing matrix by setting a null or undefined mat,
  //then remove the existing attribute from the DOM
  else
  {
    var transformAttr = DvtSvgUtils.getAttrNullNS(this.getElem(), 'transform');
    if (transformAttr)
    {
      DvtSvgUtils.removeAttrNullNS(this.getElem(), 'transform');
      if (DvtAgent.isPlatformIE()) { // bug 14617946, prev removeAttributeNS()
        DvtSvgUtils.setAttrNullNS(this.getElem(), 'transform', null);        // fails for IE9 (9.0.8112.16421)
      }

    }
    //NOTE: this is commented out for now because it causes strange rendering
    //artifacts when scaling to a small value
    var vectorEffectAttr;// = DvtSvgUtils.getAttrNullNS(this.getElem(), 'vector-effect');
    if (vectorEffectAttr)
    {
      DvtSvgUtils.removeAttrNullNS(this.getElem(), 'vector-effect');
    }
  }
};


DvtSvgDisplayable.prototype.SetElemMatrix = function(sMat) {
  DvtSvgUtils.setAttrNullNS(this.getElem(), 'transform', sMat) ;
};


DvtSvgDisplayable.prototype.applyDrawEffects = function(effects)
{
  // Create an outer group if there is a matrix defined, since SVG applies transforms before filters.
  if(!this._outerElem && this._matrix)
    this._createOuterGroupElem();

  //remove current filter
  if (this._filter)
  {
    DvtSvgUtils.removeAttrNullNS(this.getOuterElem(), 'filter');
    this.getObj().getCtx().removeDefs(this._filter);
    this._filter = null;
  }
  
  //add new filter if necessary
  if (effects && effects.length > 0)
  {
    //Bug 12394679: Repaint bug in Firefox requires us to force a repaint after removing filter
    //At worst we will create n empty filters where n=# markers
    this._filter = DvtSvgFilterUtils.createFilter(effects, this);
    if (this._filter) {
        this.getObj().getCtx().appendDefs(this._filter);
        var filterId = DvtSvgUtils.getAttrNullNS(this._filter, 'id');
        DvtSvgUtils.setAttrNullNS(this.getOuterElem(), 'filter', 'url(#' + filterId + ')');
    }
  }
  
};


/**
 * Sets the WAI-ARIA role.
 * @param {String} role The role attribute value.
 */
DvtSvgDisplayable.prototype.setAriaRole = function(role) {
  if (role)
    DvtSvgUtils.setAttrNullNS(this.getElem(), 'role', role);
  else
    DvtSvgUtils.removeAttrNullNS(this.getElem(), 'role');
}

/**
 * Sets the WAI-ARIA property/state.
 * @param {String} property The property/state attribute name. The prefix "aria-" should be skipped.
 * @param {String} value The property/state attribute value.
 */
DvtSvgDisplayable.prototype.setAriaProperty = function(property, value) {
  if (value)
    DvtSvgUtils.setAttrNullNS(this.getElem(), 'aria-' + property, value);
  else
    DvtSvgUtils.removeAttrNullNS(this.getElem(), 'aria-' + property);
}


/**
 * Convert a point from stage coords to local coords.
 * @param {DvtPoint}  point  point in stage coords
 * @type DvtPoint
 */
DvtSvgDisplayable.prototype.stageToLocal = function(point)
{
   var displayable = this.getObj();
   var pathToStage = displayable.getPathToStage();
   var mat;
   var retPoint = point;
   for (var i = pathToStage.length - 1; i >= 0; i--)
   {
      displayable = pathToStage[i];
      mat = displayable.getMatrix().clone();
      mat.invert();
      retPoint = mat.transformPoint(retPoint);
   }
   return retPoint;
};

/**
 * Convert a point from local coords to stage coords.
 * @param {DvtPoint}  point  point in local coords
 * @type DvtPoint
 */
DvtSvgDisplayable.prototype.localToStage = function(point)
{
   var displayable = this.getObj();
   var pathToStage = displayable.getPathToStage();
   var mat;
   var retPoint = point;
   for (var i = 0; i < pathToStage.length; i++)
   {
      displayable = pathToStage[i];
      mat = displayable.getMatrix();
      retPoint = mat.transformPoint(retPoint);
   }
   return retPoint;
};

/**
 * Creates a copy of the svg content in the specified container
 * 
 * @param {DvtContainer} container the container for the copied content
 */
DvtSvgDisplayable.prototype.createCopy = function(container) 
{
   var clonedSvg = this.getElem().cloneNode(true);
   DvtSvgUtils.appendChildElem(container.getImpl().getElem(), clonedSvg);
   this._disableEvents(clonedSvg);

   //Bug 16005714 - HTML5: VISIBILITY="VISIBLE" LEFT ON ELEMENT AFTER ANIMATION
   //This is a potential backup for the real fix in setVisible.
   //when this method is called to perform a whole "node" transition for panelCard
   //the "visibility" attribute on this clone element can never be turned on/off later
   //need to move this attribute to the clonedContainer now
   /*
   var vis = DvtSvgUtils.getAttrNullNS(clonedSvg, "visibility");
   if (vis) {
     DvtSvgUtils.removeAttrNullNS(clonedSvg, "visibility") ;
     DvtSvgUtils.setAttrNullNS(container.getImpl().getElem(), 'visibility', vis);
   }
   */
}

DvtSvgDisplayable.prototype._disableEvents = function(svgElem) 
{
   var child = svgElem.firstChild;
    //loop over all children
    while (child != null) {
        if (child.setAttributeNS) {
            DvtSvgUtils.setAttrNullNS(child, "pointer-events", "none");
            DvtSvgUtils.setAttrNullNS(child, "id", null);
        }
        this._disableEvents(child);
        child = child.nextSibling;
    }
}

/**
 * Dispatch event
 * @param {String} eventType
 * @param {String} params An optional parameter to specify other parameters for the event
 */
DvtSvgDisplayable.prototype.dispatchDisplayableEvent = function(eventType, params) 
{
    var createEventType = DvtSvgDisplayable._CREATE_EVENT_TYPE_MAP[eventType];
    var context;
    if (DvtMouseEvent.CLICK == createEventType ||
        DvtMouseEvent.MOUSEOVER == createEventType ||
        DvtMouseEvent.MOUSEOUT == createEventType ||
        DvtMouseEvent.MOUSEMOVE == createEventType) {
        if (document.createEvent) {
            context = this.getObj().getCtx();
            var dim = this.getObj().getDimensions();
            var elementPosition = this.getObj().localToStage(new DvtPoint(dim.x, dim.y));
            var absoluteStagePos = context.getStageAbsolutePosition();
            var absoluteElementPosition = new DvtPoint(absoluteStagePos.x + elementPosition.x, absoluteStagePos.y + elementPosition.y);
            var event = document.createEvent("MouseEvents");
            event.initMouseEvent(createEventType, true, true, window, 1, absoluteElementPosition.x, absoluteElementPosition.y, absoluteElementPosition.x, absoluteElementPosition.y, false, false, false, false, 0, null);
            this.getElem().dispatchEvent(event);
        }
    }
    else if (DvtKeyboardEvent.KEYDOWN == createEventType ||
             DvtKeyboardEvent.KEYUP == createEventType) {
      if (document.createEvent) {
        var keyboardEvent = document.createEvent("KeyboardEvent");
        var keycode = parseInt(params);
        
        if(this._ctrlKeyPressed === undefined)
          this._ctrlKeyPressed = false;

        if(this._shiftKeyPressed === undefined)
          this._shiftKeyPressed = false;
        
        if(keycode == DvtKeyboardEvent.CONTROL) {
          if(this._ctrlKeyPressed && createEventType == DvtKeyboardEvent.KEYUP)
            this._ctrlKeyPressed = false;
          else if(!this._ctrlKeyPressed && createEventType == DvtKeyboardEvent.KEYDOWN)
            this._ctrlKeyPressed = true;
        } 
        else if(keycode == DvtKeyboardEvent.SHIFT) {
          if(this._shiftKeyPressed && createEventType == DvtKeyboardEvent.KEYUP)
            this._shiftKeyPressed = false;
          else if(!this._shiftKeyPressed && createEventType == DvtKeyboardEvent.KEYDOWN)
            this._shiftKeyPressed = true;
        }
          
        
        if (typeof(keyboardEvent.initKeyboardEvent) != 'undefined') {
          keyboardEvent.initKeyboardEvent(createEventType, true, true, window, this._ctrlKeyPressed, false, this._shiftKeyPressed, false, keycode, keycode);
        } else {
          keyboardEvent.initKeyEvent(createEventType, true, true, window, this._ctrlKeyPressed, false, false, this._shiftKeyPressed, keycode, keycode);
        }        
      
        /*      
        ideally, we would just create the native keyboard event and disptach it to the wrapping div, but we
        can't for two reasons
        1. when the keyboardEvent is initialized above, the initKeyboardEvent method used by Webkit doesn't initialize
           the event with the right keycode; it is always 0.  This is a known bug tracked in Chromium and at Webkit
           see http://code.google.com/p/chromium/issues/detail?id=27048 (Chromium) and
           https://bugs.webkit.org/show_bug.cgi?id=16735 (Webkit)
        2. since the native event doesn't work, I tried dispatching a DvtSvgKeyboardEvent to the wrapping div.
           however, this generates a DOM EventException of type UNSPECIFIED_EVENT_TYPE_ERR, even though the
           type of the DvtSvgKeyboardEvent was specified.
        
        So, instead, we will directly call the bubble listener that we attach to the wrapping div 
        */
        
        var svgKeyboardEvent = DvtSvgEventFactory.newEvent(keyboardEvent);
        svgKeyboardEvent.keyCode = keycode;
        svgKeyboardEvent.ctrlKey = this._ctrlKeyPressed;
        svgKeyboardEvent.shiftKey = this._shiftKeyPressed;
      
        if(this.getObj()) {
          context = this.getObj().getCtx();
          var stage = context.getStage();
          var wrappingDiv = stage.getImpl().getSVGRoot().parentNode;
          DvtHtmlKeyboardListenerUtils._bubbleListener.call(wrappingDiv, svgKeyboardEvent);
        }
      }
    }
}

/**
 * Sets/adds desc element to the SVG node.
 * @param {String} descTxt The description value.
 */
DvtSvgDisplayable.prototype.setDesc = function(descTxt) {
  if (!descTxt || this._desc == descTxt)
    return;
    
  if (!this._desc) // new element
    this._createDesc(descTxt);    
  else if (this._desc != descTxt) { // replace existing
    var isFound = false;    
    // loop over all children
    var child = this.getElem().firstChild;    
    while (child != null) {    
      if (child.tagName == 'desc') {
        var textNode = child.firstChild;        
        if (textNode)
          textNode.nodeValue = descTxt;
        
        this._desc = descTxt;
        isFound = true;  
        break;
      }    
      child = child.nextSibling;
    }
    if (!isFound)
      this._createDesc(descTxt);
  }
}

/**
 * Adds desc element to the SVG node.
 * @param {String} descTxt The description value.
 */
DvtSvgDisplayable.prototype._createDesc = function(descTxt) {
  if (!descTxt)
    return;

  var descElem  = DvtSvgShapeUtils.createElement("desc", null);
  var textNode = document.createTextNode(descTxt);
  DvtSvgUtils.appendChildElem(descElem, textNode);
  DvtSvgUtils.appendChildElem(this.getElem(), descElem);
  this._desc = descTxt;
}

/**
 * Dispatches the native event to the internal dom element
 * Note: Use with caution
 * @param {Object} event
 * @override
 */
DvtSvgDisplayable.prototype.dispatchNativeEvent = function(event) 
{
}
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*---------------------------------------------------------------------*/
/*  DvtSvgContainer         SVG implementation of DvtContainer         */
/*---------------------------------------------------------------------*/
/**  SVG implementation of DvtContainer.
  *  @extends DvtSvgDisplayable
  *  @class DvtSvgContainer
  *  @constructor  
  *  @param {String} id   An optional id for this object.
  *  @param {String} type  The SVG DOM element type.
  */ 
var   DvtSvgContainer = function(id, type)
{
  this._Init(id, type) ; 
};

DvtObj.createSubclass(DvtSvgContainer, DvtSvgDisplayable, "DvtSvgContainer") ;

/**
  * @protected
  * Array of SVG attributes that should be transferred from the shape
  * tag to the group when this shape becomes a group (when children
  * are added to it).
  */
DvtSvgContainer.AttributesTransferableToGroup = ['transform', 'opacity', 'style', 'visibility', 'pointer-events', 'clip-path'];


/*---------------------------------------------------------------------*/
/*   addChild()           Add a child to this container                */
/*---------------------------------------------------------------------*/
/**
  *   Adds the specifed object as a child of this container.  The object is
  *   added to the end of the child list.
  *   @param {DvtSvgDisplayable} obj The object to be added.
  */
DvtSvgContainer.prototype.addChild = function(obj)
{

  if (this._bShapeContainer) {
    //  This is a shape "container".
    //  If no previous children, will create a group DOM element for the this
    //  (parent) object and add the new child to the child group DOM element.
    //  else will add the object directly to the shape group container element.

    this.CreateChildGroupElem(false);
  }

  //  Bug 15865017 - GET RID OF DVTGOLINK AND IMPLS
  //  if there is an anchor elem <a> for this container
  //  append child to the <a> element 
  //  otherwise append child directly to this container group element

  var containerElem = this.getContainerElem();
  DvtSvgUtils.appendChildElem(containerElem, obj.getOuterElem());

} ;

/**
  *   Adds the specified object as a child of this container at the specified index. Returns
  *   immediately if the index position doesn't already exist in the child list.  If a currently
  *   occupied index is specified, the current child at that position and all subsequent children
  *   are moved one position higher in the list.
  *   @param {DvtSvgDisplayable} obj The object to be added.
  *   @param {number} index The index at which to add this object
  */
DvtSvgContainer.prototype.addChildAt = function(obj, index)
{
  var idx = index;

  if (this._bShapeContainer) {
    //  This is a shape "container".
    //  If no previous children, will create a group DOM element for the this
    //  (parent) object and add the new child to the child group DOM element.
    //  else will add the object directly to the shape group container element.
    this.CreateChildGroupElem(false);

    //need to increment the index to account for the shape dom element,
    //which should be the first child of the childGroupElem
    idx++;
  }

  //  Bug 15865017 - GET RID OF DVTGOLINK AND IMPLS
  //  if there is an anchor elem <a> for this container
  //  append child to the <a> element 
  //  otherwise append child directly to this container group element

  var containerElem = this.getContainerElem();
  var existingNode = containerElem.childNodes[idx];
  // BUG #12661320: IE9 cannot handle a value of undefined in insertBefore.  Set to null in such a case. 
  if (!existingNode)
    existingNode = null;
  containerElem.insertBefore(obj.getOuterElem(), existingNode);
}

/**
 * Returns an rray of SVG attributes that should be transferred from the shape
 * tag to the group when this shape becomes a group (when children are added to it).
 * @return {array}
 */
DvtSvgContainer.prototype.GetAttributesTransferableToGroup = function() {
  return DvtSvgContainer.AttributesTransferableToGroup;
}

/*---------------------------------------------------------------------*/
/*   addChildAfter()                                                   */
/*---------------------------------------------------------------------*/
/**
  *   Adds the specifed object as a child of this container after a specific
  *   object in the child list.
  *   @param {Object} obj  The object to be added.
  *   @param {Object} objAfter  The object after which the new child is to be added.
  */
DvtSvgContainer.prototype.addChildAfter = function(obj, objAfter)
{
     // To Do  JRM
} ;


/*---------------------------------------------------------------------*/
/*  addChildBefore()                                                   */
/*---------------------------------------------------------------------*/
/**
  *   Adds the specifed object as a child of this container before a specific
  *   object in the child list.
  *   @param {Object} obj  The object to be added.
  *   @param {Object} objBefore  The object before which the new child is
  *   to be added.
  */
DvtSvgContainer.prototype.addChildBefore = function(obj, objBefore)
{
     // To Do JRM
} ;




/**
  * @override
  */
DvtSvgContainer.prototype.getElem = function()
{
  return (this._childGroupElem? this._childGroupElem : this._elem) ;
} ;

/*---------------------------------------------------------------------*/
/*   _Init()                                                           */
/*---------------------------------------------------------------------*/
/**
  *  Object initializer.
  *  @protected
  */
DvtSvgContainer.prototype._Init = function(id, type)
{
  // For the SVG implementation, we need to know whether this is a standard
  // non-shape container, or a shape "container".  Since elementary objects
  // may not be nested, e.g. a rect containing another rect, an artificial 
  // group must be constructed to group the parent and the children.  That
  // is they actually become grouped siblings.


  this._bShapeContainer = (this instanceof DvtSvgShape) ;  // if shape "container"

  DvtSvgContainer.superclass._Init.call(this, id, type) ;

  this._childGroupElem  = null ;       // child surround group
} ;


/*---------------------------------------------------------------------*/
/*    removeChild()                                                    */
/*---------------------------------------------------------------------*/
/**
  *   Removes the specified child object from this container.
  *   @param {DvtSvgDisplayable} obj The object to be removed.
  */
DvtSvgContainer.prototype.removeChild = function(obj)
{
   var elemObj  = obj.getOuterElem() ;  // obj's outer DOM element
   var parent   = obj.getParent() ;     // get parent object (the container
                                        // or the shape group container)
   if (elemObj && parent) {
     // Bug 15865017 - GET RID OF DVTGOLINK AND IMPLS
     var elem = this.getContainerElem();
     elem.removeChild(elemObj) ;
   }

   //  If no more children, can remove the added group for child containership,
   //  and reparent the containing shape directly back to its parent in the
   //  position that the added child group container occupied.

   // Bug 15865017 - GET RID OF DVTGOLINK AND IMPLS
   // don't remove the childGroupElem because we need it to hold the anchor
   if (! this._anchorElem && this._bShapeContainer && this.getObj().getNumChildren() === 1) {
     var  childGroupElem       = this._childGroupElem ;
     var  childGroupElemParent = (childGroupElem? childGroupElem.parentNode : null);
     
     if (childGroupElemParent) {
      DvtSvgDisplayable.TransferAttributes(childGroupElem, this._elem, this.GetAttributesTransferableToGroup());     
       childGroupElemParent.replaceChild(this._elem, childGroupElem) ;
       this._childGroupElem = null ;
     }
   }
} ;



/*---------------------------------------------------------------------*/
/*    swap()                                                           */
/*---------------------------------------------------------------------*/

//  In progress  TDO  JRM

/**
  *    Exchanges the positions of two objects in this container..
  */ 
DvtSvgContainer.prototype.swap = function(obj1, obj2)
{
   //  Update the DOM

   var elem1     = obj1.getElem() ;      // obj's DOM element
   var elem2     = objw.getElem() ;
   var parent    = obj1.getParent() ;    // get parent object (the container
                                         // or the shape group container)
   var elem2Next = elem2.nextSibling;

   if (parent && elem1 && elem2) {
     parent.replaceChild(elem1, elem2) ;
     parent.insertBefore(elem1, elem2Next) ;

     // Update the array

     var idx1 = obj1.getIndex() ;
     var idx2 = obj2.getIndex() ;

     this._arList[idx1] = obj2 ;
     this._arList[idx2] = obj1 ;
   }
};

/**
 * Create a group element for adding children.
 * @param {boolean} rmChildren True if all children should be removed
 * @param {boolean} forced True always create a child group element for shape container
 * @protected
 */
DvtSvgContainer.prototype.CreateChildGroupElem = function(rmChildren, forced)
{
  //  This is a shape "container".
  //  If no previous children, will create a group DOM element for the this
  //  (parent) object and add the new child to the child group DOM element.
  //  else will add the object directly to the shape group container element.

  if (! this._childGroupElem && (forced || this.getObj().getNumChildren() === 0)) {
    var childGroupId = this._id ? this._id + '_g' : null;
    this._childGroupElem  = DvtSvgShapeUtils.createElement('g', childGroupId) ;
   
    //  Remove this's DOM element from the parent, and append to child group DOM element.
    var  parent = this.getParent() ;
    var  elemParent ;
    if (parent) {
      elemParent = parent.getImpl().getElem() ;
      elemParent.replaceChild(this._childGroupElem, this._elem) ; 
    }
    if (! rmChildren)
      DvtSvgUtils.appendChildElem(this._childGroupElem, this._elem) ;   // add shape to new group
        
    //transfer attributes from the old outermost SVG element to the
    //new outer group element
    DvtSvgDisplayable.TransferAttributes(this._elem, this._childGroupElem, this.GetAttributesTransferableToGroup());


    //Note need to copy _obj reference to the new group element so that events can be propagated.
    if (this._elem._obj) {
      this._childGroupElem._obj = this._elem._obj;
    }
  }
  else if (rmChildren) {
    this.getObj().removeChildren();
  }

};


//BUG FIX #13641836: setting display='none' causes getDimensions() to 
//throw error in Firefox just like if the element is not on the DOM,
//which in turn breaks several animations
//(leaving this code here, but commented out, as a reference, because
//it was added to fix bug 13511206 in AMX graph)
/**
  *  Enables/disables the visibility of this object.
  *  @param {boolean}  bVis  True if the object is to be visible, else false if
  *  it is to be hidden.
  */
/*DvtSvgContainer.prototype.setVisible = function(bVis)
{
  if (this._bVisible !== bVis) {
      if (this instanceof DvtSvgMarker)
        DvtSvgUtils.setAttrNullNS(this._elem, 'display', bVis? 'inline' : 'none') ;
      else
        DvtSvgUtils.setAttrNullNS(this.getElem(), 'display', bVis? 'inline' : 'none') ;
  }
  DvtSvgContainer.superclass.setVisible.call(this, bVis) ;
};*/


//BUG FIX 13842194:
/**
 * @override
 */
DvtSvgContainer.prototype.getDimensionsWithStroke = function() {
  //a pure container has no shape of its own, so calculate the
  //dims for all child shapes in the subtree rooted at this container
  
  //build an initial array of all direct children of this container
  var ar = [];
  var numChildren = this.getObj().getNumChildren();
  for (var i = 0; i < numChildren; i++) {
    ar.push(this.getObj().getChildAt(i));
  }
  
  //loop over the array of children until it's empty
  var totalDims = null;
  while (ar.length > 0) {
    //remove the first object from the array
    var child = ar.shift();
    //get dims in coordinate space of this container, because we're 
    //processing whole subtree of children under this container
    var dims = child.getImpl().GetDimensionsWithStroke(this.getObj());
    if (dims) {
      if (!totalDims) {
        totalDims = dims;
      }
      else {
        totalDims = totalDims.getUnion(dims);
      }
    }
    
    //add any children of current object to end of array
    numChildren = child.getNumChildren();
    for (var i = 0; i < numChildren; i++) {
      ar.push(child.getChildAt(i));
    }
  }
  return totalDims;
};

//BUG FIX 13842194:
DvtSvgContainer.prototype.GetDimensionsWithStroke = function(targetCoordinateSpace) {
  //pure container doesn't have its own dimensions
  return null;
};


/**
 * Adds a hyperlink to this object, remove the old hyperlink if exists.
 * This method adds a <g> and a <a> tags around this object. <g><a><this element></a></g>
 * @param {DvtObj} linkObj contains destination (url) and targetFrame
 */
DvtSvgContainer.prototype.setHyperlink = function(linkObj) 
{
  var destination;
  var frame;
  var focusable;

  if (linkObj) {
    destination = linkObj["destination"];
    frame = linkObj["targetFrame"];
    focusable = linkObj["focusable"];
  }

  var gElem = this.getElem();
  var aElem;
  var elem = this._elem;

  //if an "a" element already exists, 
  if (this._anchorElem) {
    aElem = this._anchorElem;

    // replace destination and targetFrame
    if (destination) {
      DvtSvgUtils.setAttrNS(aElem, DvtSvgImage.XLINK_NS, 'xlink:href', destination);
      if (frame) {
        DvtSvgUtils.setAttrNullNS(aElem, 'target', frame);
      }
      else {
        DvtSvgUtils.removeAttrNullNS(aElem,'target');
      }
      
      if (focusable)
        DvtSvgUtils.setAttrNullNS(aElem, 'focusable', focusable);
    }

    // no destination, remove "a" element
    else {
      if (gElem && aElem.parentNode == gElem) {
        gElem.removeChild(aElem);
        DvtSvgContainer._reparentChildren(gElem, aElem);
      }
      this.setCursor("default");
      this._anchorElem = null;
    }
  }

  // create an "a" element
  else {
    aElem = DvtSvgShapeUtils.createElement('a');
    if (destination)
      DvtSvgUtils.setAttrNS(aElem, DvtSvgImage.XLINK_NS, 'xlink:href', destination);

    if (frame)
      DvtSvgUtils.setAttrNullNS(aElem, 'target', frame);

    if (focusable)
        DvtSvgUtils.setAttrNullNS(aElem, 'focusable', focusable);

    // need to wrap around shape element (<a><text></a>) with a <g> to avoid
    // dom element replacement error.
    // Bug 15865017 - GET RID OF DVTGOLINK AND IMPLS
    if (this._bShapeContainer) {
      this.CreateChildGroupElem(false, true);
    }

    gElem = this.getElem();    
    DvtSvgContainer._reparentChildren(aElem, gElem);

    DvtSvgUtils.appendChildElem(gElem, aElem);

    this.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
    this._anchorElem = aElem;
  }
};

/*
 * move children from source to target element
 */
DvtSvgContainer._reparentChildren = function(target, source) {
  var childNodes = source.childNodes;

  if (childNodes) {
    var node;
    var lastNode;

    for (var i = childNodes.length - 1; i >= 0; i--) {
      node = childNodes[i];
      if (lastNode) {
        target.insertBefore(node, lastNode);
      }
      else {
        DvtSvgUtils.appendChildElem(target, node);
      }
      lastNode = node;
    }
  }
}

/**
 * Returns the anchor element if exists.
 */
DvtSvgContainer.prototype.getContainerElem = function() {
  return this._anchorElem ? this._anchorElem : this.getElem();
}


/**
 * Dispatches the native event to the internal dom element
 * Note: Use with caution
 * @param {Object} event
 */
DvtSvgContainer.prototype.dispatchNativeEvent = function(event) 
{
  var nativeEvent = event.getNativeEvent();
  var target = null;
  
  if (this._bShapeContainer)
    target = this._childGroupElem;
  else if (this._anchorElem)
    target = this._anchorElem;
  else
    target = this._elem;
    
  if (target.dispatchEvent)
    target.dispatchEvent(nativeEvent);
}
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*---------------------------------------------------------------------*/
/* DvtSvgStage()   Contains all objects associated with a specific SVG */
/*                 Root.                                               */
/*---------------------------------------------------------------------*/
/**
 * @constructor
  *  SVG implementation of DvtStage, representing the highest level container
  *  under the &lt;svg&gt; DOM element.
  *  @extends DvtSvgContainer
  *  @class DvtSvgStage
  */
var   DvtSvgStage = function(svgRoot, id)
{
  this._Init(svgRoot, id) ; 
};

DvtObj.createSubclass(DvtSvgStage, DvtSvgContainer, "DvtSvgStage") ;


/*---------------------------------------------------------------------*/
/*  appendDefs()   Append element(s) to the application global <defs>  */
/*                 element.                                            */
/*---------------------------------------------------------------------*/

DvtSvgStage.prototype.appendDefs = function(elem)
{
  DvtSvgUtils.appendChildElem(this._elemDefs, elem) ;
} ;


/*---------------------------------------------------------------------*/
/*   getDefs()        get the application global <defs> DOM element    */
/*---------------------------------------------------------------------*/

DvtSvgStage.prototype.getDefs = function()
{
   return this._elemDefs ;
} ;



/*---------------------------------------------------------------------*/
/*   getSVGRoot()          Returns the SVG root DOM element            */
/*---------------------------------------------------------------------*/

DvtSvgStage.prototype.getSVGRoot = function()
{
   return this._SVGRoot ;
} ;



/*---------------------------------------------------------------------*/
/*   _Init()                                                           */
/*---------------------------------------------------------------------*/
/** @private */
DvtSvgStage.prototype._Init = function(svgRoot, id)
{
  DvtSvgStage.superclass._Init.call(this, id, 'g') ;

  this._SVGRoot   = svgRoot ;   // containing SVG DOM element
  this._bAppended = true ;

  this._obj = null ;            // the controlling Dvt... object
  
  //to disable ability to select text, bug 13453406
  this.disableSelection(this._elem);
  
  //set cursor to default to avoid text cursors on text objects
  this.setCursor("default");
} ;

DvtSvgStage.prototype.disableSelection = function(target){
    target.onselectstart=function(){return false};
    target.onselect=function(){return false};
};
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.

/*---------------------------------------------------------------------*/
/*   DvtSvgShapeUtils          Static Shape Utility Functions          */
/*---------------------------------------------------------------------*/

/**
 *   Static Shape Utility Functions
 *   @class DvtSvgShapeUtils
 *   @constructor
 */
var  DvtSvgShapeUtils = function()
{} ;

DvtObj.createSubclass(DvtSvgShapeUtils, DvtObj, "DvtSvgShapeUtils");

DvtSvgShapeUtils._uniqueSeed = 0 ;  // for unique Id creation
DvtSvgShapeUtils._Stage      = null ;

/**
 *  Adds a clipping region to the global defs element.
 *  @param {DvtGraphClipPath}  cp  the DvtGraphClipPath object specifying the clipping region(s).
 *  @returns {boolean}    true if the clipping paths was successfully or already added, else false.
 */
DvtSvgShapeUtils.addClipPath = function(cp, context) {
  var id = cp.getId() ;
  if (! id) {
    return  false;    // essential to have an id to reference
  }

  var elemDefs = context.getDefs() ; 
  if (! elemDefs) {
    return false;
  }

  // Check if clipping path for this id is already defined.
  var defsChildren = elemDefs.childNodes ;
  var len          = defsChildren.length ;
  for (var i = 0; i < len; i++) {
    var el = defsChildren[i] ;
    if (el.id === id)
      return true;
  }
  
  var elemClip  = DvtSvgShapeUtils.createElement('clipPath', id) ;
  context.appendDefs(elemClip) ;
  
  var regions = cp.getRegions() ;
  for (var i = 0; i < regions.length; i++) {
     var region = regions[i] ;
     if (region) {
       var  elem = null ;
       if (region.type === DvtGraphClipPath.RECT) {
          elem = DvtSvgShapeUtils.createElement('rect') ;
          DvtSvgUtils.setAttrNullNS(elem, 'x',      region.x) ;
          DvtSvgUtils.setAttrNullNS(elem, 'y',      region.y) ;
          DvtSvgUtils.setAttrNullNS(elem, 'width',  region.w) ;
          DvtSvgUtils.setAttrNullNS(elem, 'height', region.h) ;
          if (region.rx)
            DvtSvgUtils.setAttrNullNS(elem, 'rx', region.rx) ;
          if (region.ry)
            DvtSvgUtils.setAttrNullNS(elem, 'ry', region.ry) ;
       }
       else if (region.type === DvtGraphClipPath.PATH) {
          elem = DvtSvgShapeUtils.createElement('path') ;
          DvtSvgUtils.setAttrNullNS(elem, 'd', region.d) ;
       }
       else if (region.type === DvtGraphClipPath.POLYGON) {
          elem = DvtSvgShapeUtils.createElement('polygon') ;
          DvtSvgUtils.setAttrNullNS(elem, 'points', region.points) ;
       }
       else if (region.type === DvtGraphClipPath.ELLIPSE) {
          elem = DvtSvgShapeUtils.createElement('ellipse') ;
          DvtSvgUtils.setAttrNullNS(elem, 'cx', region.cx) ;
          DvtSvgUtils.setAttrNullNS(elem, 'cy', region.cy) ;
          DvtSvgUtils.setAttrNullNS(elem, 'rx', region.rx) ;
          DvtSvgUtils.setAttrNullNS(elem, 'ry', region.ry) ;
       }
       else if (region.type === DvtGraphClipPath.CIRCLE) {
          elem = DvtSvgShapeUtils.createElement('circle') ;
          DvtSvgUtils.setAttrNullNS(elem, 'cx', region.cx) ;
          DvtSvgUtils.setAttrNullNS(elem, 'cy', region.cy) ;
          DvtSvgUtils.setAttrNullNS(elem, 'r', region.r) ;
       }

       if (elem)
         DvtSvgUtils.appendChildElem(elemClip, elem) ;
     }
  }

  return true ;
};


/*---------------------------------------------------------------------*/
/*   createElement()        Create an SVG DOM element                  */
/*---------------------------------------------------------------------*/
/**
  *  Creates an SVG DOM element.  
  * @param {String} name 
  * @param {String} id  Optional ID to be applied to the created DOM element.
  * @type DOM_Element
  * @base DvtSvgShapeUtils
  */
DvtSvgShapeUtils.createElement = function(name, id)
{
   var elem = document.createElementNS(DvtSvgUtils.SVG_NS, name) ;

   if (id) {
     DvtSvgUtils.setAttrNullNS(elem, 'id', id) ;
   }
     
   return elem ;
} ;



/*---------------------------------------------------------------------*/
/*   getUniqueId()              Returns a unique ID string             */
/*---------------------------------------------------------------------*/
/**
 *  Creates a unique ID string
 * @param {DvtContext} context The rendering context
 * @param {String} sPrefix  Optional string used as a prefix for the generated ID. If 
 *                          omitted, the ID generated will be prefixed with '$'.
 * @returns {String}        A unique ID string
 */
DvtSvgShapeUtils.getUniqueId = function(context, sPrefix)
{
  var uniqueId = (sPrefix? sPrefix : '$') + DvtSvgShapeUtils._uniqueSeed++ ;
  if (DvtAgent.isEnvironmentTest())
    return (context ? context.getStage().getId() + '_' : '') + uniqueId;
  else
    return uniqueId;
} ;

/**
 * Converts an array of x,y coordinate pairs into an SVG style string.
 * @param {array} arPoints the array of points
 * @returns {Array}  the string usable by SVG polygons and polylines
 */
DvtSvgShapeUtils.convertPointsArray = function(arPoints) {
  var len = arPoints.length ;   // convert to svg space separated list
  var s = '' ;

  for (var i = 0; i < len; i++) {
    if (i > 0) {
      s += ' ' ;
    }
    s += arPoints[i] ;
  }
  return s;
}





// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*---------------------------------------------------------------------*/
/*   DvtSvgShape            Abstract base class for all shapes         */
/*---------------------------------------------------------------------*/
/**
 * A base class for shapes implemented via SVG.  Implements common DvtShape
 * drawing support for properties such as such as stroke and fill types and colors.
 * DvtSvgShape is intended to be subclassed.
 * @extends DvtSvgContainer
 * @class DvtSvgShape
 * @constructor
 * @param {string} id   An id for the object.
 * @param {string} type The SVG DOM element type - e.g. 'circle', 'rect', etc.
 */
var DvtSvgShape = function (id, type) {
  this._Init(id, type);
};

//  Allow shapes to become 'containers' themselves.  Note: this does not
//  provide a true parent/child relationship since this not available in SVG.
DvtObj.createSubclass(DvtSvgShape, DvtSvgContainer, "DvtSvgShape");




/*---------------------------------------------------------------------*/
/*  _Init()                                                            */
/*---------------------------------------------------------------------*/
/** @private */
DvtSvgShape.prototype._Init = function (id, type) {
  DvtSvgShape.superclass._Init.call(this, id, type);

  //  Base shape properties
  this._stroke = null;
  this._fill = null;
  this._origFill = null;
  this._origStroke = null;
  this._bHollow = false;

  // Since setFill() may never be called, will set a default no 'fill'
  DvtSvgUtils.setAttrNullNS(this._elem, 'fill', 'none');
};

/*---------------------------------------------------------------------*/
/*   get/setFill()                                                     */
/*---------------------------------------------------------------------*/
/**
 *  Returns the fill object (derivative of {@link DvtFill}) applied to this shape.
 * @type DvtFill
 */
DvtSvgShape.prototype.getFill = function () {
  return this._fill;
};

/**
 *  Sets the fill properties on this shape from the supplied fill object (a
 *  derivative of {@link DvtFill}).
 *  @param {DvtFill}  obj  A fill object. If null is specified, a transparent
 *  fill results.
 */
DvtSvgShape.prototype.setFill = function (obj) {
  
  this._manageDefinitions(this._fill, obj);

  if (!obj) {
    DvtSvgUtils.setAttrNullNS(this._elem, 'fill', 'none');
    DvtSvgUtils.removeAttrNullNS(this._elem, 'fill-opacity');
    this._fill = null;
    return;
  }
  else {
    if (this._fill === obj) {
      return;
    }
  }
  this._fill = obj;

  if (obj instanceof DvtGradientFill || obj instanceof DvtImageFill || obj instanceof DvtPatternFill)
    this._setSpecFill(obj);
  else { //  Basic fill
    var color = obj.getColor();
    if (color) {
      var alpha = obj.getAlpha();
      // Workaround for Safari where versions < 5.1 draw rgba values as black
      if (DvtAgent.isBrowserSafari() && color.indexOf("rgba") !==  - 1) {
        DvtSvgUtils.setAttrNullNS(this._elem, 'fill', DvtColorUtils.getRGB(color));
        // Use alpa in rgba value as a multiplier to the alpha set on the object as this is what svg does.
        if (alpha != null)
          DvtSvgUtils.setAttrNullNS(this._elem, 'fill-opacity', DvtColorUtils.getAlpha(color) * alpha);
        else 
          DvtSvgUtils.setAttrNullNS(this._elem, 'fill-opacity', DvtColorUtils.getAlpha(color));
      }
      else {
        DvtSvgUtils.setAttrNullNS(this._elem, 'fill', color);
        if (alpha != null)
          DvtSvgUtils.setAttrNullNS(this._elem, 'fill-opacity', alpha);
      }
    }
  }
};

DvtSvgShape.prototype._setSpecFill = function (obj, func) {
  var id;
  if (!obj.isLocked()) {
    id = obj.getId();
    if (!id) {
      if (obj instanceof DvtPatternFill) {
        id = DvtSvgShapeUtils.getUniqueId(this.getObj().getCtx(), "pat");// no id - create unique internal id
      }
      else {
        id = DvtSvgShapeUtils.getUniqueId(this.getObj().getCtx(), 'Gr');// no id - create unique internal id
      }
      obj.setId(id);
    }
  }
  else {
    id = obj.getId();
  }
  if (!obj._defPresent) {
    var elem = obj._defElem;
    if (!elem) {
      if (obj instanceof DvtGradientFill) 
        elem = DvtSvgGradientUtils.createElem(obj, id);
      else if (obj instanceof DvtImageFill) 
        elem = DvtSvgImageFillUtils.createElem(obj, id);
      else if (obj instanceof DvtPatternFill)
        elem = DvtSvgPatternFillUtils.createElem(obj, id);
    }
    obj._defElem = elem;
    this.getObj().getCtx().appendDefs(elem);
    obj._defPresent = true;
  }
  
  DvtSvgUtils.setAttrNullNS(this._elem, 'fill', 'url(#' + id + ')');
  DvtSvgUtils.setAttrNullNS(this._elem, 'fill-opacity', '1');
};

/*---------------------------------------------------------------------*/
/*   get/setStroke()                                                   */
/*---------------------------------------------------------------------*/
/**
 *  Returns the stroke object (derivative of {@link DvtStroke}) applied to this shape.
 * @type DvtStroke
 */
DvtSvgShape.prototype.getStroke = function () {
  return this._stroke;
};

/**
 *  Sets the stroke properties on this shape from the supplied stroke object.
 *  If the stroke object is null, any existing stroke is removed.
 *  @param {DvtStroke}  obj  A stroke object.
 */
DvtSvgShape.prototype.setStroke = function (obj) {

  this._manageDefinitions(this._stroke, obj);
  
  this._stroke = obj;

  if (!obj) {
    DvtSvgUtils.removeAttrNullNS(this._elem, 'stroke');
    DvtSvgUtils.removeAttrNullNS(this._elem, 'stroke-opacity');
    DvtSvgUtils.removeAttrNullNS(this._elem, 'stroke-width');
    DvtSvgUtils.removeAttrNullNS(this._elem, 'stroke-dasharray');
    DvtSvgUtils.removeAttrNullNS(this._elem, 'stroke-dashoffset');
    DvtSvgUtils.removeAttrNullNS(this._elem, 'stroke-linejoin');
    DvtSvgUtils.removeAttrNullNS(this._elem, 'stroke-linecap');
    DvtSvgUtils.removeAttrNullNS(this._elem, 'stroke-miterlimit');
    return;
  }

  //  Stroke color/alpha
  // gradient
  if (obj instanceof DvtGradientStroke) {
    this._setSpecStroke(obj);
  }
  else {
    var color = obj.getColor();
    if (color) {
      var alpha = obj.getAlpha();
      // Workaround for Safari where versions < 5.1 draw rgba values as black
      if (DvtAgent.isBrowserSafari() && color.indexOf("rgba") !==  - 1) {
        DvtSvgUtils.setAttrNullNS(this._elem, 'stroke', DvtColorUtils.getRGB(color));
        // Use alpa in rgba value as a multiplier to the alpha set on the object as this is what svg does.
        if (alpha != null)
          DvtSvgUtils.setAttrNullNS(this._elem, 'stroke-opacity', DvtColorUtils.getAlpha(color) * alpha);
        else 
          DvtSvgUtils.setAttrNullNS(this._elem, 'stroke-opacity', DvtColorUtils.getAlpha(color));
      }
      else {
        DvtSvgUtils.setAttrNullNS(this._elem, 'stroke', color);
        if (alpha != null)
          DvtSvgUtils.setAttrNullNS(this._elem, 'stroke-opacity', alpha);
      }
    }
  }

  //  Stroke width
  DvtSvgUtils.setAttrNullNS(this._elem, 'stroke-width', obj.getWidth());
  
  if (obj.isFixedWidth())
    DvtSvgUtils.setAttrNullNS(this._elem, 'vector-effect', 'non-scaling-stroke');

  //  Stroke type/style
  var st = obj.getType();
  if (st !== DvtStroke.SOLID) {
    DvtSvgUtils.setAttrNullNS(this._elem, 'stroke-dasharray', obj.getDash());
    if (obj.getDashOffset()) {
      DvtSvgUtils.setAttrNullNS(this._elem, 'stroke-dashoffset', obj.getDashOffset());      
    }
  }
  else {
    DvtSvgUtils.removeAttrNullNS(this._elem, 'stroke-dasharray');
    DvtSvgUtils.removeAttrNullNS(this._elem, 'stroke-dashoffset');
  }

  //  Line-joining attribute
  attrib = obj.getLineJoin();
  if (attrib) {
    DvtSvgUtils.setAttrNullNS(this._elem, 'stroke-linejoin', attrib);
  }

  //  Line-ending attribute
  attrib = obj.getLineEnd();
  if (attrib) {
    DvtSvgUtils.setAttrNullNS(this._elem, 'stroke-linecap', attrib);
  }

  //  miter limit attribute
  attrib = obj.getMiterLimit();
  if (attrib) {
    DvtSvgUtils.setAttrNullNS(this._elem, 'stroke-miterlimit', attrib);
  }
};

/**
 * Private helper function to create gradient, add it to def section of the SVG DOM,
 * and set the stroke to point to the gradient def
 * @private
 */
DvtSvgShape.prototype._setSpecStroke = function (obj, func) {
  var id;
  if (!obj.isLocked()) {
    id = obj.getId();
    if (!id) {
      id = DvtSvgShapeUtils.getUniqueId(this.getObj().getCtx(), 'Gr');// no id - create unique internal id
      obj.setId(id);
    }
  }
  else {
    id = obj.getId();
  }
  
  if (!obj._defPresent) {
      var elem = obj._defElem;
      if (!elem)
          elem = DvtSvgGradientUtils.createElem(obj, id);// create SVG DOM elem
      obj._defElem = elem;
      this.getObj().getCtx().appendDefs(elem);
      obj._defPresent = true;
  }


  DvtSvgUtils.setAttrNullNS(this._elem, 'stroke', 'url(#' + id + ')');
  var alpha = obj.getAlpha();
  if (alpha != null) {
    DvtSvgUtils.setAttrNullNS(this._elem, 'stroke-opacity', alpha);
  }
};

/**  Changes the shape to an outline shape format.  Used for legends
 *  markers that represent a hidden state for the associated series risers.
 *  @param {String} fc Border color for hollow shape in format of #aarrggbb
 *  @param {number} strokeWidth Stroke width used for shapes that were transofrmed (optional)
 */
DvtSvgShape.prototype.setHollow = function (fc, strokeWidth) {
  if (!this._bHollow) {
    // Save original fill and stroke to restore 
    this._origFill = this._fill;
    this._origStroke = this._stroke;
    // Set hollow shape fill and stroke based on shape fill
    var hollowFill = DvtSolidFill.invisibleFill();
    var hollowStroke;
    if (fc) {
      hollowStroke = new DvtSolidStroke(DvtColorUtils.getRGB(fc), DvtColorUtils.getAlpha(fc), strokeWidth);
    }
    else {
      hollowStroke = new DvtSolidStroke(this._fill.getColor(), this._fill.getAlpha(), strokeWidth);
    }
    this.setFill(hollowFill);
    this.setStroke(hollowStroke);
    this._bHollow = true;
  }
  else {
    this.setFill(this._origFill);
    this.setStroke(this._origStroke);
    this._bHollow = false;
  }
};

/**  Returns whether a shape is hollow.
 *  @returns {boolean} True if the shape is hollow
 */
DvtSvgShape.prototype.isHollow = function () {
  return this._bHollow;
}

/*
 * Manage the gradient/filter definitions for svg by keeping a reference count and removing unreferenced defs
 */
DvtSvgShape.prototype._manageDefinitions = function(oldObj, newObj) {
  // If a new obj is specified, adjust reference counts and remove def if 0 references
  if (oldObj != newObj) {
    // Decrease count on existing obj
    if (oldObj) {
        oldObj._referenceCount--;
        if (oldObj._referenceCount == 0) {
            if (oldObj._defElem) {
                this.getObj().getCtx().removeDefs(oldObj._defElem);
                oldObj._defPresent = false;
            }
        }

    }
    if (newObj) {
        if (!newObj._referenceCount)
            newObj._referenceCount = 0;
        newObj._referenceCount++;
    }
  }
}

//BUG FIX 13842194:
/**
 * @override
 */
DvtSvgShape.prototype.getDimensionsWithStroke = function() {
  //get the dims for this shape by itself
  var dims = this.GetElemDimensionsWithStroke();
  //get the dims for any children of this container
  var containerDims = DvtSvgShape.superclass.getDimensionsWithStroke.call(this);
  //create union of shape and container dims
  if (dims && containerDims) {
    dims = dims.getUnion(containerDims);
  }
  else if (containerDims) {
    dims = containerDims;
  }
  
  return dims;
};

//BUG FIX 13842194:
DvtSvgShape.prototype.GetDimensionsWithStroke = function(targetCoordinateSpace) {
  //get the dims for this shape by itself
  var dims = this.GetElemDimensionsWithStroke();
  if (!targetCoordinateSpace || targetCoordinateSpace === this.getObj())
    return dims;
  else { // Calculate the bounds relative to the target space
    return this.getObj().ConvertCoordSpaceRect(dims, targetCoordinateSpace);
  }
};

//BUG FIX 13842194:
DvtSvgShape.prototype.GetElemDimensionsWithStroke = function() {
  //get the dims for this shape by itself
  var dims = DvtSvgShape.superclass.GetElemDimensionsWithStroke.call(this);
  //assume that stroke touches every edge of bounding box, so push out
  //every edge by half the stroke width
  var stroke = this.getStroke();
  if (dims && stroke) {
    var sw = stroke.getWidth();
    if (sw) {
      var halfSw = .5 * sw;
      dims.x -= halfSw;
      dims.y -= halfSw;
      dims.w += sw;
      dims.h += sw;
    }
  }
  return dims;
};
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*---------------------------------------------------------------------*/
/*    DvtSvgArc                 Svg implementation of arc              */
/*---------------------------------------------------------------------*/
/**
  *  Creates an arc (a portion of an ellipse or circle) implemented by SVG.
  *  @extends DvtSvgShape
  *  @class  DvtSvgArc  Creates an arc (a portion of an ellipse or circle)
  *  implemented by SVG.  Do not create directly, use DvtArc.
  *  <p>
  *  Example:<br><br><code>
  *  //  Create an arc from an ellipse centered at (50,50) with radii 100 and<br>
  *  // 200, and starting at angle 40, for an extent of 75.<br><br>
  *  var  arc = new DvtArc(context,50,50,100,200,40, 75);<br><br>
  *  //  Create an arc from a circle centered at (50,50) with radii 100<br>
  *  //  starting at angle 40, for an extent of 75.<br><br>
  *  var  arc = new DvtArc(context,50,50,100,40,75);<br>
  *  </code>
  *  @constructor  
  *  @param {number} cx  The center x position.
  *  @param {number} cy  The center y position.
  *  @param {number} rx  The horizontal radius of the ellipse.
  *  @param {number} ry  The vertical radius of the ellipse. (use the same value as
  *                      the horizontal radius if a circular arc.)
  *  @param {number} sa  The starting angle in degrees (following the normal anti-clockwise is positive convention).
  *  @param {number} ae  The angle extent in degrees (following the normal anti-clockwise is positive convention).
  *  @param {String} clos  An optional closure type for the arc. Closure types are {@link DvtArc#OPEN} (the default),
  *                        {@link DvtArc#CHORD} and {@link DvtArc#PIE}.
  *   @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var  DvtSvgArc = function(cx, cy, rx, ry, sa, ae, clos, id)
{
   this._Init(cx, cy, rx, ry, sa, ae, clos, id) ;
};

DvtObj.createSubclass(DvtSvgArc, DvtSvgShape, "DvtSvgArc") ;



/*---------------------------------------------------------------------*/
/*  _AddClosure()                                                      */
/*---------------------------------------------------------------------*/
/** @private */
DvtSvgArc.prototype._addClosure = function(p)
{
  if (this._ct === DvtArc.CHORD) {
    p += ' z' ;
  }
  else if (this._ct === DvtArc.PIE) {
    p += ' L ' + this._cx + ',' + this._cy + ' z' ;
  }
  else if (this._ct === DvtArc.OPEN) {
    this.setFill(null) ;
  }

  return p ;
} ;



/*---------------------------------------------------------------------*/
/*  _createArc()                                                       */
/*---------------------------------------------------------------------*/
/**
  *   @private
  */
DvtSvgArc.prototype._createArc = function()
{
   if (this._bInInit) {       // don't need to do this until end of Init()
     return ;
   }

   var sa = (this._sa * DvtMath.RADS_PER_DEGREE);
   var ae = (this._ae * DvtMath.RADS_PER_DEGREE);

   var x1 = this._cx + (this._rx * Math.cos(sa)) ;        // get arc
   var y1 = this._cy - (this._ry * Math.sin(sa)) ;        // end points
   var x2 = this._cx + (this._rx * Math.cos(sa + ae)) ;
   var y2 = this._cy - (this._ry * Math.sin(sa + ae)) ;

   var nLargeArc  =  (Math.abs(this._ae) > 180)? '1' : '0' ;
   var nSweepFlag =  (this._ae > 0)? '0' : '1' ;     // 0 == svg +ve angle

   var path = "M " +  x1 + " " + y1 + " A " + this._rx + "," + this._ry + " " + "0" + " " +
              nLargeArc + "," + nSweepFlag + " " + x2 + "," + y2 ;   
   path = this._addClosure(path) ;

   DvtSvgUtils.setAttrNullNS(this._elem, 'd', path) ;
   if (this._stroke !== null) {
     this.setStroke(this._stroke) ;
   }
} ;






/*---------------------------------------------------------------------*/
/*   getAngleExtent()                                                  */
/*---------------------------------------------------------------------*/
/**
  *  Returns the angle subtended by the arc.
  *  @returns {number}  The angle subtended by the arc (following the normal anti-clockwise
  *  is positive convention).
  */
DvtSvgArc.prototype.getAngleExtent = function()
{
   return this._ae ;
} ;
/**
  *  Returns the angle subtended by the arc.
  *  @returns {number}  The angle subtended by the arc.
  *  is positive convention).
  */
DvtSvgArc.prototype.setAngleExtent = function(ae)
{
   ae = ((ae === null || isNaN(ae)) ? 0 : ae);
   if (this._ae !== ae) {

     //  From https://developer.mozilla.org/en/SVG/Tutorial/Paths
     //  "Complete circles and ellipses are actually the one object paths have trouble drawing.
     //  Because the start and end points for any path going around a circle are the same, there
     //  are an infinite number of circles that could be chosen, and the actual path is undefined.
     //  It's possible to approximate them by making the start and end points of your path slightly askew"

     if (ae === 360) {               // bug 12723079
       ae = 359.99 ;                 // cannnot use any precision higher than this!
     }

     this._ae = ae;
     this._createArc() ;
   }
} ;


/*---------------------------------------------------------------------*/
/*   getAngleStart()                                                   */
/*---------------------------------------------------------------------*/
/**
  *  Returns the start angle for an arc.
  *  @returns {number}  The starting angle (following the normal anti-clockwise
  *  is positive convention).
  */
DvtSvgArc.prototype.getAngleStart = function()
{
   return this._sa ;
} ;

/**
  *  Sets the start angle of the arc.
  *  @param {number}  The starting angle (following the normal anti-clockwise
  *  is positive convention).
  */
DvtSvgArc.prototype.setAngleStart = function(sa)
{
   sa = ((sa === null || isNaN(sa)) ? 0 : sa);
   if (this._sa !== sa) {
     this._sa = sa;
     this._createArc() ;
   }
} ;



/*---------------------------------------------------------------------*/
/*   get/setClosure()                                                  */
/*---------------------------------------------------------------------*/
/**
  *    Returns the closure type of the arc.
  *    @type String
  *    @returns The closure type,  such as {@link DvtArc#OPEN}
  */
DvtSvgArc.prototype.getClosure = function()
{
   return this._ct ;
} ;

/**
  *  Sets the closure type of the arc.
  *  @param {String} ct   The closure type,  such as {@link DvtArc#OPEN}
  */
DvtSvgArc.prototype.setClosure = function(ct)
{
   if (ct !== this._ct) {
     this._ct  = ct ;
     this._createArc() ;
   }

} ;



/*---------------------------------------------------------------------*/
/*  _Init()                                                            */
/*---------------------------------------------------------------------*/
/** @private */

DvtSvgArc.prototype._Init = function(cx, cy, rx, ry, sa, ae, clos, id)
{
   DvtSvgArc.superclass._Init.call(this, id, 'path') ;

   this._sa = 0 ;
   this._ae = 0 ;

   ry = ((ry === null || isNaN(ry)) ? rx : ry);

   this._bInInit = true ;         // performance - avoid multiple createArc()'s
   this.setCx(cx);
   this.setCy(cy);
   this.setRx(rx);
   this.setRy(ry);
   this.setAngleStart(sa);
   this.setAngleExtent(ae);
   this.setClosure(clos ? clos : DvtArc.OPEN);
   this._bInInit = false ;        // finally allow createArc to work.

   this._createArc() ;

} ;


/*---------------------------------------------------------------------*/
/*   setArc()         Convenience method                               */
/*---------------------------------------------------------------------*/
/**
  *  Defines the position and extent of the arc.
  *  @param {number} sa  The starting angle in degrees (following the normal
  *  anti-clockwise is positive convention).
  *  @param {number} ae  The angle extent in degrees (following the normal
  *  anti-clockwise is positive convention).
  */
DvtSvgArc.prototype.setArc = function(sa, ae)
{
   this.setAngleStart(sa) ;
   this.setAngleExtentStart(ae) ;
} ;


/*---------------------------------------------------------------------*/
/*   get/setCx()                                                       */
/*---------------------------------------------------------------------*/
/**
  *  Returns the x coordinate of the center.
  *  @type number
  */
DvtSvgArc.prototype.getCx = function()
{
   return  this._cx ;
} ;
/**
  *  Sets the x coordinate of the center.
  *  @param {number} cx  The center x position.
  */
DvtSvgArc.prototype.setCx = function(cx)
{
   if (cx !== this._cx) {
     this._cx  = cx ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'cx', cx) ;
   }
} ;


/*---------------------------------------------------------------------*/
/*   get/setCy()                                                       */
/*---------------------------------------------------------------------*/
/**
  *  Returns the y coordinate of the center.
  *  @type number
  */
DvtSvgArc.prototype.getCy = function()
{
   return  this._cy ;
} ;

/**
  *  Sets the y coordinate of the center.
  *  @param {number} cy  The center y position.
  *
  */
DvtSvgArc.prototype.setCy = function(cy)
{
   if (cy !== this._cy) {
     this._cy  = cy ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'cy', cy) ;
   }
} ;


/*---------------------------------------------------------------------*/
/*   get/setRx()                                                       */
/*---------------------------------------------------------------------*/
/**
  *  Returns the horizontal radius of the ellipse.
  *  @type number
  */
DvtSvgArc.prototype.getRx = function()
{
   return this._rx ;
} ;

/**
  *  Sets the horizontal radii of the ellipse.
  *  @param {number} rx  The horizontal radius of the ellipse.
  */
DvtSvgArc.prototype.setRx = function(rx)
{
   if (rx !== this._rx) {
     this._rx  = rx ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'rx', this._rx) ;
   }
} ;


/*---------------------------------------------------------------------*/
/*   get/setRy()                                                       */
/*---------------------------------------------------------------------*/
/**
  *  Returns the vertical radius of the ellipse.
  *  @type number
  */
DvtSvgArc.prototype.getRy = function()
{
   return this._ry ;
} ;

/**
  *  Sets the vertical radius of the ellipse.
  *  @param {number} ry  The vertical radius of the ellipse.
  */
DvtSvgArc.prototype.setRy = function(ry)
{
   if (ry !== this._ry) {
     this._ry  = ry ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'ry', this._ry) ;
   }
} ;


// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*-------------------------------------------------------------------------*/
/*   DvtSvgCircle         SVG Implementation of base circular shape        */
/*-------------------------------------------------------------------------*/
/**
  *  Creates a circular displayable shape - SVG implementation of DvtCircle.
  *  @extends DvtSvgShape
  *  @class DvtSvgCircle is an SVG implementation of DvtCircle and displays
  *  a circle.  Note:  this class should not be used directly, use DvtCircle.
  *  <p>
  *  <b>Example:</b><br><br> <code>
  *  var circle = new DvtCircle(context,10, 10, 100, 'myCircle') ;<br>
  *  circle.setStroke(new DvtStroke("red", 5)) ;<br>
  *</code>
  *
  *   @constructor  
  *   @param {number} cx  The center x position.
  *   @param {number} cy  The center y position.
  *   @param {number} r   The radius of the circle.
  *   @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var   DvtSvgCircle = function(cx, cy, r, id)
{
   this._Init(cx, cy, r, id) ;
};

DvtObj.createSubclass(DvtSvgCircle, DvtSvgShape, "DvtSvgCircle") ;



/*-------------------------------------------------------------------------*/
/*  Init()                                                                 */
/*-------------------------------------------------------------------------*/
/** @private */
DvtSvgCircle.prototype._Init = function(cx, cy, r, id, type)
{
   DvtSvgCircle.superclass._Init.call(this, id, (type ? type : 'circle')) ;

   this.setCx(cx);
   this.setCy(cy);
   this.setRadius(r);
} ;



/*-------------------------------------------------------------------------*/
/*   get/setCx()                                                           */
/*-------------------------------------------------------------------------*/

DvtSvgCircle.prototype.getCx = function()
{
   return  this._cx ;
} ;
/**
  *  Sets the x coordinate of the center.
  *  @param {number} cx  The center x position.
  */

DvtSvgCircle.prototype.setCx = function(cx)
{
   if (cx !== this._cx) {
     this._cx  = cx ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'cx', cx) ;
   }
} ;


/*-------------------------------------------------------------------------*/
/*   get/setCy()                                                           */
/*-------------------------------------------------------------------------*/
/**
  *  Returns the y coordinate of the center.
  *  @type number
  */
DvtSvgCircle.prototype.getCy = function()
{
   return  this._cy ;
} ;

/**
  *  Sets the y coordinate of the center.
  *  @param {number} cy  The center y position.
  *
  */
DvtSvgCircle.prototype.setCy = function(cy)
{
   if (cy !== this._cy) {
     this._cy  = cy ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'cy', cy) ;
   }
} ;

/*-------------------------------------------------------------------------*/
/*   setRadius()                                                           */
/*-------------------------------------------------------------------------*/
/**
  *  Gets the radius  of the circle.
  *  @type {Number}  The radius of the circle.
  */
DvtSvgCircle.prototype.getRadius = function()
{
   return  this._r ;
} ;

/**
  *  Sets the radius  of the circle.
  *  @param {number} r   The radius of the circle.
  */
DvtSvgCircle.prototype.setRadius = function(r)
{
  if(r !== this._r) {
    this._r  = r ;
    DvtSvgUtils.setAttrNullNS(this._elem, 'r', r) ;
  }
} ;

// Copyright (c) 2011, 2023, Oracle and/or its affiliates.
/*-------------------------------------------------------------------------*/
/*    DvtSvgImage           Svg implementation for base shape DvtImage        */
/*-------------------------------------------------------------------------*/
/**
  *   Svg implementation for DvtImage.
  *   @constructor  
  *   Maybe specified as individual values or using a DvtImage object.
  *
  *   e.g. Image = new DvtImage(context,'pic'png', 10, 10, 50, 100);  or
  *
  *        Image = new DvtImage(context, myImage);   where myImage = new DvtImage(context,'pic.png', 10, 10, 50, 100);
  *
  */
var  DvtSvgImage = function(src, x, y, w, h, id)
{
  this._Init(src, x, y, w, h, id) ;
}

DvtObj.createSubclass(DvtSvgImage, DvtSvgShape, "DvtSvgImage");



DvtSvgImage.XLINK_NS = "http://www.w3.org/1999/xlink";

/**
 * Helper method called by the constructor for initializing this object
 * @param {string} src The url for the image
 * @param {number} x The top left x-coordinate of the image
 * @param {number} y The top left y-coordinate of the image
 * @param {number} w The image width
 * @param {number} h The image height
 * @param {string} id The identifier for the image
 * @private
 */
DvtSvgImage.prototype._Init = function(src, x, y, w, h, id)
{
  DvtSvgImage.superclass._Init.call(this, id, 'image') ;
  // IE doesn't allow interactivity unless there's a fill
  if (DvtAgent.isPlatformIE()) {
    DvtSvgUtils.setAttrNullNS(this._elem, 'fill', '#FFFFFF');
    DvtSvgUtils.setAttrNullNS(this._elem, 'fill-opacity', '0');
  }
  this.setImage(src, x, y, w, h);
} ;



/*-------------------------------------------------------------------------*/
/*   get/setHeight()                                                       */
/*-------------------------------------------------------------------------*/
/**
  *  Returns the height of the rectangle.
  *  @type number
  */
DvtSvgImage.prototype.getHeight = function()
{
   return  this._h ;
} ;

/**
  *  Sets the height of the image.
  *  @param {number} h  The height of the image.
  *  @returns nothing
  */
DvtSvgImage.prototype.setHeight= function(h)
{
   if (h !== this._h) {
     this._h  = h ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'height', this._h) ;
   }
} ;



/*-------------------------------------------------------------------------*/
/*   get/setPos(x,y)                                                       */
/*-------------------------------------------------------------------------*/

DvtSvgImage.prototype.getPos = function()
{
   return new DvtPoint(this._x, this._y) ;
};

DvtSvgImage.prototype.setPos= function(x,y)
{
   this.setX(x) ;
   this.setY(y) ;
};



/*-------------------------------------------------------------------------*/
/*   get/setX()                                                            */
/*-------------------------------------------------------------------------*/

DvtSvgImage.prototype.getX = function()
{
   return this._x ;
} ;

DvtSvgImage.prototype.setX = function(x)
{
   if (x !== this._x) {
     this._x  = x ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'x', this._x) ;
   }
} ;

/*-------------------------------------------------------------------------*/
/*   get/setY()                                                            */
/*-------------------------------------------------------------------------*/

DvtSvgImage.prototype.getY = function()
{
   return this._y ;
} ;
DvtSvgImage.prototype.setY = function(y)
{
   if (y !== this._y) {
     this._y  = y ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'y', this._y) ;
   }
} ;


/*-------------------------------------------------------------------------*/
/*   get/setWidth()                                                        */
/*-------------------------------------------------------------------------*/

DvtSvgImage.prototype.getWidth = function()
{
   return this._w ;
} ;

/**
  *  Sets the width of the image.
  *  @param {number} w  The width of the image.
  *  @returns nothing
  */
DvtSvgImage.prototype.setWidth = function(w)
{
   if (w !== this._w) {
     this._w  = w ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'width', this._w) ;
   }
} ;

/*-------------------------------------------------------------------------*/
/*   get/setSrc()                                                          */
/*-------------------------------------------------------------------------*/
/**
  *  Returns the src of the image.
  *  @returns (String) the src of the image.
  */
DvtSvgImage.prototype.getSrc = function()
{
  return this._src;
} ;

/**
  *  Sets the src of the image.
  *  @param {number} src  The src of the image.
  *  @returns nothing
  */
DvtSvgImage.prototype.setSrc = function(src)
{
   if (src !== this._src) {
     this._src  = src ;

     //TODO: 
     // DvtSvgUtils.setAttrNS(this._elem, "xlink", 'href', this._src) ;
     DvtSvgUtils.setAttrNS(this._elem, DvtSvgImage.XLINK_NS, 'xlink:href', this._src) ;
   }
} ;


/*-------------------------------------------------------------------------*/
/*   setImage()                                                            */
/*-------------------------------------------------------------------------*/
/**
  *     Sets the position and size and src of the image
  *     Maybe specified as individual values or using a DvtImage object.
  *
  *   e.g. Image = factory.newImage('pic'png', 10, 10, 50, 100);  or
  *
  *        Image = factory.newImage(myImage);   where myImage = new DvtImage('pic.png', 10, 10, 50, 100);
  */
DvtSvgImage.prototype.setImage = function(src,x,y,w,h)
{
   if (x instanceof DvtImage) {
     this.setSrc(src.src);
     this.setPos(src.x, src.y) ;
     this.setWidth(src.w) ;
     this.setHeight(src.h) ;
   }
   else {
     this.setSrc(src);
     this.setPos(x,y) ;
     this.setWidth(w) ;
     this.setHeight(h) ;
   }

   //TODO: set preserveAspectRatio="none" for now
   DvtSvgUtils.setAttrNullNS(this._elem, 'preserveAspectRatio', 'none') ;
} ;


/**
 * @override
 */
DvtSvgImage.prototype.getDimensions = function() {
  var bbox = this.getElem().getBBox();

  if (bbox.width && bbox.height) {
    return new DvtRectangle(bbox.x, bbox.y, bbox.width, bbox.height);
  }
  else {
    return new DvtRectangle(bbox.x, bbox.y, this.getWidth(), this.getHeight());
  }

};


/**
  *  Loads the image and sets the image size if it was loaded
  *  Returns the dimension of the image if it was loaded
  */
DvtSvgImage.prototype.loadImage = function() {
  if (this._src) {
    var imageSize = DvtSvgImageLoader.loadImage(this._src);
    if (imageSize) {
      this.setWidth(imageSize.width);
      this.setHeight(imageSize.height);

      //BUG 17601740 - FIREFOX: NODE CONTENT COLLAPSED ON INITIAL RENDERING 
//      return this.getDimensions();
      return new DvtRectangle(0, 0, this.getWidth(), this.getHeight());
    }
  }
  return null;
}

// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*---------------------------------------------------------------------*/
/*   DvtSvgLine          SVG Implementation of DvtLine                 */
/*---------------------------------------------------------------------*/
/**
  *   Creates an SVG implementation of a line segment drawn from (x1, y1) to (x2, y2).
  *   An object of this class must not created directly, use {@link DvtLine}.
  *   @extends DvtSvgShape
  *   @class DvtSvgLine
  *   @constructor  
  *   @param {number} x1  An x end-position of the line.
  *   @param {number} y1  The associated y end-point (for x1).
  *   @param {number} x2  The other end-point's x coordinate.
  *   @param {number} y2  The y coordinate associated with x2.
  *   @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var DvtSvgLine = function(x1, y1, x2, y2, id)
{
   this._Init(x1, y1, x2, y2, id) ;
};

DvtObj.createSubclass(DvtSvgLine, DvtSvgShape, "DvtSvgLine") ;


/*---------------------------------------------------------------------*/
/*   _Init()                                                           */
/*---------------------------------------------------------------------*/
/**   @private  */
DvtSvgLine.prototype._Init = function(x1, y1, x2, y2, id)
{
   DvtSvgLine.superclass._Init.call(this, id, 'line') ;

   DvtSvgUtils.setAttrNullNS(this._elem, 'fill', 'none') ;
   this.setX1(x1);
   this.setY1(y1);
   this.setX2(x2);
   this.setY2(y2);
   this._bHollow = false;
} ;

/*---------------------------------------------------------------------*/
/*   get/setX1()                                                       */
/*---------------------------------------------------------------------*/

DvtSvgLine.prototype.getX1 = function()
{
   return this._x1 ;
} ;

DvtSvgLine.prototype.setX1 = function(x1)
{
   if (x1 !== this._x1) {
     this._x1  = x1 ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'x1', this._x1) ;
   }
} ;

/*---------------------------------------------------------------------*/
/*   get/setY1()                                                       */
/*---------------------------------------------------------------------*/

DvtSvgLine.prototype.getY1 = function()
{
   return this._y1 ;
} ;

DvtSvgLine.prototype.setY1 = function(y1)
{
   if (y1 !== this._y1) {
     this._y1  = y1 ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'y1', this._y1) ;
   }
} ;

/*---------------------------------------------------------------------*/
/*   get/setX2()                                                       */
/*---------------------------------------------------------------------*/

DvtSvgLine.prototype.getX2 = function()
{
   return this._x2 ;
} ;

DvtSvgLine.prototype.setX2 = function(x2)
{
   if (x2 !== this._x2) {
     this._x2  = x2 ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'x2', this._x2) ;
   }
} ;

/*---------------------------------------------------------------------*/
/*   get/setY2()                                                       */
/*---------------------------------------------------------------------*/

DvtSvgLine.prototype.getY2 = function()
{
   return this._y2 ;
} ;

DvtSvgLine.prototype.setY2 = function(y2)
{
   if (y2 !== this._y2) {
     this._y2  = y2 ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'y2', this._y2) ;
   }
} ;

/**  Changes the shape to an outline shape format.  Used for legend
  *  markers that represent a hidden state for the associated series risers.
  *                            reinstates the pevious state.
  *  @override
  */
DvtSvgLine.prototype.setHollow = function()
{
    var  parentElem = this._elem.parentNode ;

    if (!this._bHollow) {
        this._origElem   = this._elem;
        var hollowMarker; 
        var width  = this.getX2() - this.getX1() ;   // Legend lines are always horizontal, so take width as height
        var height = width ;
        var startY = this.getY1() - width/2 ;
        var stroke = this.getStroke();
        
        hollowMarker = DvtSvgShapeUtils.createElement('rect');
        DvtSvgUtils.setAttrNullNS(hollowMarker, 'x', this.getX1()) ;
        DvtSvgUtils.setAttrNullNS(hollowMarker, 'y', startY) ;
        DvtSvgUtils.setAttrNullNS(hollowMarker, 'width', this.getX2() - this.getX1()) ;
        DvtSvgUtils.setAttrNullNS(hollowMarker, 'height', height) ;
        var color = stroke.getColor();
        if (color) {
          var alpha = stroke.getAlpha();
          // Workaround for Safari where versions < 5.1 draw rgba values as black
          if (DvtAgent.isBrowserSafari() && color.indexOf("rgba") !==  - 1) {
            DvtSvgUtils.setAttrNullNS(hollowMarker, 'stroke', DvtColorUtils.getRGB(color));
            // Use alpa in rgba value as a multiplier to the alpha set on the object as this is what svg does.
            if (alpha != null)
              DvtSvgUtils.setAttrNullNS(hollowMarker, 'stroke-opacity', DvtColorUtils.getAlpha(color) * alpha);
            else 
              DvtSvgUtils.setAttrNullNS(hollowMarker, 'stroke-opacity', DvtColorUtils.getAlpha(color));
          }
          else {
            DvtSvgUtils.setAttrNullNS(hollowMarker, 'stroke', color);
            if (alpha != null)
              DvtSvgUtils.setAttrNullNS(hollowMarker, 'stroke-opacity', alpha);
          }
        }
        DvtSvgUtils.setAttrNullNS(hollowMarker, 'fill', '#ffffff') ;
        DvtSvgUtils.setAttrNullNS(hollowMarker, 'fill-opacity', '0.001') ;    // need this or hit detection fails on center
        DvtSvgUtils.setAttrNullNS(hollowMarker, 'shape-rendering', 'crispEdges') ;
        hollowMarker._obj = this;       // replace the elem's _obj backpointer 
        
        parentElem.replaceChild(hollowMarker, this._elem) ;
        this._elem = hollowMarker ;
        DvtSvgUtils.setAttrNullNS(this._elem, 'opacity', this._alpha);
        this._bHollow = true;
    }
    else if (this._origElem) {
        parentElem.replaceChild(this._origElem, this._elem) ;
        DvtSvgUtils.setAttrNullNS(this._origElem, 'opacity', this._alpha);
        this._elem = this._origElem ;
        this._origElem   = null ;
        this._bHollow = false;
    }
} ;  

/**  Returns whether a shape is hollow.
  *  @returns {boolean} True if the shape is hollow
  *  @override
  */
DvtSvgShape.prototype.isHollow = function() {
    return this._bHollow;
}

// TODO JSDoc
// Must be connected for this to work
DvtSvgLine.prototype.getDimensions = function(targetCoordinateSpace) {
  var bounds = DvtSvgLine.superclass.getDimensions.call(this, targetCoordinateSpace);
  // BUG #12806130: Vertical/horizontal lines in svg are ignored when group containers wrap them
  if (this._childGroupElem && this._elem) {
      var groupBox = this._childGroupElem.getBBox();
      // Empty bounding box is an indication of only vertical/horizontal lines present in this group
      // In this case, use the original line itself to get the bounds as an approximation
      if (groupBox.x == 0 && groupBox.y == 0 && groupBox.width == 0 && groupBox.height == 0) {
         var lineBounds = this._elem.getBBox();
         bounds = new DvtRectangle(lineBounds.x, lineBounds.y, lineBounds.width, lineBounds.height);
      }
  }
  return bounds;
};
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*--------------------------------------------------------------------*/
/*   DvtSvgMarker         SVG implementation of a DvtMarker           */
/*--------------------------------------------------------------------*/
/**
  *   An SVG implementation of DvtMarker
  *   @class An SVG implementation of DvtMarker. Do not create directly, use DvtMarker.
  *   @extends DvtSvgShape
  *   @constructor    Returns a DvtSvgMarker instance.
  *   @param {number} type  The marker type (such as {@link DvtMarker#CIRCLE}).
  *   @type  DvtSvgMarker
  */
var  DvtSvgMarker = function(type, id, etype)
{
  this._Init(type, id, etype) ; 
}  ;

DvtObj.createSubclass(DvtSvgMarker, DvtSvgShape, "DvtSvgMarker") ;

// The reference coords and sizes that the shapes will be initialized to.
DvtSvgMarker._REFERENCE_X = 0;
DvtSvgMarker._REFERENCE_Y = 0;
DvtSvgMarker._REFERENCE_W = 10;
DvtSvgMarker._REFERENCE_H = 10;

DvtSvgMarker.MARKER_CIRCLE_ELEM_NAME = "circle";
DvtSvgMarker.MARKER_ELLIPSE_ELEM_NAME = "ellipse";
DvtSvgMarker.MARKER_LINE_ELEM_NAME = "line";
DvtSvgMarker.MARKER_PATH_ELEM_NAME = "path";
DvtSvgMarker.MARKER_POLYGON_ELEM_NAME = "polygon";
DvtSvgMarker.MARKER_POLYLINE_ELEM_NAME = "polyline";
DvtSvgMarker.MARKER_RECT_ELEM_NAME = "rect";


/**
 * Initializes the svg element type based on the marker type
 * @param {string} type The marker type
 * @param {id} id The identifier for this marker
 * @param {string} etype The svg element type to use for this marker
 * @private
 */ 
DvtSvgMarker.prototype._Init = function(type, id, etype)
{
   var elemType;
   if (etype) {
     elemType = etype;
   }
   else {
     elemType = (type === DvtMarker.CIRCLE? 'ellipse' :
                (type === DvtMarker.SQUARE? 'rect' :
                (type === DvtMarker.ROUNDED_RECTANGLE? 'rect' :
                (type === DvtMarker.TRIANGLE_UP? 'polygon' :
                (type === DvtMarker.TRIANGLE_DOWN? 'polygon' :
                (type === DvtMarker.DIAMOND? 'polygon' :
                (type === DvtMarker.PLUS? 'polygon' :
                (type === DvtMarker.HUMAN? 'path' :
                (type === DvtMarker.IMAGE? 'image' : 'rect'))))))))) ;
   }         
   DvtSvgMarker.superclass._Init.call(this, id, elemType);
   
   // Store the type
   this._type = type;
   
   //Store reference to underlay
   this._underlay = null;
   
   //default scale of the marker used to force it to the reference size
   this._defaultScale = 1;
};

/**
 * Sets the position and size of the marker.
 * @param {number} x The top left x-coordinate of the marker's bounding rectangle.
 * @param {number} y The top left y-coordinate of the marker's bounding rectangle.
 * @param {number} w The width of the marker's bounding rectangle.
 * @param {number} h The height of the marker's bounding rectangle.
 * @param {DvtMarkerDef} markerDef
 */
DvtSvgMarker.prototype.setBounds = function(x, y, w, h, markerDef) {   
   // Initialize the shape to the reference coords
   if(!this._shapeInitialized)
     this._initShape(this._type, markerDef, x, y, w, h);  
}

/**
 * Initializes the shape to the specified coordinates.
 * @param {string} type The marker type
 * @param {DvtMarkerDef} markerDef The marker definition used for custom markers generated on the server
 * @param {number} x The top left x-coordinate of the marker's bounding rectangle.
 * @param {number} y The top left y-coordinate of the marker's bounding rectangle.
 * @param {number} w The width of the marker's bounding rectangle.
 * @param {number} h The height of the marker's bounding rectangle.
 * @private
 */
DvtSvgMarker.prototype._initShape = function (type, markerDef, x, y, w, h) {
  // Only need to do this once
  this._shapeInitialized = true;

  // Save info for underlay positioning
  this._x = x;
  this._y = y;
  this._w = w;
  this._h = h;

  var multiPathRoot;
  if (type === DvtMarker.CUSTOM || type === DvtMarker.HUMAN) {
    // Calculate the scale factor to get to the right size
    var dim = DvtDisplayableUtils._getDimForced(this.getObj().getCtx(),
                                                markerDef);
    var maxDim = Math.max(dim.w, dim.h);

    var sx = 1;
    var sy = 1;

    // Calculate the transform to get to the right position
    if (type === DvtMarker.CUSTOM) {
      sx = w/(this.getObj().getMaintainAspect() ? maxDim : dim.w);
      sy = h/(this.getObj().getMaintainAspect() ? maxDim : dim.h);
      var dx = x + (-dim.x * sx) + (w - (dim.w * sx))/2;
      var dy = y + (-dim.y * sy) + (h - (dim.h * sy))/2;
      multiPathRoot = this._setCustomMarker(markerDef, dx, dy, sx, sy);
    }
    else if (type === DvtMarker.HUMAN) {
      sx = w/maxDim;
      sy = h/maxDim;
      var dx = x + (-dim.x * sx) + (w - (dim.w * sx))/2;
      var dy = y + (-dim.y * sy) + (h - (dim.h * sy))/2;
      this._setCmds(DvtPathUtils.transformPath(this.getObj().getSkin() == DvtCSSStyle.SKIN_ALTA ? DvtMarkerDef.HUMAN2_CMDS :
                                                DvtMarkerDef.HUMAN_CMDS, dx, dy, sx, sy));
    }
    var scale = (dim.h === maxDim) ? (h/maxDim) : (w/maxDim);
    //save the default scale used to force the marker to the reference size
    //because we may need it later for inversely scaling the selection stroke
    this._defaultScale = scale;    
  }
  else if (type === DvtMarker.IMAGE) {
    var src = this.getObj().GetMarkerImage(DvtMarker.IMAGE_SOURCE);
    this._setX(x);
    this._setY(y);
    this._setWidth(w);
    this._setHeight(h);
    this.setSource(src);
    DvtSvgUtils.setAttrNullNS(this._elem, 'preserveAspectRatio', 'none') ;
    // IE doesn't allow interactivity unless there's a fill
    if (DvtAgent.isPlatformIE()) {
      DvtSvgUtils.setAttrNullNS(this._elem, 'fill', '#FFFFFF');
      DvtSvgUtils.setAttrNullNS(this._elem, 'fill-opacity', '0');
    }
  }
  else if (type === DvtMarker.SQUARE) {
    this._setX(x);
    this._setY(y);
    this._setWidth(w);
    this._setHeight(h);
  }
  else if (type === DvtMarker.ROUNDED_RECTANGLE) {
    this._setX(x);
    this._setY(y);
    this._setWidth(w);
    this._setHeight(h);
    var rx = 6;
    var ry = 6;
    if (w/4 < rx || h/4 < ry) {
      rx = Math.min(w, h)/4;
      ry = rx;
    }
    this._setRX(rx);
    this._setRY(ry);
  }
  else if (type === DvtMarker.CIRCLE) {
    this._setCx(x + w/2);
    this._setCy(y + h/2);
    this._setRX(w/2);
    this._setRY(h/2);
  }
  else {
    var ar = [];
    var halfWidth = w / 2;
    var halfHeight = h / 2;
   
    if (type === DvtMarker.TRIANGLE_UP) {
      ar.push(x);
      ar.push(y + h);
      ar.push(x + w);
      ar.push(y + h);
      ar.push(x + halfWidth);
      ar.push(y);
      this._setPolygon(ar);
    }
    else if (type === DvtMarker.TRIANGLE_DOWN) {
      ar.push(x);
      ar.push(y);
      ar.push(x + w);
      ar.push(y);
      ar.push(x + halfWidth);
      ar.push(y + h);
      this._setPolygon(ar);
    }
    else if (type === DvtMarker.DIAMOND) {
      ar.push(x + halfWidth);
      ar.push(y);
      ar.push(x + w);
      ar.push(y + halfHeight);
      ar.push(x + halfWidth);
      ar.push(y + h);
      ar.push(x);
      ar.push(y + halfHeight);
      this._setPolygon(ar);
    }
    else if (type === DvtMarker.PLUS) {
      var wThird = w / 3;
      var wTwoThird = 2 * wThird;
      var hThird = h / 3;
      var hTwoThird = 2 * hThird;

      ar.push(x + wThird);
      ar.push(y);
      ar.push(x + wTwoThird);
      ar.push(y);
      ar.push(x + wTwoThird);
      ar.push(y + hThird);
      ar.push(x + w);
      ar.push(y + hThird);
      ar.push(x + w);
      ar.push(y + hTwoThird);
      ar.push(x + wTwoThird);
      ar.push(y + hTwoThird);
      ar.push(x + wTwoThird);
      ar.push(y + h);
      ar.push(x + wThird);
      ar.push(y + h);
      ar.push(x + wThird);
      ar.push(y + hTwoThird);
      ar.push(x);
      ar.push(y + hTwoThird);
      ar.push(x);
      ar.push(y + hThird);
      ar.push(x + wThird);
      ar.push(y + hThird);
      ar.push(x + wThird);
      ar.push(y);
      this._setPolygon(ar);
    }
  }
}

/**
  *  Returns the default scale of the marker used to force it to the 
  *  reference size.
  *  @type number
  */
DvtSvgMarker.prototype.getDefaultScale = function()
{
   return this._defaultScale;
};

/**
  *  Returns the type of the marker (such as {@link DvtMarker#CIRCLE}.
  *  @type number
  */
DvtSvgMarker.prototype.getType = function()
{
   return  this._type ;
} ;

/**
 * @private
 */
DvtSvgMarker.prototype._setCx = function(cx)
{
  DvtSvgUtils.setAttrNullNS(this._elem, 'cx', cx) ;
} ;

/**
 * @private
 */
DvtSvgMarker.prototype._setCy = function(cy)
{
  DvtSvgUtils.setAttrNullNS(this._elem, 'cy', cy) ;
} ;

/**
 * @private
 */
DvtSvgMarker.prototype._setX = function(x)
{
  DvtSvgUtils.setAttrNullNS(this._elem, 'x', x) ;
} ;

/**
 * @private
 */
DvtSvgMarker.prototype._setY = function(y)
{
  DvtSvgUtils.setAttrNullNS(this._elem, 'y', y) ;
} ;

/**
 * @private
 */
DvtSvgMarker.prototype._setWidth = function(w)
{
  DvtSvgUtils.setAttrNullNS(this._elem, 'width', w) ;
} ;

/**
 * @private
  */
DvtSvgMarker.prototype._setHeight = function(h)
{
  DvtSvgUtils.setAttrNullNS(this._elem, 'height', h) ;
} ;

/**
 * @private
  */
DvtSvgMarker.prototype._setRadius = function(r)
{
   DvtSvgUtils.setAttrNullNS(this._elem, 'r', r) ;
} ;

/**
 *   @private
 */
DvtSvgMarker.prototype._setPolygon = function(ar)
{
   var sPoints = DvtSvgShapeUtils.convertPointsArray(ar);
   DvtSvgUtils.setAttrNullNS(this._elem, 'points', sPoints) ;
} ;

/**
 *   @private
 */
DvtSvgMarker.prototype._setCmds = function(cmds)
{
   if (cmds !== this._cmds) {
     this._cmds  = cmds ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'd', cmds) ;
   }
} ;


DvtSvgMarker.prototype.UpdateMarkerImage = function(imgSrc) {
  this.setSource(imgSrc);
}


/**
 * Updates the marker image source based on the current selection state
 * @param {string} src The image uri to set for the current marker state
 */
DvtSvgMarker.prototype.setSource = function(src)
{
  DvtSvgUtils.setAttrNS(this._elem, DvtSvgImage.XLINK_NS, 'xlink:href', src);
}
/**
 * @override
 */
DvtSvgMarker.prototype.GetAttributesTransferableToGroup = function() {
  var attrNames = DvtSvgContainer.AttributesTransferableToGroup.slice(0);
  // Check to see if matrix set since we don't transfer the 'transform' attr, bc it affects the clip path for custom markers.
  if (!this.getObj().getMatrix()) {
    var transformIndex = DvtArrayUtils.getIndex(attrNames, 'transform');
    attrNames.splice(transformIndex, 1);
  }
  var visibilityIndex = DvtArrayUtils.getIndex(attrNames, 'visibility');
  attrNames.splice(visibilityIndex, 1);
  return attrNames;
}

/**
 * Sets the position and size of the underlay for all but human markers
 * @private
 */
DvtSvgMarker.prototype._positionUnderlay = function(x, y, w, h)
{
    //Underlays cannot be custom, plus, or human shapes
    var type = this._underlay.getType();
    var radius = Math.max(w, h)/2;
    var pad;
    var ar = [];
    // Padding for triangles
    var triBottomPad = radius*.4;
    var triTipPad = + 1.6*radius;

    if (type === DvtMarker.SQUARE) {
        pad = radius*.5;
        this._underlay._setX(x - pad);
        this._underlay._setY(y - pad);
        this._underlay._setWidth(2*(radius + pad));
        this._underlay._setHeight(2*(radius + pad));
    }
    else if (type === DvtMarker.CIRCLE) {
        pad = radius*.7;
        this._underlay._setCx(x + w/2);
        this._underlay._setCy(y + h/2);
        this._underlay._setRX(radius + pad);
        this._underlay._setRY(radius + pad);
    }
    else if (type === DvtMarker.TRIANGLE_UP) {  
      pad = radius * 1.3;
      ar.push(x - pad);
      ar.push(y + 2*radius + triBottomPad);
      ar.push(x + radius);
      ar.push(y - triTipPad);
      ar.push(x + 2*radius + pad);
      ar.push(y + 2*radius + triBottomPad);
      this._underlay._setPolygon(ar);
    }
    else if (type === DvtMarker.TRIANGLE_DOWN) {
      pad = radius * 1.3;
      ar.push(x - pad);
      ar.push(y - triBottomPad);
      ar.push(x + 2*radius + pad);
      ar.push(y - triBottomPad);
      ar.push(x + radius);
      ar.push(y + 2*radius + triTipPad);
      this._underlay._setPolygon(ar);
    }
    else if (type === DvtMarker.DIAMOND) {
      pad = radius * 1.1;
      ar.push(x - pad);
      ar.push(y + radius);
      ar.push(x + radius);
      ar.push(y - pad);
      ar.push(x + 2*radius + pad);
      ar.push(y + radius);
      ar.push(x + radius);
      ar.push(y + 2*radius + pad);
      this._underlay._setPolygon(ar);
    }
}

/**
 * Sets the position and size of the underlay for human markers
 * @private
 */
DvtSvgMarker.prototype._positionHumanUnderlay = function(x, y, w, h)
{
    //Underlays cannot be custom, plus, or human shapes
    var type = this._underlay.getType();
    var radius = Math.max(w, h)/2;
    var pad;
    var ar = [];
    // Padding for triangles
    var triBottomPad = radius*.2;
    var triTipPad = + 0.65*radius;

    if (type === DvtMarker.SQUARE) {
        pad = radius*.15;
        this._underlay._setX(x - pad);
        this._underlay._setY(y - pad);
        this._underlay._setWidth(2*(radius + pad));
        this._underlay._setHeight(2*(radius + pad));
    }
    else if (type === DvtMarker.CIRCLE) {
        pad = radius*.2;
        this._underlay._setCx(x + w/2);
        this._underlay._setCy(y + h/2);
        this._underlay._setRX(radius + pad);
        this._underlay._setRY(radius + pad);
    }
    else if (type === DvtMarker.TRIANGLE_UP) {  
      pad = radius * 0.5;
      ar.push(x - pad);
      ar.push(y + 2*radius + triBottomPad);
      ar.push(x + radius);
      ar.push(y - triTipPad);
      ar.push(x + 2*radius + pad);
      ar.push(y + 2*radius + triBottomPad);
      this._underlay._setPolygon(ar);
    }
    else if (type === DvtMarker.TRIANGLE_DOWN) {
      pad = radius * 0.5;
      ar.push(x - pad);
      ar.push(y - triBottomPad);
      ar.push(x + 2*radius + pad);
      ar.push(y - triBottomPad);
      ar.push(x + radius);
      ar.push(y + 2*radius + triTipPad);
      this._underlay._setPolygon(ar);
    }
    else if (type === DvtMarker.DIAMOND) {
      pad = radius * 0.4;
      ar.push(x - pad);
      ar.push(y + radius);
      ar.push(x + radius);
      ar.push(y - pad);
      ar.push(x + 2*radius + pad);
      ar.push(y + radius);
      ar.push(x + radius);
      ar.push(y + 2*radius + pad);
      this._underlay._setPolygon(ar);
    }
}

/**  Sets the underlay for markers
 *  @param {string} ut underlay type
 *  @param {string} uc fill color of the underlay
 *  @param {number} ua fill alpha of the underlay
 */
DvtSvgMarker.prototype.setUnderlay = function(ut, uc, ua)
{
    var context = this.getObj().getCtx();
    this._underlay = context.getImplFactory().newMarker(ut);
    this._underlay.setFill(new DvtSolidFill(uc, ua));
    this._underlay.setObj(this.getObj());
    if (this.getType() === DvtMarker.HUMAN) {
        this._positionHumanUnderlay(this._x, this._y, 
                                    this._w, this._h);
    } else {
        this._positionUnderlay(this._x, this._y, 
                               this._w, this._h);
    }
    
    // add the underlay to the group elem explicitly because behavior of addChildAt might change
    this.CreateChildGroupElem(false);    
    var existingNode = this._childGroupElem.childNodes[0];
    // BUG #12661320: IE9 cannot handle a value of undefined in insertBefore.  Set to null in such a case. 
    if (!existingNode)
      existingNode = null;
    this._childGroupElem.insertBefore(this._underlay.getOuterElem(), existingNode);
}


/*---------------------------------------------------------------------*/
/*   getUnderlay()                                                     */
/*---------------------------------------------------------------------*/
/**  Gets the underlay for markers
 */
DvtSvgMarker.prototype.getUnderlay = function() 
{
  return this._underlay;
}

/**
 * Sets whether mouse events are enabled on this object.
 * @param {boolean} whether mouse events are enabled
 */
DvtSvgMarker.prototype.setMouseEnabled = function(bEnabled)
{
  DvtSvgMarker.superclass.setMouseEnabled.call(this, bEnabled);
  if (this._childGroupElem) {
      var val;
      if (bEnabled)
      {
        val = "visiblePainted";
      }
      else
      {
        val = "none";
      }
      DvtSvgUtils.setAttrNullNS(this._childGroupElem, "pointer-events", val);
  }
};

/**  Changes the shape to an outline shape format.  Used for legend
  *  markers that represent a hidden state for the associated series risers.
  *  @param {String} color Border color for hollow shape in format of #aarrggbb
  *  @param {number} strokeWidth Stroke width used for shapes that were transofrmed (optional)
  *  @override
  */
DvtSvgMarker.prototype.setHollow = function(color, strokeWidth)
{
    DvtSvgMarker.superclass.setHollow.call(this, color, strokeWidth);
};


DvtSvgMarker.prototype._setCustomMarker = function(markerDef, x, y, sx, sy)
{
  if (this._isMultiPaths()) {
    var root = this._cloneMultiPaths(markerDef, x, y, sx, sy);
    this.getObj().addChild(root);

    // return container of multi paths
    return root;
  }
  else {
    this._setSingleShape(markerDef, x, y, sx, sy);
    return null;
  }
} ;


DvtSvgMarker.prototype._setSingleShape = function(markerDef, x, y, sx, sy)
{
  var defImpl = markerDef.getImpl();
  var type = defImpl.getElem().nodeName;

  if (type == DvtSvgMarker.MARKER_PATH_ELEM_NAME) {
    this._setCmds(DvtPathUtils.transformPath(defImpl._sCmds, x, y, sx, sy));
  }
  /*
  else if (type == DvtSvgMarker.MARKER_CIRCLE_ELEM_NAME ||
           type == DvtSvgMarker.MARKER_ELLIPSE_ELEM_NAME) {
    this._setCx(markerDef.getCx());
    this._setCy(markerDef.getCy());
    this._setRadius(markerDef.getRadius());
  }

  else if (type == DvtSvgMarker.MARKER_LINE_ELEM_NAME) {
    this._setX1(defImpl.getX1());
    this._setX2(defImpl.getX2());
    this._setY1(defImpl.getY1());
    this._setY2(defImpl.getY2());
  }
  else if (type == DvtSvgMarker.MARKER_POLYGON_ELEM_NAME) {
    this._setPoints(defImpl._sPoints);
  }
  else if (type == DvtSvgMarker.MARKER_POLYLINE_ELEM_NAME) {
    this._setPoints(defImpl._sPoints);
  }
  else if (type == DvtSvgMarker.MARKER_RECT_TYPE_ELEM_NAME) {
    this._setX(markerDef.getX());
    this._setY(markerDef.getY());
    this._setWidth(markerDef.getWidth());
    this._setHeight(markerDef.getHeight());
  }
  */

  var fill = defImpl.getFill();
  if (fill) {
    this.setFill(fill);
  }
  var alpha = defImpl.getAlpha();
  if (alpha) {
    this.setAlpha(alpha);
  }
  var stroke = defImpl.getStroke();
  if (stroke) {
    var scaledStroke = stroke.clone();
    scaledStroke.setWidth(Math.min(sx, sy) * scaledStroke.getWidth());
    this.setStroke(scaledStroke);
  }
} ;


/**
 *   @private
 */
DvtSvgMarker.prototype._setPoints = function(points)
{
   if (points !== this._points) {
     this._points  = points ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'points', points);
   }
} ;

/**
 * @private
 */
DvtSvgMarker.prototype._setX1 = function(x1)
{
  DvtSvgUtils.setAttrNullNS(this._elem, 'x1', x1) ;
} ;

/**
 * @private
 */
DvtSvgMarker.prototype._setY1 = function(y1)
{
  DvtSvgUtils.setAttrNullNS(this._elem, 'y1', y1) ;
} ;

/**
 * @private
 */
DvtSvgMarker.prototype._setX2 = function(x2)
{
  DvtSvgUtils.setAttrNullNS(this._elem, 'x2', x2) ;
} ;

/**
 * @private
 */
DvtSvgMarker.prototype._setY2 = function(y2)
{
  DvtSvgUtils.setAttrNullNS(this._elem, 'y2', y2) ;
} ;

/**
 * @private
 */
DvtSvgMarker.prototype._setRX = function(rx)
{
  DvtSvgUtils.setAttrNullNS(this._elem, 'rx', rx) ;
} ;

/**
 * @private
 */
DvtSvgMarker.prototype._setRY = function(ry)
{
  DvtSvgUtils.setAttrNullNS(this._elem, 'ry', ry) ;
} ;


/**
 * @private
 */
DvtSvgMarker.prototype._cloneMultiPaths = function(markerDef, x, y, sx, sy) {
  var context = this.getObj().getCtx();
  var root = new DvtContainer(context, markerDef.getId() + "_x");

  var childCnt = markerDef.getNumChildren();
  var childDef;
  var child;
  for (var i = 0; i < childCnt; i++) {
    childDef = markerDef.getChildAt(i);
    child = new DvtPath(context, DvtPathUtils.transformPath(childDef.getCmds(), x, y, sx, sy), childDef.getId());

    if (childDef.getFill()) {
      child.setFill(childDef.getFill());
    }
    if (childDef.getAlpha()) {
      child.setAlpha(childDef.getAlpha());
    }
    if (childDef.getStroke()) {
      var scaledStroke = childDef.getStroke().clone();
      scaledStroke.setWidth(Math.min(sx, sy) * scaledStroke.getWidth());
      child.setStroke(scaledStroke);
    }
    root.addChild(child);
  }

  return root;
}

/**
 * @override
 */
DvtSvgMarker.prototype.addChild = function(obj) {
  // if this marker has multi paths, don't add an additonal childGroupElem
  if (this._isMultiPaths()) {
    DvtSvgUtils.appendChildElem(this._elem, obj.getOuterElem()) ;
  }
  else {
    DvtSvgMarker.superclass.addChild.call(this, obj);
  }
};


DvtSvgMarker.prototype._isMultiPaths = function() {
  return (this.getElem().nodeName == "g" && this.getType() != DvtMarker.IMAGE);
};

/**
 * @override
 */
DvtSvgMarker.prototype.getDimensions = function() {
  // Added in order to prevent getDimensions returning null or a 0 width and height while waiting for an image load
  if (this._type == DvtMarker.IMAGE) {
    var w = this.getObj().getScaledWidth();
    var h = this.getObj().getScaledHeight()
    return new DvtRectangle(this._x, this._y, w, h);
  } else {
    return DvtSvgMarker.superclass.getDimensions.call(this);
  }
}

/**
 * @override
 */
DvtSvgMarker.prototype.GetElemDimensionsWithStroke = function() {
  if (this._type == DvtMarker.IMAGE)
    return this.getDimensions(); // images don't have borders
  else
    return DvtSvgMarker.superclass.GetElemDimensionsWithStroke.call(this);
}
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*-------------------------------------------------------------------------*/
/*    DvtSvgOval       Svg implementation of base elliptical shape         */
/*-------------------------------------------------------------------------*/
/**
  *   Creates an elliptical shape implemented by SVG.
  *   @extends DvtSvgCircle
  *   @class  DvtSvgOval Creates an elliptical shape implemented by SVG. Do not create directly
  *   - use {@link DvtOval}.
  *   <p>
  *   Example:<br><br><code>
  *   var Oval = new DvtOval(context, 50, 50, 20, 40) ;<br>
  *   </code>
  *   @constructor  
  *   @param {number} cx  The center x position.
  *   @param {number} cy  The center y position.
  *   @param {number} rx  The horizontal radius of the ellipse.
  *   @param {number} ry  The vertical radius of the ellipse.
  *   @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var  DvtSvgOval = function(cx, cy, rx, ry, id)
{
   this._Init(cx, cy, rx, ry, id) ;
};

DvtObj.createSubclass(DvtSvgOval, DvtSvgCircle, "DvtSvgOval") ;



/*-------------------------------------------------------------------------*/
/*  _Init()                                                                */
/*-------------------------------------------------------------------------*/
/** @private */

DvtSvgOval.prototype._Init = function(cx, cy, rx, ry, id)
{
   DvtSvgOval.superclass._Init.call(this, cx, cy, rx, id, 'ellipse') ;

   this.setRx(rx);
   this.setRy(ry);
   this.setRadius(null) ;
} ;


/*-------------------------------------------------------------------------*/
/*   get/setRx()                                                           */
/*-------------------------------------------------------------------------*/
/**
  *  Returns the horizontal radius of the ellipse.
  *  @type number
  */
DvtSvgOval.prototype.getRx = function()
{
   return this._rx ;
} ;

/**
  *  Sets the horizontal radii of the ellipse.
  *  @param {number} rx  The horizontal radius of the ellipse.
  */
DvtSvgOval.prototype.setRx = function(rx)
{
   if (rx !== this._rx) {
     this._rx  = rx ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'rx', this._rx) ;
   }
} ;


/*-------------------------------------------------------------------------*/
/*   get/setRy()                                                           */
/*-------------------------------------------------------------------------*/
/**
  *  Returns the vertical radius of the ellipse.
  *  @type number
  */
DvtSvgOval.prototype.getRy = function()
{
   return this._ry ;
} ;

/**
  *  Sets the vertical radius of the ellipse.
  *  @param {number} ry  The vertical radius of the ellipse.
  */
DvtSvgOval.prototype.setRy = function(ry)
{
   if (ry !== this._ry) {
     this._ry  = ry ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'ry', this._ry) ;
   }
} ;

// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/**
  *   Creates a shape using SVG path commands implemented for SVG.
  *   @extends DvtSvgShape
  *   @class DvtSvgPath  Creates a shape using SVG path commands implemented for
  *   SVG.  Do not create directly, use {@link DvtPath}.
  *   <p>Example:<br><br><code>
  *   var  path = new DvtPath(context, mycmds) ;<br>
  *   </code>
  *   @constructor  
  *   @param {Object} cmds  Optional string of SVG path command (see comment for
  *                         {@link DvtPath#setCmds}),
  *                         or an array containing consecutive command and coordinate
  *                          entries (see comment for {@link DvtPath#setCommands}).
  *   @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var  DvtSvgPath = function(cmds, id)
{
   this._Init(cmds, id) ;
};

DvtObj.createSubclass(DvtSvgPath, DvtSvgShape, "DvtSvgPath") ;

/** @private */
DvtSvgPath.prototype._Init = function(cmds, id)
{
   DvtSvgPath.superclass._Init.call(this, id, 'path') ;

   if (DvtArrayUtils.isArray(cmds)) {
     this.setCmds(DvtPathUtils.getPathString(cmds)) ;
   }
   else {
     this.setCmds(cmds) ;
   }
} ;

/**
  *  Gets the path from a string of SVG commands in SVG "d" attribute format.
  *  @param {String} cmds A string containing the SVG commands sequences.
  */
DvtSvgPath.prototype.getCmds = function()
{
   return this._sCmds ;
} ;
/**
  *  Sets the path from a string of SVG commands in SVG "d" attribute format.
  *  @param {String} cmds A string containing the SVG commands sequences.
  */
DvtSvgPath.prototype.setCmds = function(cmds)
{
   this._sCmds  = cmds ;
   if (cmds) {
     DvtSvgUtils.setAttrNullNS(this._elem, 'd', cmds) ;
   }
} ;
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*-------------------------------------------------------------------------*/
/*   DvtSvgPolygon         SVG Implementation of Base Polygon shape        */
/*-------------------------------------------------------------------------*/
/**
  *   Creates a polygon from an array of (x,y) coordinates.
  *   SVG implementation for DvtPolygon which creates a shape of connected line segments.
  *   @extends DvtSvgShape
  *   @class DvtSvgPolygon
  *   @constructor  
  *   @param {Array} arPoints  An array of x, y pairs of coordinates.
  *   @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var  DvtSvgPolygon = function(arPoints, id)
{
   this._Init(arPoints, id) ;
};

DvtObj.createSubclass(DvtSvgPolygon, DvtSvgShape, "DvtSvgPolygon") ;





/*-------------------------------------------------------------------------*/
/*   _Init()                                                               */
/*-------------------------------------------------------------------------*/
/** @private */
DvtSvgPolygon.prototype._Init = function(arPoints, id)
{
   DvtSvgPolygon.superclass._Init.call(this, id, 'polygon') ;

   this.setPoints(arPoints) ;

} ;

/*-------------------------------------------------------------------------*/
/*   get/setPoints()            Gets/Sets the polygon points               */
/*-------------------------------------------------------------------------*/

DvtSvgPolygon.prototype.getPoints = function()
{
   return  this._arPoints ;
} ;

/**
  *   Sets the polygon points from an array of x,y coordinate pairs.
  *   @param {Array} arPoints  An array of x, y pairs of coordinates.
  */
DvtSvgPolygon.prototype.setPoints = function(arPoints)
{
   this._arPoints = arPoints ;
   this._sPoints = DvtSvgShapeUtils.convertPointsArray(arPoints);
   DvtSvgUtils.setAttrNullNS(this._elem, 'points', this._sPoints) ;
} ;
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*--------------------------------------------------------------------*/
/*   DvtSvgPolyline         SVG Implementation of DvtPolyline         */
/*--------------------------------------------------------------------*/
/**
  *   Svg implementation for DvtPolyline which creates a series of joined
  *   lines whose points are specified by an array of x,y pairs).
  *   @extends DvtSvgShape
  *   @class DvtSvgPolyline
  *   @constructor  
  *   @param {Array} arPoints  An array of point coordinates.
  *   @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *   <p>
  *   Example:<br><br><code
  *   var points = [10,20,40,34,59,128, . . .] ;<br>
  *   var polyline = new DvtPolyline(context, points);<br>
  *    </code>
  */
var DvtSvgPolyline = function(arPoints, id)
{
   this._Init(arPoints, id) ;
};

DvtObj.createSubclass(DvtSvgPolyline, DvtSvgShape, "DvtSvgPolyline") ;





/*--------------------------------------------------------------------*/
/*   _Init()                                                          */
/*--------------------------------------------------------------------*/

DvtSvgPolyline.prototype._Init = function(arPoints, id)
{
   DvtSvgPolyline.superclass._Init.call(this, id, 'polyline') ;

   DvtSvgUtils.setAttrNullNS(this._elem, 'fill', 'none') ;
   this.setPoints(arPoints) ;

} ;


/*-------------------------------------------------------------------------*/
/*   get/setPoints()            Gets/Sets the polygon points               */
/*-------------------------------------------------------------------------*/

DvtSvgPolyline.prototype.getPoints = function()
{
   return  this._arPoints ;
} ;

/**
  *   Sets the polyline points from an array of x,y coordinate pairs.
  *   @param {Array} arPoints  An array of x, y pairs of coordinates.
  */
DvtSvgPolyline.prototype.setPoints = function(arPoints)
{
   this._arPoints = arPoints ;
   this._sPoints = DvtSvgShapeUtils.convertPointsArray(arPoints);
   DvtSvgUtils.setAttrNullNS(this._elem, 'points', this._sPoints) ;
} ;
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*---------------------------------------------------------------------*/
/*    DvtSvgRect          Svg implementation for base shape DvtRect    */
/*---------------------------------------------------------------------*/
/**
  *   Svg implementation for DvtRect.
  *   @extends DvtSvgShape
  *   @class DvtSvgRect
  *   @constructor  
  *   May be specified as individual values or using a DvtRectangle object.
  *   <p>
  *   Example:<br><br><code>
  *   rect = new DvtRect(context, 10, 10, 50, 100) ; &nbsp; @nbsp;  or<br>
  *
  *   rect = new DvtRect(context, myRect) ;  &nbsp; &nbsp;  where myRect = new DvtRectangle(10, 10, 50, 100);<br>
  *   </code>
  */
var  DvtSvgRect = function(x, y, w, h, id)
{
   this._Init(x, y, w, h, id) ;
};

DvtObj.createSubclass(DvtSvgRect, DvtSvgShape, "DvtSvgRect") ;

DvtSvgRect._cssAttrs = 
[
  "background-color",
  "border-color",
  "border-width"
];

/*---------------------------------------------------------------------*/
/*  Init()                                                             */
/*---------------------------------------------------------------------*/
/** @private */
DvtSvgRect.prototype._Init = function(x, y, w, h, id)
{
   DvtSvgRect.superclass._Init.call(this, id, 'rect') ;

   this.setX(x);
   this.setY(y);
   this.setWidth(w);
   this.setHeight(h);
} ;

/*---------------------------------------------------------------------*/
/*   get/setX()                                                        */
/*---------------------------------------------------------------------*/

DvtSvgRect.prototype.getX = function()
{
   return this._x ;
} ;
DvtSvgRect.prototype.setX = function(x)
{
   if (x !== this._x) {
     this._x  = x ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'x', this._x) ;
   }
} ;


/*---------------------------------------------------------------------*/
/*   get/setY()                                                        */
/*---------------------------------------------------------------------*/

DvtSvgRect.prototype.getY = function()
{
   return this._y ;
} ;
DvtSvgRect.prototype.setY = function(y)
{
   if (y !== this._y) {
     this._y  = y ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'y', this._y) ;
   }
} ;


/*---------------------------------------------------------------------*/
/*   get/setWidth()                                                    */
/*---------------------------------------------------------------------*/

DvtSvgRect.prototype.getWidth = function()
{
   return this._w ;
} ;

/**
  *  Sets the width of the rectangle.
  *  @param {number} w  The width of the rectangle.
  */
DvtSvgRect.prototype.setWidth = function(w)
{
   if (w !== this._w) {
     this._w  = w ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'width', this._w) ;
   }
} ;

/*---------------------------------------------------------------------*/
/*   get/setHeight()                                                   */
/*---------------------------------------------------------------------*/
/**
  *  Returns the height of the rectangle.
  *  @type number
  */
DvtSvgRect.prototype.getHeight = function()
{
   return  this._h ;
} ;

/**
  *  Sets the height of the rectangle.
  *  @param {number} h  The height of the rectangle.
  */
DvtSvgRect.prototype.setHeight = function(h)
{
   if (h !== this._h) {
     this._h  = h ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'height', this._h) ;
   }
} ;

/*---------------------------------------------------------------------*/
/*   get/setRx()                                                       */
/*---------------------------------------------------------------------*/

DvtSvgRect.prototype.getRx = function()
{
   return this._rx ;
} ;
DvtSvgRect.prototype.setRx = function(rx)
{
   if (rx !== this._rx) {
     this._rx  = rx ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'rx', this._rx) ;
   }
} ;

/*---------------------------------------------------------------------*/
/*   get/setRy()                                                       */
/*---------------------------------------------------------------------*/

DvtSvgRect.prototype.getRy = function()
{
   return this._ry ;
} ;
DvtSvgRect.prototype.setRy = function(ry)
{
   if (ry !== this._ry) {
     this._ry  = ry ;
     DvtSvgUtils.setAttrNullNS(this._elem, 'ry', this._ry) ;
   }
} ;

/**
 * Sets the DvtCSSStyle of this object.
 * @param {DvtCssStyle} style The DvtCSSStyle of this object.
 */
DvtSvgRect.prototype.setCSSStyle = function(style)
{
  DvtSvgRect.superclass.setCSSStyle.call(this, style);
  
  //calling this results in fill="none" being removed
  //on rects that don't apply a css style, which
  //results in them being filled black
  //DvtSvgRect._removeDefaultAttributes(this._elem);
  
  var elem = this._elem;
  if (style)
  {
    var val = style["background-color"];
    if (val) {
      DvtSvgUtils.setAttrNullNS(elem, "fill", val);
    }
    val = style["border-color"];
    if (val) {
      DvtSvgUtils.setAttrNullNS(elem, "stroke", val);
    }
    val = style["border-width"];
    if (val) {
      DvtSvgUtils.setAttrNullNS(elem, "stroke-width", val);
    }

    //bug 13826956 - border-radius css property not supported when used inside <dvt:node>
    val = style["border-radius"];
    if (val) {
      var radArr = DvtStringUtils.trim(val).split(" ");
      if (radArr.length > 0 && radArr[0]) {
        this.setRx(radArr[0]);
      }
      if (radArr.length > 1 && radArr[1]) {
        this.setRy(radArr[1]);
      }
    }
  }
  else
  {
    DvtSvgUtils.removeAttrNullNS(this._elem, "style");
  }
};

DvtSvgRect._removeDefaultAttributes = function(elem)
{
  DvtSvgUtils.removeAttrNullNS(elem,"fill");
  DvtSvgUtils.removeAttrNullNS(elem,"stroke");
  DvtSvgUtils.removeAttrNullNS(elem,"stroke-width");
};
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*-------------------------------------------------------------------------*/
/*   DvtSvgText        SVG implementation of text "shape"                  */
/*-------------------------------------------------------------------------*/
/**
 * Creates an instance of DvtSvgText.
 * @class DvtSvgText
 * @extends DvtSvgShape
 * @constructor
 * @param {DvtContext} context
 * @param textStr {string} the text string
 * @param x {number} x the x position
 * @param y {number} y the y position
 * @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
 */
var  DvtSvgText = function(context, textStr, x, y, id)
{
  this._Init(context, textStr, x, y, id) ;
};

DvtObj.createSubclass(DvtSvgText, DvtSvgShape, "DvtSvgText") ;


/**
 * Initializes this instance of DvtText.
 * @param {DvtContext} context
 * @param textStr {string} the text string
 * @param x {number} x the x position
 * @param y {number} y the y position
 * @param id {string} the id for this object
 */
DvtSvgText.prototype._Init = function(context, textStr, x, y, id)
{
  DvtSvgText.superclass._Init.call(this, id, 'text') ;
  this._context = context;
  this.setTextString(textStr);
  this.setX(x);
  this.setY(y);
  
  // TODO this should be defined elsewhere
  // cursor is needed for chrome, but should be dependent on interactivity
  DvtSvgUtils.setAttrNullNS(this.getElem(), "font-family", "tahoma, sans-serif");
  DvtSvgUtils.setAttrNullNS(this.getElem(), "fill", "rgb(0,0,0)");  
  
  // fix for bug 14528150 (the chrome case) and 14297988 (the ie case)
  if (DvtAgent.isRightToLeft(context)) {
    if (DvtAgent.isPlatformIE())
      this.setTextString("\u202B"+textStr);
    else if (DvtSvgText.needsTextAnchorAdjustment(context))
      this.alignStart();
    else
      DvtSvgUtils.setAttrNullNS(this.getElem(), "unicode-bidi", "embed");
  }
 
  //Bug 13879007 - NODE RENDERED INCORRECTLY IN IE9
  this._baseline = null;
  //FIX BUG 16856961: additional pixel offset for baseline, currently only
  //specified by DvtSvgTextArea subclass
  this._baselineOffset = null;

  //  For edit mode 
  this._bEditMode   = false ;
  this._editStartCallback = null;
  this._editEndCallback = null;
  this._editPxWidth ;
  this._editElem    = null ;    // <textarea> elem
  this._editBorder  = false ;
  this._editTooltip = null ;
} ;

/**
 * Returns the text string for this text object.
 * @return {string} the text string
 */ 
DvtSvgText.prototype.getTextString = function() {
  var txt ;
  if (this._bEditMode && this._editElem) {      // if in edit mode
     txt = this._editElem.value ;
  }
  else {
    var textNode = this.GetTextElem().firstChild;
    if (textNode) {
      txt = textNode.nodeValue;
    }
  }
  return txt ;
} ;

/**
 * Sets the text string for this text object.
 * @param {string} textStr the text string
 */
DvtSvgText.prototype.setTextString = function(textStr)
{
  // Update the text node if it is already created
  var textNode = this.GetTextElem().firstChild;
  if(textNode !== null) {
    textNode.nodeValue = textStr;
  }
  else { // Otherwise create it
    textNode = document.createTextNode(textStr);
    DvtSvgUtils.appendChildElem(this.GetTextElem(), textNode);
  }

  if (this._editElem) {             // if an edit element, change its text also.
    this._editElem.value = textStr ;
  }

} ;


/**
 * Sets the text angle. See also {@link DvtSvgText#setRotatePoint}.
 * @param {number} a the angle in degrees. Positive angles rotate counter-clockwise.
 */
DvtSvgText.prototype.doNotUseSetAngle = function(a)
{
   this._angle = (a % 360) ;

   if (this._angle > 0) {
     this._angle = 360 - this._angle ;    // for svg, positive angles rotate clockwise
    }

   if (this._angle != 0) {
     if (this._cx && this._cy) {
       DvtSvgUtils.setAttrNullNS(this.getElem(), 'transform', "rotate(" + this._angle + " " + 
                                           this._cx + "," + this._cy + ")") ;
    }
  }
  else {
    DvtSvgUtils.removeAttrNullNS(this.getElem(), 'transform');
    if (DvtAgent.isPlatformIE()) { // bug 14622256, prev removeAttributeNS()
      DvtSvgUtils.setAttrNullNS(this.getElem(), 'transform', null);        // fails for IE9 (9.0.8112.16421)
    }
  }
}; 


/**
 * Sets the rotation point for angled text (see {@link DvtSvgText#setAngle}).
 * @param {number} cx the rotation point x-coordinate.
 * @param {number} cy the rotation point y-coordinate.
 */
DvtSvgText.prototype.doNotUseSetRotatePoint = function(cx, cy)
{
  this._cx = cx ;
  this._cy = cy ;
  if (this._angle) {
    DvtSvgUtils.setAttrNullNS(this.getElem(), 'transform', "rotate(" + this._angle + " " + cx + "," + cy + ")") ;
  }
}; 


/**
 * Gets the x position for this text object.
 * @returns {number}  the x position
 */
DvtSvgText.prototype.getX = function()
{
  return  this._x ;
};

/**
 * Sets the x position for this text object.
 * @param {number} x the x position
 */
DvtSvgText.prototype.setX = function(x)
{
  if (x !== this._x) {
    this._x  = x ;
    DvtSvgUtils.setAttrNullNS(this.getElem(), 'x', x);
  }
}; 

/**
 * Gets the y position for this text object.
 * @returns {number}  the y position
 */
DvtSvgText.prototype.getY = function()
{
  return  this._y ;
};
/**
 * Sets the y position for this text object.
 * @param {number} y the y position
 */
DvtSvgText.prototype.setY = function(y)
{
  if (y !== this._y) {
    this._y  = y ;
    DvtSvgUtils.setAttrNullNS(this.getElem(), 'y', y);
  }
};

/**
 * Sets the font size.
 * @param {number} fontSize the font size
 */
DvtSvgText.prototype.setFontSize = function(fontSize)
{
  DvtSvgUtils.setAttrNullNS(this.getElem(), 'font-size', fontSize);

  //Bug 13879007 - NODE RENDERED INCORRECTLY IN IE9
  this._setMatrix(this.getMatrix(), fontSize);
};

/**
 * Aligns the left side of the text to the x coordinate. Note: This does not always correspond to what the browser
 * considers a "start" alignment, as we work around issues in rtl locales to provide a consistent API.
 */
DvtSvgText.prototype.alignStart = function() {
  // Bug 17776065: When html dir="rtl", Webkit and FF25+ treat the right side of the text as the start, and the left 
  // side of the text as end.  Our API always treats the left side as start, so we need to adjust based on agent.
  var bAdjust = DvtSvgText.needsTextAnchorAdjustment(this._context);
  DvtSvgUtils.setAttrNullNS(this.getElem(), "text-anchor", bAdjust ? "end" : "start");
}

/**
 * Aligns the center of the text to the x coordinate.
 */
DvtSvgText.prototype.alignCenter = function() {
  DvtSvgUtils.setAttrNullNS(this.getElem(), "text-anchor","middle");
}

/**
 * Aligns the right side of the text to the x coordinate. Note: This does not always correspond to what the browser
 * considers an "end" alignment, as we work around issues in rtl locales to provide a consistent API.
 */
DvtSvgText.prototype.alignEnd = function() {
  // Bug 17776065: When html dir="rtl", Webkit and FF25+ treat the right side of the text as the start, and the left 
  // side of the text as end.  Our API always treats the left side as start, so we need to adjust based on agent.
  var bAdjust = DvtSvgText.needsTextAnchorAdjustment(this._context);
  DvtSvgUtils.setAttrNullNS(this.getElem(), "text-anchor", bAdjust ? "start" : "end");
}

// TODO JSDOC Not sure how to expose these, so this is temporary
DvtSvgText.prototype.alignTop = function() {
  //Bug 13879007 - NODE RENDERED INCORRECTLY IN IE9
  if (DvtAgent.isPlatformIE()){
    //+ font-size
    this.SetBaseline(1);
  }
  else {
    this.SetDominantBaselineAttr("text-before-edge");
  }
};

// TODO JSDOC Not sure how to expose these, so this is temporary
DvtSvgText.prototype.alignMiddle = function() {
  //Bug 13879007 - NODE RENDERED INCORRECTLY IN IE9
  if (DvtAgent.isPlatformIE()){
    //+ 2/5 font-size
    this.SetBaseline(.4);
  }
  else {
    this.SetDominantBaselineAttr("middle");
  }
};

// TODO JSDOC Not sure how to expose these, so this is temporary
DvtSvgText.prototype.alignBottom = function() {
  //Bug 13879007 - NODE RENDERED INCORRECTLY IN IE9
  if (DvtAgent.isPlatformIE()){
    //+ 1/5 font-size
    this.SetBaseline(.2);
  }
  else {
    this.SetDominantBaselineAttr("text-after-edge");
  }
};

// TODO JSDOC Not sure how to expose these, so this is temporary
DvtSvgText.prototype.alignBaseline = function() {
  //Bug 13879007 - NODE RENDERED INCORRECTLY IN IE9
  if (DvtAgent.isPlatformIE()){
    this.SetBaseline(0);
  }
  else {
    this.SetDominantBaselineAttr(null);
  }
};

/**
 * @protected
 * Save the baseline and adjust the matrix for vertical alignment in IE.
 */
DvtSvgText.prototype.SetBaseline = function(baseline) {
  this._baseline = baseline;
  this._setMatrix(this.getMatrix(), null);
}

/**
 * @protected
 * Set the dominant baseline for vertical alignment.
 */
DvtSvgText.prototype.SetDominantBaselineAttr = function(baseline) {
  if (baseline) {
    DvtSvgUtils.setAttrNullNS(this.getElem(), "dominant-baseline", baseline);
  }
  else {
    DvtSvgUtils.removeAttrNullNS(this.getElem(), "dominant-baseline");
  }
}

/**
 * Sets the text string for this text object.
 * @param {string} text the text string
 */
DvtSvgText.prototype.setText = function(text) {
  // Update the text node if it is already created
  var textNode = this.GetTextElem().firstChild;
  if(textNode !== null) {
    textNode.nodeValue = text;
  }
  else { // Otherwise create it
    textNode = document.createTextNode(text);
    DvtSvgUtils.appendChildElem(this.GetTextElem(), textNode);
  }

  if (this._bEditMode && this._editElem) {             // if in edit mode, also change the textarea
    this._editElem.value = text ;
  }
};

/**
 * @return the text string for this text object.
 */
DvtSvgText.prototype.getText = function() {
  if (this._bEditMode && this._editElem) {
    return this._editElem.value;
  }
  
  var textNode = this.GetTextElem().firstChild;
  if(textNode) {
    return textNode.nodeValue;
  }
};

/**
 * Sets the DvtCSSStyle of this object.
 * @param {DvtCSSStyle} style The DvtCSSStyle of this object.
 */
DvtSvgText.prototype.setCSSStyle = function(style)
{
  DvtSvgText.superclass.setCSSStyle.call(this, style);
  var elem = this.getOuterElem();

  if (style) {
    //NOTE: svg does not recognize css "color" attribute, use "fill" instead
    var val = style.color;
    if(val)
      DvtSvgUtils.setAttrNullNS(elem, "fill", val);
  
    val = style["font-family"];
    if (val) {
      DvtSvgUtils.setAttrNullNS(elem, "font-family", val);
    }
    val = style["font-size"];
    if (val) {
      DvtSvgUtils.setAttrNullNS(elem, "font-size", val);

      //Bug 13879007 - NODE RENDERED INCORRECTLY IN IE9
      this._setMatrix(this.getMatrix(), val);
    }
    val = style["font-style"];
    if (val) {
      DvtSvgUtils.setAttrNullNS(elem, "font-style", val);
    }
    val = style["font-weight"];
    if (val) {
      DvtSvgUtils.setAttrNullNS(elem, "font-weight", val);
    }

    //NOTE: svg does not recognize css "text-align" attribute, 
    //call alignCenter, alignStart, alignEnd... if needed.
    //For multi line text, text-align is handled in DvtSvgTextArea

    val = style["text-decoration"];
    if (val) {
      DvtSvgUtils.setAttrNullNS(elem, "text-decoration", val);
    }

    val = style["cursor"];
    if (val) {
      DvtSvgUtils.setAttrNullNS(elem, "cursor", val);
    }

    color = style.color;
  }
};

/**
 * Get the font styles.
 * @return {DvtCSSStyle} the font styles
 */
DvtSvgText.prototype.getFontStyles = function() {
  var cssStyle = new DvtCSSStyle();
  var fontFamily = DvtSvgUtils.getAttrNullNS(this.getElem(), DvtCSSStyle.FONT_FAMILY);
  if (!fontFamily) {
    fontFamily = "tahoma, sans-serif"; //this is default from DvtSvgText._Init()
  }
  cssStyle.setStyle(DvtCSSStyle.FONT_FAMILY, fontFamily);
  var fontSize = DvtSvgUtils.getAttrNullNS(this.getElem(), DvtCSSStyle.FONT_SIZE);
  if (!fontSize) {
    fontSize = "medium"; //this is default according to SVG 1.1 spec
  }
  cssStyle.setStyle(DvtCSSStyle.FONT_SIZE, fontSize);
  var fontStyle = DvtSvgUtils.getAttrNullNS(this.getElem(), DvtCSSStyle.FONT_STYLE);
  if (!fontStyle) {
    fontStyle = "normal"; //this is default according to SVG 1.1 spec
  }
  cssStyle.setStyle(DvtCSSStyle.FONT_STYLE, fontStyle);
  var fontWeight = DvtSvgUtils.getAttrNullNS(this.getElem(), DvtCSSStyle.FONT_WEIGHT);
  if (!fontWeight) {
    fontWeight = "normal"; //this is default according to SVG 1.1 spec
  }
  cssStyle.setStyle(DvtCSSStyle.FONT_WEIGHT, fontWeight);
  return cssStyle;
};


/**
 * Returns true if this text object is in editMode, else false.
 * @type boolean
 */
DvtSvgText.prototype.getEditMode = function()
{
    return this._bEditMode ;
};


/**
 * Sets the edit mode of this text object.
 * @param bEditMode {boolean} true if the text is to be editable, else false.
 *                            The mode may be changed at any time.
 * @param {function} editStartCallback  optional function to call when editing starts that takes the string as
 *        an argument and can optionally return a replacement string
 * @param {function} editEndCallback  optional function to call when editing ends that takes the string as
 *        an argument and can optionally return a replacement string
 */
DvtSvgText.prototype.setEditMode = function(bEditMode, editStartCallback, editEndCallback) {
  this._editStartCallback = editStartCallback;
  this._editEndCallback = editEndCallback;
  if (this._bEditMode == bEditMode) {
    return;
  }
  //FIX BUG 16901294: in edit mode, make this text a shape container and add a rect behind the text
  //to define the size of the edit box
  if (bEditMode) {
    var obj = this.getObj();
    var dims = obj.getDimensions();
    
    this.CreateChildGroupElem();
    this._editRectElem = DvtSvgShapeUtils.createElement("rect") ;
    DvtSvgUtils.setAttrNullNS(this._editRectElem, "x", 0);
    DvtSvgUtils.setAttrNullNS(this._editRectElem, "y", 0);
    DvtSvgUtils.setAttrNullNS(this._editRectElem, "width", this._editPxWidth);
    DvtSvgUtils.setAttrNullNS(this._editRectElem, "height", dims.h);
    DvtSvgUtils.setAttrNullNS(this._editRectElem, "fill", "#ffffff");
    DvtSvgUtils.setAttrNullNS(this._editRectElem, "fill-opacity", 0);
    DvtSvgUtils.setAttrNullNS(this._editRectElem, "cursor", "text");
    DvtSvgUtils.appendChildElem(this._childGroupElem, this._editRectElem);
  }
  else {
    this._childGroupElem.removeChild(this._editRectElem);
  }
  this._bEditMode = bEditMode;
};

//FIX BUG 16901294: 
/**
 * @protected
 * Set whether editable text is currently being edited.
 * @param {boolean}  bEditing  true if editing, false otherwise
 */
DvtSvgText.prototype.SetEditing = function(bEditing) {
  if (!this._bEditMode || bEditing == this._bEditing) {
    return;
  }
  var obj        =  this.getObj() ;
  var textElem   =  this.GetTextElem();
  var input ;                                   // textArea elem

  if ((!bEditing) && this._editElem)
  {                                             // change from edit to non-edit mode
     var editedText = this._editElem.value;
    //(NOTE: set the text on the thisDvtText before calling the _editEndCallback, because the
    //callback might change the text on this DvtText, and that should take precedence)
     this.setTextString(editedText) ;

     //call the callback and potentially get replacement text
     if (this._editEndCallback) {
       var retText = this._editEndCallback.call(null, editedText);
       if (retText) {
         this.setTextString(retText);
       }
     }
     
     //put the SVG text behind the _editRectElem
     this.getElem().insertBefore(textElem, this._editRectElem) ;

     if (this._bEventListener) {
       input = this._editElem ;
       //remove the listeners on the HTML text area
       if (this._htmlTextAreaListenerFunc) {
         DvtSvgUtils.removeDomEventListener(input, DvtKeyboardEvent.KEYDOWN,   this._htmlTextAreaListenerFunc, false);
         DvtSvgUtils.removeDomEventListener(input, DvtKeyboardEvent.KEYUP,     this._htmlTextAreaListenerFunc, false);
         DvtSvgUtils.removeDomEventListener(input, "keypress",  this._htmlTextAreaListenerFunc, false);
         DvtSvgUtils.removeDomEventListener(input, "textInput", this._htmlTextAreaListenerFunc, false);
         DvtSvgUtils.removeDomEventListener(input, "textinput", this._htmlTextAreaListenerFunc, false);
         this._htmlTextAreaListenerFunc = null;
       }
       //remove the blur listener on the HTML text area
       if (this._editBlurFunc) {
         DvtSvgUtils.removeDomEventListener(input, "blur", this._editBlurFunc, false);
         this._editBlurFunc = null;
       }
       this._bEventListener = false ;
     }
     //remove the HTML text area from its parent div AFTER removing listeners so that blur listener
     //does not get called due to removal from DOM
     if (this._editElem && this._editElem.parentNode) {
       this._editElem.parentNode.removeChild(this._editElem);
     }
     //This is commented out for now because when it is uncommented and a new HTML text area is created every time, 
     //if you enter some text and then click outside and then click back inside the text, the cursor appears at
     //the beginning of the text.  With this commented out, the cursor appears wherever it last was, which seems 
     //preferable.
     //this._editElem = null;
  }
  else                                       // change to editMode
  {
    var editText = this.getText();
    //create the HTML text area
    //(NOTE: set the text on the HTML text area before calling the _editStartCallback, because the
    //callback might change the text on this DvtText, and that should take precedence)
    if (! this._editElem) {
      input = this._createHtmlTextArea(obj, textElem, editText);
    }

    //call the callback and potentially get replacement text
    if (this._editStartCallback) {
      var retText = this._editStartCallback.call(null, editText);
      if (retText) {
        this.setTextString(retText);
      }
    }
    
    //remove the SVG text from the DOM
    this.getElem().removeChild(textElem) ;
    //FIX BUG 16901294: add the HTML text area to the SVG parent div so that it can be overlaid on top of SVG content
    if (this._editElem) {
      DvtSvgUtils.appendChildElem(obj.getCtx().getStage().getImpl().getSVGRoot().parentNode, this._editElem);
    }

    input = this._editElem ;
    var thisRef = this;
    //create a closure to redirect to the instance function so we have a valid "this" pointer in the listener
    this._htmlTextAreaListenerFunc = function(event) {
      thisRef._handleHtmlTextAreaKeyboardEvent(event);
    };
    //add listeners on the HTML text area
    DvtSvgUtils.addDomEventListener(input, DvtKeyboardEvent.KEYDOWN,   this._htmlTextAreaListenerFunc, false);
    DvtSvgUtils.addDomEventListener(input, DvtKeyboardEvent.KEYUP,     this._htmlTextAreaListenerFunc, false);
    DvtSvgUtils.addDomEventListener(input, "keypress",  this._htmlTextAreaListenerFunc, false);   // legacy event handler, works everywhere
    DvtSvgUtils.addDomEventListener(input, "textInput", this._htmlTextAreaListenerFunc, false);   // Safari and Chrome
    DvtSvgUtils.addDomEventListener(input, "textinput", this._htmlTextAreaListenerFunc, false);   // DOM Level 3 Events draft
    //create a closure to redirect to the instance function so we have a valid "this" pointer in the listener
    this._editBlurFunc = function(event) {
      thisRef._blurEditText();
    };
    //listen for blur event to know when editing has stopped
    DvtSvgUtils.addDomEventListener(input, "blur", this._editBlurFunc, false);
    this._bEventListener = true ;
  }
  //save value at end because other function calls above may depend on old value
  this._bEditing = bEditing;
};


/**
  *   Returns the horizontal pixel width of the edit field set by {@link DvtSvgText#setEditWidth}.
  *    @type {number} the horizontal pixel width of the edit field.
  */
DvtSvgText.prototype.getEditWidth = function(pxWidth)
{
  return this._editPxWidth ;
};


/**
  *    Defines the horizontal pixel width to contain the edit field.  This should be
  *    set before applying setEditMode(true).  If not specified,a size based on the
  *    number of characters in the DvtText is used.
  *    @param pxWidth {number} the horizontal pixel width to contain the edit field.
  */
DvtSvgText.prototype.setEditWidth = function(pxWidth)
{
  this._editPxWidth = pxWidth ;
};

DvtSvgText.prototype.setEditTooltip = function(sTip)
{
  this._editTooltip = sTip ;
};


/*
DvtSvgText.prototype.getEditBorder = function(bShow)
{
  return this._editBorder ;
};


DvtSvgText.prototype.setEditBorder = function(bShow)
{
  this._editBorder = bShow ;
  if (this._editElem)
    this._editElem.setAttribute("style", "border: " + bShow? ";" : "none;") ;
    this._editElem.setAttribute("style", "border-width: " + bShow? "1;" : "none;") ;
};
*/

/**
 * @private
 * Handle keyboard event on the HTML text area used for editing.
 * @param {KeyboardEvent} event Native keyboard event
 */
DvtSvgText.prototype._handleHtmlTextAreaKeyboardEvent = function(event) {

  //filter the event
  //TO DO: this filtering may need to happen on the native event, which is handled in DvtHtmlKeyboardListenerUtils...
  DvtSvgText._editFilter(event);
  //FIX BUG 16901294: the HTML text area will be flagged so that DvtHtmlKeyboardListenerUtils ignores its native events,
  //and we want to dispatch events from the DvtText
  this.getObj().dispatchDisplayableEvent(event.type, "" + event.keyCode);
};

DvtSvgText._editFilter = function(event)
{
   var  e    = event || window.event ;

   if (e.type == "textinput" || e.type == "textInput")
   {   //  for future filtering use
   }
   else if (e.type == "keypress")
   {
      var  code = e.charCode || e.keyCode ;
      if ( code < 32)                           // ascii ctl char?
      {
         if (code == 13) {                       // filter out CR
           if (e.preventDefault)
             e.preventDefault() ;
         }
      }
   }
};

DvtSvgText.prototype.selectEditText = function() {
  if (this._editElem) {
    this._editElem.select();
  }
};

DvtSvgText.prototype.focusEditText = function() {
  //FIX BUG 16901294: actively editing when text field receives focus
  this.SetEditing(true);
  if (this._editElem) {
    //set focus on HTML text area on timeout so that it will take precedence over any other elements that
    //may want to receive focus
    var thisRef = this;
    var func = function() {thisRef._editElem.focus();};
    window.setTimeout(func, 0);
  }
};

/**
 * @private
 * Remove focus from the HTML text area and stop editing.
 */
DvtSvgText.prototype._blurEditText = function() {
  this.SetEditing(false);
};

/**
 * @private
 * Remove the default font attributes that are set in the constructor
 * Used the font attributes in the DvtCSSStyle instead
 * @param {DvtCssStyle} style The DvtCSSStyle of this object.
 */
DvtSvgText.prototype._removeDefaultFontAttributes = function(elem)
{
  DvtSvgUtils.removeAttrNullNS(elem,"font-family");
  DvtSvgUtils.removeAttrNullNS(elem,"fill");
};


DvtSvgText.prototype._createTSpan = function(text, parentElem) {
  var tspan  = DvtSvgShapeUtils.createElement('tspan', undefined);
  if (parentElem) {
    DvtSvgUtils.appendChildElem(parentElem, tspan);
  }
  else {
    DvtSvgUtils.appendChildElem(this.getElem(), tspan);
  }

  var baseline = DvtSvgUtils.getAttrNullNS(this.getElem(), 'dominant-baseline');
  if (baseline)
      DvtSvgUtils.setAttrNullNS(tspan, "dominant-baseline",baseline);
  var anchor = DvtSvgUtils.getAttrNullNS(this.getElem(), 'text-anchor');
  if (anchor)
      DvtSvgUtils.setAttrNullNS(tspan, "text-anchor",anchor);
  
  // add text node
  if (text) {
    this._addTextNode(tspan, text);
  }
  return tspan;
};


DvtSvgText.prototype._createTextNode = function(text) {
  this._addTextNode(this.getElem(), text);
};

DvtSvgText.prototype._addTextNode = function(elem, text) {
  var textNode = document.createTextNode(text);
  if (elem) {
    DvtSvgUtils.appendChildElem(elem, textNode);
  }
  else {
    DvtSvgUtils.appendChildElem(this.getElem(), textNode);
  }
};

//Bug 13879007 - NODE RENDERED INCORRECTLY IN IE9
DvtSvgText.prototype._adjustBaseline = function(fontSize) {

  //if not in IE, no adjustment required
  var dy = 0;
  if (this._baseline != null) {
    //default fontSize to 11px
    if (! fontSize) {
      fontSize = DvtSvgUtils.getAttrNullNS(this.getElem(), 'font-size');
      if (! fontSize) {
        fontSize = DvtSvgUtils.getAttrNullNS(this.getOuterElem(), 'font-size');
        if (! fontSize)
          fontSize = "11";
      }
    }
    dy = this._baseline * parseFloat(fontSize);
    //FIX BUG 16856961: additional pixel offset for baseline, currently only
    //specified by DvtSvgTextArea subclass
    if (this._baselineOffset != null)
      dy += this._baselineOffset;
  }
  return dy;
};

DvtSvgText.prototype._setMatrix = function(mat, fontSize) {
  if (this._baseline != null && this.getObj()) {
    if (! mat) {
      mat = new DvtMatrix();
    }
    var nmat = new DvtMatrix(null, null, null, null, null, this._adjustBaseline(fontSize));
    nmat.concat(mat);
    mat = nmat;
  }
  DvtSvgText.superclass.setMatrix.call(this, mat);
}

DvtSvgText.prototype.getMatrix = function() {
  return this.getObj() ? this.getObj().getMatrix() : null;
}


/**
  *  @override
  */
DvtSvgText.prototype.setMatrix = function(mat) {
  this._setMatrix(mat, null);
}

/**
  *  @override
  */
DvtSvgText.prototype.getDimensions = function() {
  var bbox = DvtSvgText.superclass.getDimensions.call(this);
  
  //Bug 13879007 - NODE RENDERED INCORRECTLY IN IE9
  if (bbox)
    bbox.y += this._adjustBaseline(null);

  return bbox;
};

/**
 * @private
 */
DvtSvgText.prototype._createHtmlTextArea = function(obj, textElem, editText) {
   var input  = document.createElement("textArea") ;
   this._editElem = input ;
   this._editElem._obj = this;

   var bbox   = obj.getDimensions() ;
   var txt    = editText ;
   var css    = obj.getCSSStyle() ;
   var cssSize ;
   var cssFamily ;
   var cols   = 0 ;       // textArea cols

   if (! css) {
     cssSize   = DvtSvgUtils.getAttrNullNS(textElem, "font-size") || "11" ;
     cssFamily = DvtSvgUtils.getAttrNullNS(textElem, "font-family")
     css       = "font-family:" + cssFamily + ";" + " font-size: " + cssSize + "px;" ;
   }

   if (this._editPxWidth) {                            // pixel width is expected!
      // Figure out text char width to get approximate cols
      var t = new DvtText(obj.getCtx(), "W");
      t.setCSSStyle(css);
      t.alignMiddle();
      obj.getParent().addChild(t);
      var dims = t.getDimensions();
      obj.getParent().removeChild(t);
      cols = (this._editPxWidth/dims.w) ;
   }
   else {                                              // no pixel width!
      if (bbox.w == 0) {                               // just use some default value so
        bbox.w = 10 ;                                  // that its at least visible.
      }
      this._editPxWidth = bbox.w * 2 ;
      cols = 2 ;
   }

   cols = Math.floor(cols * 1.5) ;                       // heuristic for cols
   //console.log("cols=" + cols) ;
   
   input.setAttribute("rows", "1") ;
   input.setAttribute("cols",  cols) ;           // cols determines edit box width
   if (txt) {
     input.value = txt ;
   }
   else {
     input.value = "";
   }

   if (this._editTooltip) {
     input.setAttribute("title", this._editTooltip) ;
   }

   css += " color:" + DvtSvgUtils.getAttrNullNS(textElem, "fill") + ";" ;
   css += " overflow-x: hidden;" ;      
   css += " border: none; border-width: 0px; margin: 0px; resize: none; outline: none;" ; 
   css += " dominant-baseline:" + DvtSvgUtils.getAttrNullNS(textElem, "dominant-baseline") + ";" ;

// css += " line-height: 1;" ; 
   
   //FIX BUG 16901294: absolutely position the HTML text area over the DvtText
   var localPoint = new DvtPoint(this.getX(), this.getY());
   var stagePoint = this.getObj().localToStage(localPoint);
   css += " position: absolute; top: " + stagePoint.y + "px; left: " + stagePoint.x + "px;" ;
   
   css += " vertical-align: baseline; outline-offset: 0px;" ; 
   var padding = 2;
   if (DvtAgent.isRightToLeft(this.getObj().getCtx())) {
     css += " text-align: right; padding: 0px " + padding + "px 0px 0px;" ;
   }
   else {
     css += " text-align: left; padding: 0px 0px 0px " + padding + "px;" ;
   }
   
   input.setAttribute("style", css) ;

   if (DvtAgent.isBrowserChrome() || DvtAgent.isBrowserSafari()) {
     input.setAttribute("wrap", "off") ;
   }
   
   //FIX BUG 16901294: mark the HTML text area so that DvtHtmlKeyboardListenerUtils will ignore its native events,
   //because the div that DvtHtmlKeyboardListenerUtils listens on will also get the text area events
   input[DvtHtmlKeyboardListenerUtils.ATTR_IGNORE_EVENTS_FROM_TARGET] = true;

   return input ;
} ;

DvtSvgText.prototype.AddElemListener = function(type, listener, useCapture) {
  if (this.getEditMode() && this._editElem) {
    DvtSvgUtils.addDomEventListener(this._editElem, type, listener, useCapture);
    if (type == DvtTouchEvent.TOUCHEND) {
        DvtSvgUtils.addDomEventListener(this._editElem, DvtTouchEvent.TOUCHCANCEL, listener, useCapture);
    }
  }
  
  DvtSvgText.superclass.AddElemListener.call(this, type, listener, useCapture);
};

//FIX BUG 16901294: when editing, getElem() will not return the text element, so provide a function
//that will always do that
/**
 * @protected
 * Get the text DOM element.
 */
DvtSvgText.prototype.GetTextElem = function() {
  return this._elem;
};

/**
 * Removes existing textarea from the DOM including listeners attached to it
 */
DvtSvgText.prototype.destroy = function() {
  if (this._editElem) {
    this.SetEditing(false);  
    this._editElem = null;
  }
};

/**
 * Returns true if the text anchor needs to be flipped from "start" to "end" and vice versa.
 * @param {DvtContext} context
 * @return {boolean}
 */
DvtSvgText.needsTextAnchorAdjustment = function(context) {
  // Bug 17776065: When html dir="rtl", Webkit and FF25+ treat the right side of the text as the start, and the left 
  // side of the text as end.  Our API always treats the left side as start, so we need to adjust based on agent.
  // Note: Technically this context check is not correct, because the text anchor adjustment is not based on the reading
  // direction of our context, but rather the rtl flag on the page.  We are passing context incorrectly here so that our
  // unit tests can be used to verify rtl support for JET components, and we should remove this context param once all
  // components have been updated to support rtl in JET (by passing context within their RTL calls).  We'd do this by
  // setting documentElement.document.dir to rtl in the test environment, which mimics the situation where the adjustment
  // is needed in the browser.
  return DvtAgent.isRightToLeft(context) &&  
         (DvtAgent.isPlatformWebkit() || (DvtAgent.isPlatformGecko() && DvtAgent.getVersion() >= 25));
}
// Copyright (c) 2011, 2023, Oracle and/or its affiliates.
/*-------------------------------------------------------------------------*/
/*   DvtSvgTextArea        SVG implementation of textArea "shape"          */
/*-------------------------------------------------------------------------*/
/**
 * @constructor
 * Creates an instance of DvtSvgTextArea.
 * @param {DvtContext} context
 * @extends DvtSvgShape
 * @class DvtSvgTextArea
 * @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
 */
var DvtSvgTextArea = function(context, x, y, id) {
  this._Init(context, x, y, id) ;
};

DvtObj.createSubclass(DvtSvgTextArea, DvtSvgText, "DvtSvgTextArea") ;


/**
 * Initializes this instance of DvtTextArea.
 * @param {DvtContext} context
 * @param text {string} the text string
 * @param x {number} x the x position
 * @param y {number} y the y position
 * @param id {string} the id for this object
 */
DvtSvgTextArea.prototype._Init = function(context, x, y, id) {
  //remove this if graph is ok with the text alignment changes
  if (x === undefined && y === undefined) {
    x = 0;
    y = 0;
    this._notGraph = true;
  }

  DvtSvgTextArea.superclass._Init.call(this, context, "", x, y, id) ;  
  
  // the adjustment to the amount of space between lines
  this._leading = 0;
}


/**
 * Sets the text string for this textArea object.
 * @param {array} single line text or multiline text
 * @param {array} dimList Optional dimensions of multilines
 */
DvtSvgTextArea.prototype.setText = function(multilines, dimList) {
  var singleLine;
  if (DvtArrayUtils.isArray(multilines)) {
    if (multilines.length == 1)
      singleLine = multilines[0];
  }
  else {
    singleLine = multilines;
  }

  // clear the old text
  var obj = this.getObj();
  var elem = this.getElem();

  // keep the old dominant-baseline
  var dbl = this._getCurrentDominantBaseline();

  var child = elem.firstChild;
  while(child != null) {
    var delNode = child;
    child = child.nextSibling;
    elem.removeChild(delNode);
  }
  
  //FIX BUG 16856961: save multiline dimensions for vertical alignment
  this._multilineDims = null;

  if (obj.getNumChildren() === 0 && singleLine) {
    // Update the text node if it is already created
    var textNode = this.getElem().firstChild;
    if(textNode) {
      textNode.nodeValue = singleLine;
    }
    // Otherwise create it
    else {
      this._addTextLine(obj, singleLine);
    }
  }
  else {
    var oldx = parseFloat(DvtSvgUtils.getAttrNullNS(elem, 'x'));
    var oldy = parseFloat(DvtSvgUtils.getAttrNullNS(elem, 'y'));
    if (isNaN(oldx)) {
      // FIX Bug 16588881 - js chart: pie labels may overflow when the pie is small
      // Problem with multiline labels resolved by following check
      oldx = 0;
    }
    if (isNaN(oldy)) {
      oldy = 0;
    }
    if (singleLine) {
      this._addLine(obj, singleLine, oldx, oldy);
    }
    else {
      var x = 0;
      //line position needs to start at specified value
      var y = oldy;
      var dim;
      var maxDim = new DvtRectangle(oldx, oldy, obj._getMaxTextWidth(), obj._getMaxTextHeight());

      //FIX BUG 16856961: save the multiline dimensions for vertical alignment
      this._multilineDims = [];
      for (var i = 0; i < multilines.length; i++) {
        dim = (dimList && dimList[i]) ? dimList[i] : maxDim;
        this._multilineDims.push(dim);

        x = oldx;
        if (this._notGraph) 
          x += this._getHorzOffset(dim.w);

        this._addTextLine(obj, multilines[i], x, y, dbl);

        y += dim.h;
      }

      //Bug 13542241 - treemap node content sometimes doesn't respect bold
      var g = this.getOuterElem();
      if (g.nodeName && g.nodeName == "g" && g !== elem) {
        //remove the old element
        g.removeChild(elem);

        //apply css styles on the group element
        var style = this.getCSSStyle();
        if (style) {
          this.setCSSStyle(style);
        }
      }
    }
  }
  
  //FIX BUG 16856961: refresh vertical alignment
  this.RefreshVertAlign();
}


/**
 * Gets the text string for this textArea object.
 * @param {string} text the text string
 */
DvtSvgTextArea.prototype.getText = function() {
  var obj = this.getObj();
  var nkids = obj.getNumChildren();

  if (nkids == 0) {
    return DvtSvgTextArea.superclass.getText.call(this);
  }
  else {
    var child;
    var text = "";
    for (var i = 0; i < nkids; i++) {
      child = obj.getChildAt(i);
      text += child.getText();
    }
  }
  return text;
}


/**
 * Returns the amount of vertical space, in pixels, between lines
 * @return {number} the amount of vertical space (called leading) between lines
 */
DvtSvgTextArea.prototype.getLeading = function() {
  return this._leading;
};

/**
 * Sets the amount of vertical space, in pixels, between lines
 * @param {number} leading amount of vertical space, in pixels, between lines
 */
DvtSvgTextArea.prototype.setLeading = function(leading) {
  this._leading = leading;
};

// add a text line
DvtSvgTextArea.prototype._addLine = function(obj, newLineText, x, dy) {
  var newText = this._createTSpan(newLineText);
  DvtSvgUtils.setAttrNullNS(newText, 'x', x);
  DvtSvgUtils.setAttrNullNS(newText, 'dy', dy + this.getLeading());

  // peformance: dont getDimensions for each line
//   return this.getDimensions().h;
}

// add a text line
DvtSvgTextArea.prototype._addTextLine = function(obj, newLineText, x, y, dbl) {
  var newDvtText = new DvtText(obj.getCtx(), newLineText, x, y, null);
  newDvtText.setParent(obj);
  
  var newText = newDvtText.getImpl();
  var textNode = this.getElem().firstChild;
  
  if (textNode == null){
      textNode = this.getElem();
  }
  var attr = DvtSvgUtils.getAttrNullNS(textNode, 'dominant-baseline');
  if (attr) {
      DvtSvgUtils.setAttrNullNS(newText.getElem(), "dominant-baseline",attr);
  }
  else if (dbl) {
      DvtSvgUtils.setAttrNullNS(newText.getElem(), "dominant-baseline",dbl);
  }

  attr = DvtSvgUtils.getAttrNullNS(textNode, 'text-anchor');
  if (attr)
      DvtSvgUtils.setAttrNullNS(newText.getElem(), "text-anchor",attr);
      
  attr = DvtSvgUtils.getAttrNullNS(textNode, 'font-family');
  if (attr)
      DvtSvgUtils.setAttrNullNS(newText.getElem(), "font-family",attr);
      
  attr = DvtSvgUtils.getAttrNullNS(textNode, 'font-size');
  if (attr)
      DvtSvgUtils.setAttrNullNS(newText.getElem(), "font-size",attr);
      
  attr = DvtSvgUtils.getAttrNullNS(textNode, 'fill');
  if (attr)
      DvtSvgUtils.setAttrNullNS(newText.getElem(), "fill",attr);
      
  attr = DvtSvgUtils.getAttrNullNS(textNode, 'cursor');
  if (attr)
      DvtSvgUtils.setAttrNullNS(newText.getElem(), "cursor",attr);

  // Note: This code doesn't get called in anything except FF v24, and it's unclear whether it's useful or not.  Once FF24 is removed,
  // we should just get rid of it.  Fake usage to help search:
  // DvtAgent.isPlatformGecko() && DvtAgent.getVersion() < 25;
  if (DvtAgent.isRightToLeft(obj.getCtx())) {
    attr = DvtSvgUtils.getAttrNullNS(textNode, 'unicode-bidi');
    if (attr)
      DvtSvgUtils.setAttrNullNS(newText.getElem(), "unicode-bidi", attr);
  }
  
  //FIX BUG 16856961: remove vertical alignment transform on child text so it
  //doesn't interfere with vert align transform on this text area
  if (DvtAgent.isPlatformIE()){
    DvtSvgUtils.setAttrNullNS(newText.getElem(), "transform", null);
    DvtSvgUtils.removeAttrNullNS(newText.getElem(), "transform");
  }
  
  this.addChild(newText);
}


DvtSvgTextArea.prototype._getTextAlign = function() {
  var style = this.getCSSStyle();
  var textAlign = style["text-align"];
  switch (textAlign) {
  case "start":
  case "end":
  case "center":
    break;

  case "right":
    textAlign = "end";
    break;

  case "left":
    textAlign = "start";
    break;

  default:
    textAlign = DvtAgent.isRightToLeft(this.getObj().getCtx())? "end" : "start";
    break;
  }

  return textAlign;
}


/**
 * @override
 */
DvtSvgTextArea.prototype.setCursor = function (cursorType) {
  DvtSvgTextArea.superclass.setCursor.call(this, cursorType);

  var elem = this.getElem();
  if (elem != null) {
    for (var i = 0;i < elem.childElementCount;i++) {
      var child = elem.childNodes[i];
      DvtSvgUtils.setAttrNullNS(child, "cursor", cursorType);
    }
  }
}


DvtSvgTextArea.prototype._getHorzOffset = function(ww) {
  var offset = 0;
  var obj = this.getObj();
  var textAlign = this._getTextAlign();
  
  if (textAlign == "end") {
    offset = obj._getMaxTextWidth() - ww;
  }
  else if (textAlign == "center") {
    offset = (obj._getMaxTextWidth() - ww) / 2;
  }
  
  // Bug 17776065: When html dir="rtl", Webkit and FF25+ treat the right side of the start as the start, and the left 
  // side of the text as end.  Our API always treats the left side as start, so we need to adjust based on agent.
  var bAdjust = DvtSvgText.needsTextAnchorAdjustment(obj.getCtx());
  if (bAdjust) {
    offset += ww;
  }

  return offset;
}

//FIX BUG 16856961:
/**
 * @protected
 * Refresh vertical alignment when text changes.
 */
DvtSvgTextArea.prototype.RefreshVertAlign = function() {
  //Bug 13879007 - NODE RENDERED INCORRECTLY IN IE9
  if (DvtAgent.isPlatformIE()){
    this.SetBaseline(this._baseline);
  }
  else {
    this._adjustMultiBaselines();
  }
}

//FIX BUG 16856961:
/**
 * @override
 */
DvtSvgTextArea.prototype.SetBaseline = function(baseline) {
  //if there are multiple lines, calculate an additional pixel baseline offset
  this._baselineOffset = null;
  if (this._multilineDims) {
    var accumHeight = 0;
    //the first line is already shifted correctly for a single line, so only
    //need to take additional lines into account to determine extra shift
    for (var j = 1; j < this._multilineDims.length; j++) {
      accumHeight += this._multilineDims[j].h;
    }
    switch (this.getObj().getVertAlignment()) {
      case DvtText.V_ALIGN_MIDDLE:
        this._baselineOffset = -.5 * accumHeight;
        break;
      case DvtText.V_ALIGN_BOTTOM:
      case DvtText.V_ALIGN_BASELINE:
        this._baselineOffset = -accumHeight;
        break;
    }
  }
  DvtSvgTextArea.superclass.SetBaseline.call(this, baseline);
}

/**
 * @override
 * Set the dominant baseline for vertical alignment.
 */
DvtSvgTextArea.prototype.SetDominantBaselineAttr = function(baseline) {
  DvtSvgTextArea.superclass.SetDominantBaselineAttr.call(this, baseline);
  
  //FIX BUG 16856961: adjust baselines for vertical alignment
  this._adjustMultiBaselines();
}

/**
 * @private
 * Get the current value of dominant-baseline.
 */
DvtSvgTextArea.prototype._getCurrentDominantBaseline = function() {
  var elem = this.getElem();
  var dbl;
  if (elem && elem.nodeName == "g") {
    var childNodes = elem.childNodes;
    if (childNodes && childNodes.length > 0 && childNodes[0].nodeName == "text") {
      dbl = DvtSvgUtils.getAttrNullNS(childNodes[0], 'dominant-baseline');
    }
  }
  return dbl;
}

//FIX BUG 16856961: 
/**
 * @private
 * Adjust baselines for vertical alignment of multi-line text.
 */
DvtSvgTextArea.prototype._adjustMultiBaselines = function() {
  var elem = this.getElem();
  if (elem && this._multilineDims) {
    var valign = this.getObj().getVertAlignment();
    var baselineShiftAttr = "baseline-shift";
    var dominantBaselineAttr = "dominant-baseline";
    var dominantBaselineValue = this._getCurrentDominantBaseline();
    var px = "px";
    var numChildren = elem.childElementCount;
    var accumHeight = 0;
    
    //the first line is already shifted correctly for a single line, so only
    //need to take additional lines into account to determine extra shift
    for (var j = 1; j < this._multilineDims.length; j++) {
      accumHeight += this._multilineDims[j].h;
    }
    
    for (var i = 0; i < numChildren; i++) {
      var child = elem.childNodes[i];
      
      //copy the dominant-baseline from first line to other lines
      if (dominantBaselineValue) {
        DvtSvgUtils.setAttrNullNS(child, dominantBaselineAttr, dominantBaselineValue);
      }
      else {
        DvtSvgUtils.removeAttrNullNS(child, dominantBaselineAttr);
      }
      
      //shift the baseline
      switch (valign) {
        case DvtText.V_ALIGN_TOP:
          DvtSvgUtils.removeAttrNullNS(child, baselineShiftAttr);
          break;
        case DvtText.V_ALIGN_MIDDLE:
          DvtSvgUtils.setAttrNullNS(child, baselineShiftAttr, (.5 * accumHeight) + px);
          break;
        case DvtText.V_ALIGN_BOTTOM:
        case DvtText.V_ALIGN_BASELINE:
          DvtSvgUtils.setAttrNullNS(child, baselineShiftAttr, accumHeight + px);
          break;
      }
    }
  }
}
// Copyright (c) 2011, 2023, Oracle and/or its affiliates.

/**
 * Creates an instance of DvtSvgTextFormatted.
 * @param {DvtContext} context
 * @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
 * @constructor
 * @extends DvtSvgShape
 * @class DvtSvgTextFormatted
 */
var DvtSvgTextFormatted = function(context, x, y, id) {
  this._Init(context, x, y, id) ;
};

DvtObj.createSubclass(DvtSvgTextFormatted, DvtSvgText, "DvtSvgTextFormatted") ;


DvtSvgTextFormatted.BOLD = "b";
DvtSvgTextFormatted.BREAK = "br";
DvtSvgTextFormatted.ITALIC = "i";
DvtSvgTextFormatted.LIST_ITEM = "li";
DvtSvgTextFormatted.ORDERED_LIST = "ol";
DvtSvgTextFormatted.PARAGRAPH = "p";
DvtSvgTextFormatted.UNORDERED_LIST = "ul";


/**
 * Initializes this instance of DvtTextFormatted.
 * @param {DvtContext} context
 * @param text {string} the text string
 * @param x {number} x the x position
 * @param y {number} y the y position
 * @param id {string} the id for this object
 */
DvtSvgTextFormatted.prototype._Init = function(context, x, y, id) {
  DvtSvgTextFormatted.superclass._Init.call(this, context, "", x, y, id) ;
};


/**
 * Sets the text string for this textArea object.
 * @param {array} single line text or multiline text
 */
DvtSvgTextFormatted.prototype.setText = function(text) {
  // parse the formatted text
  var obj = this.getObj();
  var parser = new DvtXmlParser(obj.getCtx());

  // wrap around the text with a tag to make the dom parser happy
  var newText = "<root>" + text + "</root>";
  var node = parser.parse(newText);

  if (node) {
    //disable vertical indentation until something is rendered, so
    //that we don't render empty lines at the top
    this._bEnableVertIndent = false;
    this._parseChildren(node, null, 0, false, false);
  }

};

DvtSvgTextFormatted.prototype._parseChildren = function(node, currTspan, nestedListLevel, bFirstListItem, bListItem) {
  var childNodes = node.getAllChildNodes();
  var childCnt = childNodes.length;

  var bPrevP = false;
  for (var i = 0; i < childCnt; i++) {
    var child = childNodes[i];
    if (child) {
      var childName = child.getName();
      
      //if <p>, then need to shift all subsequent siblings
      //by additional amount at end of paragraph
      if (bPrevP && 
          childName !== DvtSvgTextFormatted.PARAGRAPH &&
          childName !== DvtSvgTextFormatted.ORDERED_LIST &&
          childName !== DvtSvgTextFormatted.UNORDERED_LIST) {
        var pTspan = this._handleMarkup(DvtSvgTextFormatted.PARAGRAPH, currTspan);
        currTspan = pTspan;
      }
      bPrevP = false;
      
      // text node
      if (childName === '#text') {
        var strText = child.getNodeValue();
        if (bListItem) {
          strText = "- " + strText;
        }
        this._addTextNode(currTspan, strText);
        //enable vertical indentation now that we've rendered something
        this._bEnableVertIndent = true;
      }
      // element
      else {
        var tspan = this._handleMarkup(childName, currTspan, nestedListLevel, bFirstListItem);
        
        var childNestedListLevel = nestedListLevel;
        var childBFirstListItem = false;
        if (childName === DvtSvgTextFormatted.ORDERED_LIST ||
            childName === DvtSvgTextFormatted.UNORDERED_LIST) {
          if (childNestedListLevel == 0) {
            childBFirstListItem = true;
          }
          childNestedListLevel++;
        }
        
        this._parseChildren(child, tspan, childNestedListLevel, childBFirstListItem, (childName === DvtSvgTextFormatted.LIST_ITEM));
        
        //if <br>, then need to shift all subsequent siblings
        if (childName === DvtSvgTextFormatted.BREAK) {
          currTspan = tspan;
        }
        //if <p>, then need to shift all subsequent siblings
        //by additional amount at end of paragraph
        if (childName === DvtSvgTextFormatted.PARAGRAPH ||
            ((childName === DvtSvgTextFormatted.ORDERED_LIST ||
              childName === DvtSvgTextFormatted.UNORDERED_LIST) &&
             nestedListLevel < 1)) {
          bPrevP = true;
        }
        //if processed the first <li>, then toggle the flag to false
        if (childName === DvtSvgTextFormatted.LIST_ITEM) {
          bFirstListItem = false;
        }
      }
    } //end if (child)
  } //end for (var i = 0; i < childCnt; i++)
};


// handle html markup
DvtSvgTextFormatted.prototype._handleMarkup = function(nodeName, currTspan, nestedListLevel, bFirstListItem) {
  // create tspan
  var tspan  = this._createTSpan(null, currTspan);

  // element
  if (nodeName === DvtSvgTextFormatted.BOLD) {
    DvtSvgUtils.setAttrNullNS(tspan, 'font-weight', 'bold');
  }
  else if (nodeName === DvtSvgTextFormatted.ITALIC) {
    DvtSvgUtils.setAttrNullNS(tspan, 'font-style', 'italic');
  }
  else if (nodeName === DvtSvgTextFormatted.BREAK) {
    DvtSvgUtils.setAttrNullNS(tspan, 'x', '0');
    DvtSvgUtils.setAttrNullNS(tspan, 'dy', '1em');
  }
  else if (nodeName === DvtSvgTextFormatted.PARAGRAPH) {
    if (this._bEnableVertIndent) {
      DvtSvgUtils.setAttrNullNS(tspan, 'x', '0');
      DvtSvgUtils.setAttrNullNS(tspan, 'dy', '2em');
    }
    //enable vertical indentation now that we've rendered something
    this._bEnableVertIndent = true;
  }
  else if (nodeName === DvtSvgTextFormatted.ORDERED_LIST ||
           nodeName === DvtSvgTextFormatted.UNORDERED_LIST) {
  }
  else if (nodeName === DvtSvgTextFormatted.LIST_ITEM) {
    DvtSvgUtils.setAttrNullNS(tspan, 'x', (nestedListLevel) +'em');
    if (this._bEnableVertIndent) {
      if (bFirstListItem) {
        DvtSvgUtils.setAttrNullNS(tspan, 'dy', '2em');
      }
      else {
        DvtSvgUtils.setAttrNullNS(tspan, 'dy', '1em');
      }
    }
    //enable vertical indentation now that we've rendered something
    this._bEnableVertIndent = true;
  }
//     DvtSvgUtils.setAttrNullNS(tspan, 'text-decoration', 'underline');
  
  return tspan;
};


// Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
/**
  *  Creates a selectable shape using SVG path commands.
  *  @extends DvtArc
  *  @class DvtGraphSelectableArc  Creates an arbitrary shape using SVG path commands.
  *  @constructor
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {number} cx  The center x position.
  *  @param {number} cy  The center y position.
  *  @param {number} rx  The horizontal radius of the ellipse.
  *  @param {number} ry  The vertical radius of the ellipse.
  *  @param {number} sa  The starting angle in degrees (following the normal anti-clockwise is positive convention).
  *  @param {number} ae  The angle extent in degrees (following the normal anti-clockwise is positive convention).
  *  @param {String} clos  An optional closure type for the arc. Closures are "OPEN" (the default), "SEGMENT", and "CHORD".
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var  DvtGraphSelectableArc = function(context, cx, cy, rx, ry, sa, ae, clos, id)
{
   this.Init(context, cx, cy, rx, ry, sa, ae, clos, id) ;
};

DvtObj.createSubclass(DvtGraphSelectableArc, DvtArc, "DvtGraphSelectableArc") ;

/**
  *  Object initializer.
  *  @protected
  */
DvtGraphSelectableArc.prototype.Init = function(context, cx, cy, rx, ry, sa, ae, clos, id)
{
  DvtGraphSelectableArc.superclass.Init.call(this, context, cx, cy, rx, ry, sa, ae, clos, id) ;
  //properties related to selection
  this._bSelected = false;
  this._bSelecting = false;
  this._selectionEffects = null;
  this._bSelectedShadow = true;
  this._dataColor = "#000000";
  this._innerColor = null;
  this._outerColor = null;
  
  this._savedStroke = null;
  this._bSavedStroke = false;
  this._relatedLogicalObjects = null;
  this._applyStrokeTimer  = null;
  
  this._bZorder = true;
  this._zOrderManager = null;

  this._selectionType    = DvtSelectionEffectUtils.DEFAULT_SEL_TYPE;
}

/**
 * @protected
 * Apply any selection effects to this object.
 */
DvtGraphSelectableArc.prototype.ApplySelectionState = function()
{
  DvtSelectionEffectUtils.applySelectionState(this, this._dataColor, this._innerColor, this._outerColor);
};

/**
 * @protected
 * Apply any mouse hover selection effects to this object.
 */
DvtGraphSelectableArc.prototype.ApplySelectingState = function()
{
  DvtSelectionEffectUtils.applySelectingState(this, this._dataColor);
};


/**
 * Show the selection hover effect.
 */
DvtGraphSelectableArc.prototype.showHoverEffect = function()
{
  this.SetSelectionMouseOver(true);
};


/**
 * Hide the selection hover effect.
 */
DvtGraphSelectableArc.prototype.hideHoverEffect = function()
{
  this.SetSelectionMouseOver(false);
};


/**
 * Determine if the selection hover effect is shown.
 * 
 * @type boolean
 */
DvtGraphSelectableArc.prototype.isHoverEffectShown = function()
{
  return this._bSelecting;
};


/**
 * @protected
 * Set whether the selection mouse hover effect is shown.
 * 
 * @param bOver {boolean}  bOver  true if the mouse is over the object,
 *        false otherwise
 */
DvtGraphSelectableArc.prototype.SetSelectionMouseOver = function(bOver)
{
  this.SetSelectionMouseOverSelf(bOver);

  //set selection on related parts of this marker also
  if (this._relatedLogicalObjects)
  {
    for (var i = 0; i < this._relatedLogicalObjects.length; i++)
    {
      if (this._relatedLogicalObjects[i].SetSelectionMouseOverSelf)
      {
        this._relatedLogicalObjects[i].SetSelectionMouseOverSelf(bOver);
      }
    }
  }
}

/**
 * @protected
 * Set whether the selection mouse hover effect is shown.
 * 
 * @param bOver {boolean}  bOver  true if the mouse is over the object,
 *        false otherwise
 */
DvtGraphSelectableArc.prototype.SetSelectionMouseOverSelf = function(bOver)
{
  if (this._bSelecting != bOver)
  {
    this._bSelecting = bOver;
    if (this._bZorder)
    {   
      if (this._bSelecting && this._zOrderManager)
      {
        this._zOrderManager.bringToFrontOfSelection(this);
      }
      else if (!this._bSelected)
      {
        this._zOrderManager.pushToBackOfSelection(this);
      }
    }
    this.ApplySelectingState();
  }
};


/**
 * Determine whether this object is selected.
 * 
 * @type boolean
 */
DvtGraphSelectableArc.prototype.isSelected = function()
{
  return this._bSelected;
};

/**
 * Set whether the selection effect is shown.
 * 
 * @param bOver {boolean}  bSel  true if the object is selected,
 *        false otherwise
 */
DvtGraphSelectableArc.prototype.setSelected = function(bSel)
{
  this.SetSelectedSelf(bSel);

  //set selection on related parts of this marker also
  if (this._relatedLogicalObjects)
  {
    for (var i = 0; i < this._relatedLogicalObjects.length; i++)
    {
      if (this._relatedLogicalObjects[i].SetSelectedSelf)
      {
        this._relatedLogicalObjects[i].SetSelectedSelf(bSel);
      }
    }
  }
};

/**
 * @protected
 * Set whether the selection effect is shown.
 * 
 * @param bOver {boolean}  bSel  true if the object is selected,
 *        false otherwise
 */
DvtGraphSelectableArc.prototype.SetSelectedSelf = function(bSel)
{
  if (this._bSelected != bSel)
  {
    this._bSelected = bSel;
    if (this._bZorder)
    {
      if (this._bSelected && this._zOrderManager)
      {
        this._zOrderManager.bringToFrontOfSelection(this);
      }
      else if (!this._bSelecting && this._zOrderManager)
      {
        this._zOrderManager.pushToBackOfSelection(this);
      }
    }
    this.ApplySelectionState();
  }
};

/**
 * Add a related object of this det object.
 *
 * @param {DvtDisplayable} displayable A DvtDisplayable object related to this one
 */
DvtGraphSelectableArc.prototype.addRelatedObject = function(displayable)
{
  if (!this._relatedLogicalObjects)
    this._relatedLogicalObjects = [];

  this._relatedLogicalObjects.push(displayable);
};


/**
 * Set whether a drop shadow is shown when this object is selected.
 *
 * @param {boolean}  bShadow  true if a selection drop shadow should be shown,
 *        false if not
 */
DvtGraphSelectableArc.prototype.setSelectedShadow = function(bShadow)
{
  this._bSelectedShadow = bShadow;
};

/**
 * Specifies the color of the data item and its selection feedback, if different from the default.
 * @param {string} dataColor The CSS color string of the primary color of the data item.
 * @param {string} innerColor The CSS color string of the inner color of the selection effect.
 * @param {string} outerColor The CSS color string of the outer color of the selection effect.
 */
DvtGraphSelectableArc.prototype.setDataColor = function(dataColor, innerColor, outerColor)
{ 
  this._dataColor = dataColor;
  this._innerColor = innerColor;
  this._outerColor = outerColor;
}

/**
 * Sets whether the z order of an object should be adjusted for selection.
 *
 * @param {boolean}  yesNo true if zorder is to change
 */
DvtGraphSelectableArc.prototype.setZorder = function(yesNo)
{
  this._bZorder = yesNo;
};

DvtGraphSelectableArc.prototype.setZOrderManager = function(manager) 
{
  this._zOrderManager = manager;
}

/**
 * @override
 */
DvtGraphSelectableArc.prototype.UpdateSelectionEffect = function() {}
// Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
/** 
  *  A marker object for selectable invisible markers.
  *  @class DvtGraphSelectableLineMarker A marker object for selectable scatter and bubble charts.
  *  @extends DvtMarker
  *  @constructor
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {number} type The type of the marker (see {@link DvtMarker}).
  *  @param {number} x  The x position of the top left corner of the marker.
  *  @param {number} y  The y position of the top left corner of the marker.
  *  @param {number} w  The width of the marker.
  *  @param {number} h  The height of the marker.
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *  @param {number} sx  Optional The horizontal scale factor of the marker.
  *  @param {number} sy  Optional The vertical scale factor of the marker.
  *  @param {boolean} maintainAspect Optional Indicates whether aspect ratio should be maintained (defaults to false)
  */
var  DvtGraphSelectableLineMarker = function(context, type, skin, x, y, w, h, id, sx, sy, maintainAspect)
{
   this.Init(context, type, skin, x, y, w, h, id, sx, sy, maintainAspect) ;
};

DvtObj.createSubclass(DvtGraphSelectableLineMarker, DvtMarker, "DvtGraphSelectableLineMarker") ;



/*---------------------------------------------------------------------*/
/*  Init()                                                             */
/*---------------------------------------------------------------------*/
/**
  *  Object initializer.
  *  @protected
  */
DvtGraphSelectableLineMarker.prototype.Init = function(context, type, skin, x, y, w, h, id, sx, sy, maintainAspect)
{
  DvtGraphSelectableLineMarker.superclass.Init.call(this, context, type, skin, x, y, w, h, id, sx, sy, maintainAspect) ;
  
  //For Selection
  this._savedFill = null;
  this._bSavedFill = false;
  this._bSavedStroke = false;
  
  this._zOrderManager = null;
};

/**
 * @private
 * Apply selection effects to line markers on line, area, combo, pareto, and radar graphs.
 */
DvtGraphSelectableLineMarker.prototype._applySelectingSelectionState = function() 
{
  var strokeColor = this._outerColor ? this._outerColor : "#000000";
  var stroke;
  var fill;
  
  if (this.isSelected()) 
  {
    if (this._zOrderManager)
      this._zOrderManager.bringToFrontOfSelection(this);
    
    stroke = new DvtSolidStroke(strokeColor, 1, 1.5);
    stroke.setMiterLimit(1);
    fill = this._innerColor ? new DvtSolidFill(DvtColorUtils.setAlpha(this._innerColor, 1)) : DvtSolidFill.invisibleFill();
      
    //This is for pre-selected case, we need to save the original stroke and fill without mouse over event
    if(!this._bSelecting) 
      this.SaveStrokeAndFill();
  } 
  else if (this._bSelecting) 
  {
    if (this._zOrderManager)
      this._zOrderManager.bringToFrontOfSelection(this);
    
    //Selecting stroke is a lighter version of the data color
    stroke = DvtSelectionEffectUtils.createSelectingStroke(this._dataColor);
    fill = DvtSolidFill.invisibleFill();
    this.SaveStrokeAndFill();
  } 
  else 
  {
    if (this._zOrderManager)
      this._zOrderManager.pushToBackOfSelection(this);
      
    stroke = this._savedStroke;
    fill = this._savedFill;
    this._savedStroke = null;
    this._savedFill = null
    this._bSavedStroke = false;
    this._bSavedFill = false;
  }
  
  this.setStroke(stroke);
  this.setFill(fill);
}

/**
 * @protected
 * Helper function to save the original stroke and fill
 */
DvtGraphSelectableLineMarker.prototype.SaveStrokeAndFill = function() 
{
  if (!this._bSavedStroke) 
  {
    this._savedStroke = this.getStroke();
    this._bSavedStroke = true;
  }
      
  if (!this._bSavedFill) 
  {
    this._savedFill = this.getFill();  
    this._bSavedFill = true;
  } 
}

/**
 * @override
 */
DvtGraphSelectableLineMarker.prototype.ApplySelectingState = function()
{
  this._applySelectingSelectionState();
};

/**
 * @override
 */
DvtGraphSelectableLineMarker.prototype.ApplySelectionState = function()
{
   this._applySelectingSelectionState();
};

DvtGraphSelectableLineMarker.prototype.setZOrderManager = function(manager) 
{
  this._zOrderManager = manager;
}

/**
 * @override
 */
DvtGraphSelectableLineMarker.prototype.UpdateSelectionEffect = function() {}
// Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
/**
  *  Creates a selectable shape using SVG path commands.
  *  @extends DvtPath
  *  @class DvtGraphSelectablePath  Creates an arbitrary shape using SVG path commands.
  *  @constructor
  *  @param {DvtContext} context  The platform dependent context object (such
  *                               as {@link DvtSvgContext}).
  *  @param {Object} cmds  Optional string of SVG path commands (see comment for
  *                        {@link DvtPath#setCmds}), or an array containing
  *                        consecutive command and coordinate entries (see comment
  *                        for {@link DvtPath#setCommands}).
  * @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var  DvtGraphSelectablePath = function(context, cmds, id)
{
   this.Init(context, cmds, id) ;
};

DvtObj.createSubclass(DvtGraphSelectablePath, DvtPath, "DvtGraphSelectablePath") ;

/**
  *  Object initializer.
  *  @protected
  */
DvtGraphSelectablePath.prototype.Init = function(context, cmds, id)
{
  DvtGraphSelectablePath.superclass.Init.call(this, context, cmds, id) ;
  
  this._bSelected = false;
  this._bSelecting = false;
  this._selectionEffects = null;
  this._bSelectedShadow = true;
  this._dataColor = "#000000";
  this._innerColor = null;
  this._outerColor = null;
  
  //properties related to selection
  this._savedStroke = null;
  this._bSavedStroke = false;
  this._relatedLogicalObjects = null;
  this._applyStrokeTimer = null;
  
  this._bZorder = true;
  this._zOrderManager = null;
  this._selectionType    = DvtSelectionEffectUtils.DEFAULT_SEL_TYPE;
};

/**
 * @protected
 * Apply any selection effects to this object.
 */
DvtGraphSelectablePath.prototype.ApplySelectionState = function()
{
  DvtSelectionEffectUtils.applySelectionState(this, this._dataColor, this._innerColor, this._outerColor);
};

/**
 * @protected
 * Apply any mouse hover selection effects to this object.
 */
DvtGraphSelectablePath.prototype.ApplySelectingState = function()
{
  DvtSelectionEffectUtils.applySelectingState(this, this._dataColor);
};


/**
 * Show the selection hover effect.
 */
DvtGraphSelectablePath.prototype.showHoverEffect = function()
{
  this.SetSelectionMouseOver(true);
};


/**
 * Hide the selection hover effect.
 */
DvtGraphSelectablePath.prototype.hideHoverEffect = function()
{
  this.SetSelectionMouseOver(false);
};


/**
 * Determine if the selection hover effect is shown.
 * 
 * @type boolean
 */
DvtGraphSelectablePath.prototype.isHoverEffectShown = function()
{
  return this._bSelecting;
};


/**
 * @protected
 * Set whether the selection mouse hover effect is shown.
 * 
 * @param bOver {boolean}  bOver  true if the mouse is over the object,
 *        false otherwise
 */
DvtGraphSelectablePath.prototype.SetSelectionMouseOver = function(bOver)
{
  this.SetSelectionMouseOverSelf(bOver);

  //set selection on related parts of this marker also
  if (this._relatedLogicalObjects)
  {
    for (var i = 0; i < this._relatedLogicalObjects.length; i++)
    {
      if (this._relatedLogicalObjects[i].SetSelectionMouseOverSelf)
      {
        this._relatedLogicalObjects[i].SetSelectionMouseOverSelf(bOver);
      }
    }
  }
}

/**
 * @protected
 * Set whether the selection mouse hover effect is shown.
 * 
 * @param bOver {boolean}  bOver  true if the mouse is over the object,
 *        false otherwise
 */
DvtGraphSelectablePath.prototype.SetSelectionMouseOverSelf = function(bOver)
{
  if (this._bSelecting != bOver)
  {
    this._bSelecting = bOver;
    if (this._bZorder)
    {
      if (this._bSelecting && this._zOrderManager) {
        this._zOrderManager.bringToFrontOfSelection(this);
      }
      else if (!this._bSelected && this._zOrderManager) {
        this._zOrderManager.pushToBackOfSelection(this);
      }
    }
    this.ApplySelectingState();
  }
};


/**
 * Determine whether this object is selected.
 * 
 * @type boolean
 */
DvtGraphSelectablePath.prototype.isSelected = function()
{
  return this._bSelected;
};

/**
 * Set whether the selection effect is shown.
 * 
 * @param bOver {boolean}  bSel  true if the object is selected,
 *        false otherwise
 */
DvtGraphSelectablePath.prototype.setSelected = function(bSel)
{
  this.SetSelectedSelf(bSel);

  //set selection on related parts of this marker also
  if (this._relatedLogicalObjects)
  {
    for (var i = 0; i < this._relatedLogicalObjects.length; i++)
    {
      if (this._relatedLogicalObjects[i].SetSelectedSelf)
      {
        this._relatedLogicalObjects[i].SetSelectedSelf(bSel);
      }
    }
  }
};

/**
 * @protected
 * Set whether the selection effect is shown.
 * 
 * @param bOver {boolean}  bSel  true if the object is selected,
 *        false otherwise
 */
DvtGraphSelectablePath.prototype.SetSelectedSelf = function(bSel)
{
  if (this._bSelected != bSel) {
    this._bSelected = bSel;
    if (this._bZorder)
    {
      if (this._bSelected && this._zOrderManager) {
        this._zOrderManager.bringToFrontOfSelection(this);
      }
      else if (!this._bSelecting && this._zOrderManager) {
        this._zOrderManager.pushToBackOfSelection(this);
      }
    }
    this.ApplySelectionState();
  }
};

/**
 * Add a related object of this det object.
 *
 * @param {DvtDisplayable} displayable A DvtDisplayable object related to this one
 */
DvtGraphSelectablePath.prototype.addRelatedObject = function(displayable)
{
  if (!this._relatedLogicalObjects)
  {
    this._relatedLogicalObjects = [];
  }

  this._relatedLogicalObjects.push(displayable);
};


/**
 * Set whether a drop shadow is shown when this object is selected.
 *
 * @param {boolean}  bShadow  true if a selection drop shadow should be shown,
 *        false if not
 */
DvtGraphSelectablePath.prototype.setSelectedShadow = function(bShadow)
{
  this._bSelectedShadow = bShadow;
};

/**
 * Specifies the color of the data item and its selection feedback, if different from the default.
 * @param {string} dataColor The CSS color string of the primary color of the data item.
 * @param {string} innerColor The CSS color string of the inner color of the selection effect.
 * @param {string} outerColor The CSS color string of the outer color of the selection effect.
 */
DvtGraphSelectablePath.prototype.setDataColor = function(dataColor, innerColor, outerColor)
{ 
  this._dataColor = dataColor;
  this._innerColor = innerColor;
  this._outerColor = outerColor;
}

/**
 * Sets whether the z order of an object should be adjusted for selection.
 *
 * @param {boolean}  yesNo true if zorder is to change
 */
DvtGraphSelectablePath.prototype.setZorder = function(yesNo)
{
  this._bZorder = yesNo;
};

DvtGraphSelectablePath.prototype.setZOrderManager = function(manager) 
{
  this._zOrderManager = manager;
}

/**
 * @override
 */
DvtGraphSelectablePath.prototype.UpdateSelectionEffect = function() {}
/**
  * Creates a selectable polygon from an array of consecutive (x,y) coordinates.
  * @extends DvtPolygon
  * @constructor  
  * @param {DvtContext} context   The platform dependent context object (such
  *                               as {@link DvtSvgContext}).
  * @param {Array} ar  An array of consecutive x,y coordinate pairs.
  * @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var DvtGraphSelectablePolygon = function(color, context, arPoints, id)
{
   this.Init(color, context, arPoints, id) ;
}

DvtObj.createSubclass(DvtGraphSelectablePolygon, DvtPolygon, "DvtGraphSelectablePolygon") ;

/**
  *  Object initializer.
  *  @protected
  */
DvtGraphSelectablePolygon.prototype.Init = function(context, arPoints, id)
{
  DvtGraphSelectablePolygon.superclass.Init.call(this, context, arPoints, id) ;
  this._dataColor = "#000000";
  this._innerColor = null;
  this._outerColor = null;
  
  this._bSelected = false;
  this._bSelecting = false;
  this._selectionEffects = null;
  this._bSelectedShadow = true;
  
  //properties related to selection
  this._savedStroke = null;
  this._bSavedStroke = false;
  this._relatedLogicalObjects = null;
  this._applyStrokeTimer = null;
  
  this._bZorder = true;
  this._zOrderManager = null;  
  this._selectionType = DvtSelectionEffectUtils.DEFAULT_SEL_TYPE;
}

/**
 * @protected
 * Apply any selection effects to this object.
 */
DvtGraphSelectablePolygon.prototype.ApplySelectionState = function()
{
  DvtSelectionEffectUtils.applySelectionState(this, this._dataColor, this._innerColor, this._outerColor);
}


/**
 * @protected
 * Apply any mouse hover selection effects to this object.
 */
DvtGraphSelectablePolygon.prototype.ApplySelectingState = function()
{
  DvtSelectionEffectUtils.applySelectingState(this, this._dataColor);
}


/**
 * Show the selection hover effect.
 */
DvtGraphSelectablePolygon.prototype.showHoverEffect = function()
{
  this.SetSelectionMouseOver(true);
};


/**
 * Hide the selection hover effect.
 */
DvtGraphSelectablePolygon.prototype.hideHoverEffect = function()
{
  this.SetSelectionMouseOver(false);
};


/**
 * Determine if the selection hover effect is shown.
 * 
 * @type boolean
 */
DvtGraphSelectablePolygon.prototype.isHoverEffectShown = function()
{
  return this._bSelecting;
}

/**
 * @protected
 * Set whether the selection mouse hover effect is shown.
 * 
 * @param bOver {boolean}  bOver  true if the mouse is over the object,
 *        false otherwise
 */
DvtGraphSelectablePolygon.prototype.SetSelectionMouseOver = function(bOver)
{
  this.SetSelectionMouseOverSelf(bOver);

  //set selection on related parts of this marker also
  if (this._relatedLogicalObjects)
  {
    for (var i = 0; i < this._relatedLogicalObjects.length; i++)
    {
      if (this._relatedLogicalObjects[i].SetSelectionMouseOverSelf)
      {
        this._relatedLogicalObjects[i].SetSelectionMouseOverSelf(bOver);
      }
    }
  }
}

/**
 * @protected
 * Set whether the selection mouse hover effect is shown.
 * 
 * @param bOver {boolean}  bOver  true if the mouse is over the object,
 *        false otherwise
 */
DvtGraphSelectablePolygon.prototype.SetSelectionMouseOverSelf = function(bOver)
{
  if (this._bSelecting != bOver)
  {
    this._bSelecting = bOver;
    if (this._bZorder)
    {
      if (this._bSelecting && this._zOrderManager)
      {
        this._zOrderManager.bringToFrontOfSelection(this);
      }
      else if (!this._bSelected && this._zOrderManager)
      {
        this._zOrderManager.pushToBackOfSelection(this);
      }
    }
    this.ApplySelectingState();
  }
};

/**
 * Determine whether this object is selected.
 * 
 * @type boolean
 */
DvtGraphSelectablePolygon.prototype.isSelected = function()
{
  return this._bSelected;
}

/**
 * Set whether the selection effect is shown.
 * 
 * @param bOver {boolean}  bSel  true if the object is selected,
 *        false otherwise
 */
DvtGraphSelectablePolygon.prototype.setSelected = function(bSel)
{
  this.SetSelectedSelf(bSel);

  //set selection on related parts of this marker also
  if (this._relatedDetObjects)
  {
    for (var i = 0; i < this._relatedDetObjects.length; i++)
    {
      if (this._relatedDetObjects[i].SetSelectedSelf)
      {
        this._relatedDetObjects[i].SetSelectedSelf(bSel);
      }
    }
  }
}

/**
 * @protected
 * Set whether the selection effect is shown.
 * 
 * @param bOver {boolean}  bSel  true if the object is selected,
 *        false otherwise
 */
DvtGraphSelectablePolygon.prototype.SetSelectedSelf = function(bSel)
{
  if (this._bSelected != bSel)
  {
    this._bSelected = bSel;
    if (this._bZorder)
    {
      if (this._bSelected && this._zOrderManager)
      {
        this._zOrderManager.bringToFrontOfSelection(this);
      }
      else if (!this._bSelecting && this._zOrderManager)
      {
        this._zOrderManager.pushToBackOfSelection(this);
      }
    }
    this.ApplySelectionState();
  }
}

/**
 * Add a related object of this logical object.
 *
 * @param {DvtDisplayable} displayable A DvtDisplayable object related to this one
 */
DvtGraphSelectablePolygon.prototype.addRelatedObject = function(displayable)
{
  if (!this._relatedLogicalObjects)
  {
    this._relatedLogicalObjects = [];
  }

  this._relatedLogicalObjects.push(displayable);
}


/**
 * Set whether a drop shadow is shown when this object is selected.
 *
 * @param {boolean}  bShadow  true if a selection drop shadow should be shown,
 *        false if not
 */
DvtGraphSelectablePolygon.prototype.setSelectedShadow = function(bShadow)
{
  this._bSelectedShadow = bShadow;
}

/**
 * Specifies the color of the data item and its selection feedback, if different from the default.
 * @param {string} dataColor The CSS color string of the primary color of the data item.
 * @param {string} innerColor The CSS color string of the inner color of the selection effect.
 * @param {string} outerColor The CSS color string of the outer color of the selection effect.
 */
DvtGraphSelectablePolygon.prototype.setDataColor = function(dataColor, innerColor, outerColor)
{ 
  this._dataColor = dataColor;
  this._innerColor = innerColor;
  this._outerColor = outerColor;
}

/**
 * Sets whether the z order of an object should be adjusted for selection.
 *
 * @param {boolean}  yesNo true if zorder is to change
 */
DvtGraphSelectablePolygon.prototype.setZorder = function(yesNo)
{
  this._bZorder = yesNo;
}

DvtGraphSelectablePolygon.prototype.setZOrderManager = function(manager) 
{
  this._zOrderManager = manager;
}

/**
 * @override
 */
DvtGraphSelectablePolygon.prototype.UpdateSelectionEffect = function() {}
// Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*   DvtPieChart                                                       */
/*---------------------------------------------------------------------*/

/*
 * Call chain:
 * 
 * The DvtGraphParser calls DvtPieChart.setPieInfo and DvtPieChart.setPosition
 * to set general pie properties and DvtPieChart.addSliceInfo to set individual 
 * slice properties when it parses the graph xml. 
 * 
 * Then, DvtPieChart.init gets called to create each logical DvtPieSlice object. Setting up the slice's size, location,
 * fill, label, label layout and the creation of the physical shapes are NOT done at this step.
 * 
 * DvtPieChart.render then gets called to 
 * 
 * 1. create and layout the physical pie surface objects and physical labels
 * 2. order the slices for rendering
 * 3. layout the slice labels and feelers
 * 4. render the actual slices (add them to this DvtPieChart)
 * 5. render the slice labels and feelers.
 */

/**
 * Creates an instance of DvtPieChart
 * @param {DvtContext} context The platform dependent context object (such as {@link DvtSvgContext}).
 * @param {DvtEventManager} eventManager The event manager that will process events for this pie.
 * @param {function} callback A function that responds to events
 * @param {object} callbackObj The optional object instance that the callback function is defined on.
 *  @class DvtPieChart
 *  @extends DvtContainer
 *  @constructor
 */
var DvtPieChart = function (context, eventManager, callback, callbackObj) {
  this.Init(context, eventManager, callback, callbackObj);
};

// constants for label placement
/** final @type number */
DvtPieChart.LABEL_POS_UNSPEC =  - 1;
/** final @type number */
DvtPieChart.LABEL_POS_NONE = 0;
/** final @type number */
DvtPieChart.LABEL_POS_INSIDE = 1;
/** final @type number */
DvtPieChart.LABEL_POS_OUTSIDE = 2;
/** final @type number */
DvtPieChart.LABEL_POS_OUTSIDE_FEELERS = 3;

// constants for label content
/** final @type number */
DvtPieChart.LABEL_TYPE_UNSPEC =  - 1;
/** final @type number */
DvtPieChart.LABEL_TYPE_PERCENT = 0;
/** final @type number */
DvtPieChart.LABEL_TYPE_VALUE = 1;
/** final @type number */
DvtPieChart.LABEL_TYPE_TEXT = 2;
/** final @type number */
DvtPieChart.LABEL_TYPE_TEXT_PERCENT = 3;
/** final @type number */
DvtPieChart.LABEL_TYPE_TEXT_VALUE = 4;
/** final @type number */
DvtPieChart.LABEL_TYPE_CUSTOM = 5;

// constants for pie explode/unite
/** final @type number */
DvtPieChart.EXPLODE = 0;
/** final @type number */
DvtPieChart.UNITE = 1;

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

/*----------------------------------------------------------------------*/
/* Init()                                                               */
/*----------------------------------------------------------------------*/
/**
 * Object initializer
 * @protected
 *
 * @param {DvtContext} context
 * @param {DvtEventManager} eventManager The event manager that will process events for this pie.
 * @param {function} callback A function that responds to events
 * @param {object} callbackObj The optional object instance that the callback function is defined on.
 */
DvtPieChart.prototype.Init = function (context, eventManager, callback, callbackObj) {
  DvtPieChart.superclass.Init.call(this, context);

  this._eventManager = eventManager;

  this._anchorOffset = 90;

  this._depth = 0;

  this._xRadius = 0;
  this._yRadius = 0;
  this._center = new DvtPoint(0, 0);

  this._pieInfo = null;// an instance of DvtPieInfo
  this._frame = new DvtRectangle(0, 0, 0, 0);// Pie frame
  this._bAutoExplode = false;// whether or not interactive slice behavior is enabled
  this._slices = new Array();// array of DvtPieSlice objects; stored in the order
  // in which they are parsed from the xml
  this._maxExplodePixels = 0;// max explode value amongst all slices
  this._is3D = false;

  // a DvtContainer where we add parts of the pie and feeler lines
  // any special filters (currently, drop shadow effect for 2D pies)
  // affecting the pie are added to this container  
  this._shapesContainer = new DvtContainer(context);

  // Support for changing z-order for selection
  this._numFrontObjs = 0;
  this._numSelectedObjsInFront = 0;

  this._animationDuration = 1;// in seconds, for explosion
  this._maxSliceIndex =  - 1;
};

/**
 * Returns the DvtEventManager for this component.
 * @return {DvtEventManager}
 */
DvtPieChart.prototype.__getEventManager = function () {
  return this._eventManager;
}

/**
 * Sets the position of the pie, and its pie frame
 *
 * @param {DvtPoint} center The center of the pie
 * @param {number} radiusX The x-radius of the pie
 * @param {number} radiusY The y-radius of the pie
 * @param {DvtRectangle} pieFrame The region allocated to the pie and its labels
 */
DvtPieChart.prototype.setPosition = function (center, radiusX, radiusY, pieFrame) {
  this._center = center;
  this._xRadius = radiusX;
  this._yRadius = radiusY;
  // Bug fix 12853989; make our own copy of the pie frame
  var frame = new DvtRectangle(pieFrame.x, pieFrame.y, pieFrame.w, pieFrame.h);
  this._frame = frame;
}

/**
 * Processes an event that has been dispatched from a container or peer.
 * @param {object} event
 */
DvtPieChart.prototype.processEvent = function (event) {
  var type = event.getType();
  if (type == DvtCategoryHideShowEvent.TYPE_HIDE || type == DvtCategoryHideShowEvent.TYPE_SHOW)
    DvtHideShowCategoryHandler.processEvent(event, this._slices);
  else if (type == DvtCategoryRolloverEvent.TYPE_OVER || type == DvtCategoryRolloverEvent.TYPE_OUT)
    DvtCategoryRolloverHandler.processEvent(event, this._slices);
};

/**
 * Highlights the specified categories.
 * @param {array} categories The array of categories whose data items will be highlighted. If null or empty, all 
 *                           highlighting will be removed.
 * @export
 */
DvtPieChart.prototype.highlight = function(categories) {
  DvtCategoryRolloverHandler.highlight(categories, this._slices);
}

/**
 * Returns the array of showPopupBehaviors for the object.
 * @return {array} The array of applicable DvtShowPopupBehaviors
 */
DvtPieChart.prototype.__getShowPopupBehaviors = function () {
  return null;// subclasses can override to provide popup support
}

// TODO JSDOC
DvtPieChart.prototype.render = function () {
  var shadow;
  if (!this.contains(this._shapesContainer)) {
    if (!this._shapesContainer) {
      this._shapesContainer = new DvtContainer(this.getCtx());
    }
    this.addChild(this._shapesContainer);

    if (!this._is3D && this._pieInfo.hasVisualEffects()) {
      var shadowRGBA = DvtColorUtils.makeRGBA(78, 87, 101, 0.45);

      shadow = new DvtShadow(shadowRGBA, 4, // distance
          7, // blurX
          7, // blurY
          54, // angle of the shadow
          2, // strength or the imprint/spread
          3, // quality
          false, // inner shadow
          false, // knockout effect
          false// hide object
      );
    }
  }

  // Set each slice's angle start and angle extent
  // The order in which these slices are rendered is determined in the later call to orderSlicesForRendering
  DvtPieChart._layoutSlices(this._slices, this._anchorOffset);

  // create the physical surface objects and labels
  var len = this._slices.length;
  for (var i = 0;i < len;i++) {
    var slice = this._slices[i];
    slice.preRender();
  }

  // we order the slices for rendering, such that
  // the "front-most" slice, the one closest to the user
  // is redered last.  each slice is then responsible
  // for properly ordering each of its surfaces before 
  // the surfaces are rendered.
  var zOrderedSlices = DvtPieChart._orderSlicesForRendering(this._slices);

  DvtPieChart._layoutLabelsAndFeelers(this);

  // now that everything has been laid out, tell the slices to 
  // render their surfaces 
  len = zOrderedSlices.length;
  for (var i = 0;i < len;i++) {
    var slice = zOrderedSlices[i];
    slice.render();
  }

  // then go back a second time and render the labels and feelers
  // we need this second pass because in the case of inside labels,
  // we want the labels rendered after all pie tops have been rendered
  len = zOrderedSlices.length;
  for (var i = 0;i < len;i++) {
    var slice = zOrderedSlices[i];
    slice.renderLabelAndFeeler();
  }

  // Apply the correct cursor to the slices if action is defined
  for (var sliceIndex = 0;sliceIndex < this._slices.length;sliceIndex++) {
    var slice = this._slices[sliceIndex];
    if (slice.getAction()) {
      var sliceDisplayables = slice.getDisplayables();
      for (var displayableIndex = 0;displayableIndex < sliceDisplayables.length;displayableIndex++) {
        sliceDisplayables[displayableIndex].setCursor(DvtSelectionEffectUtils.getSelectingCursor());
      }
    }
  }

  // Bug fix 12958165: Don't render shadows in Chrome SVG
  if (!(DvtAgent.isPlatformWebkit() && this.getCtx() instanceof DvtSvgContext)) {
    //BUG FIX #12427741: apply shadow after rendering slices because
    //shadow effect may depend on bounding box
    if (shadow) {
      this._shapesContainer.addDrawEffect(shadow);
    }
  }

  // perform initial animation if necessary
  if (this._pieInfo.isInitFx() && !this._bInitFxDone) {
    this._doInitFx();
    this._bInitFxDone = true;
  }

  // perform initial selection and explosion if necessary
  len = zOrderedSlices.length;
  for (var i = 0;i < len;i++) {
    var slice = zOrderedSlices[i];
    if (slice._selected)
      slice.setSelected(true);
    slice.explodeSlice(slice.getExplode());
  }

};

/**
 * Adds a DvtSliceInfo object to the pie chart. Hook used by DvtGraphParser to add parsed slice info to the DvtPieChart
 * @param {DvtSliceInfo}  sliceInfo The DvtSliceInfo object to add to the pie
 * @return {DvtPieSlice} The slice that was added.
 */
DvtPieChart.prototype.addSliceInfo = function (sliceInfo) {
  // Keep track of the max exploded pixels
  var explodePixels = sliceInfo.getExplode();
  if (explodePixels > this._maxExplodePixels)
    this._maxExplodePixels = explodePixels;

  //Keep track of the max slice index
  var index = sliceInfo.getIndex();
  if (index > this._maxSliceIndex)
    this._maxSliceIndex = index;

  // Create the DvtPieSlice object for this sliceInfo and add it to this pie
  var slice = this.CreateSlice(this, sliceInfo);
  this._slices.push(slice);
  return slice;
};

/**
 * @return {number} The highest index of the slices
 * This is not equal to the number of slices because the index may skip if there's a slice with zero value.
 */
DvtPieChart.prototype.getMaxSliceIndex = function () {
  return this._maxSliceIndex;
}

/**
 * Computes the total value of the pie
 *
 * @param {DvtPieChart} pieChart
 * @return {number} The total value of all pie slices
 *
 */
DvtPieChart.__computeTotal = function (pieChart) {
  var total = 0;
  var slices = pieChart._slices;

  for (var i = 0;i < slices.length;i++) {
    var sliceValue = slices[i].getSliceInfo().getValue();
    if (sliceValue >= 0)// Ignore negative slice values
      total += sliceValue;
  }

  return total;
}

/**
 * Computes the maximum explode pixels of all slices in the pie
 *
 * @param {Array} aSliceInfo An array of DvtSliceInfo objects
 * @return {number} The maximum explode value of all the slices
 *
 * @private
 */
DvtPieChart._computeMaxExplodePixels = function (aSliceInfo) {
  var len = aSliceInfo.length;

  var maxExplodePixels = 0;
  for (var i = 0;i < len;i++) {
    var sliceInfo = aSliceInfo[i];
    var explodePixels = sliceInfo.getExplode();

    if (explodePixels > maxExplodePixels) {
      maxExplodePixels = explodePixels;
    }
  }

  return maxExplodePixels;
}

/**
 * Creates a new DvtPieSlice with the specified params.
 * @param {DvtPieChart} pie
 * @param {DvtSliceInfo} sliceInfo
 */
DvtPieChart.prototype.CreateSlice = function (pie, sliceInfo) {
  return new DvtPieSlice(pie, sliceInfo);
}

// ported over from PieChart.as
/**
 * Sets the location of each slice in the pie. That is, each slice in the input slices array has its angle start and
 * angle extent set.  Label layout is not handled in this method.
 *
 * @param {Array} slices An array of DvtPieSlice objects
 * @param {number} anchorOffset The initial rotation offset for the pie, measured in degrees with 0 being the standard
 *                              0 from which trigonometric angles are measured. Thus, 90 means the first pie slice is
 *                              at the 12 o'clock position
 *
 * @private
 */
DvtPieChart._layoutSlices = function (slices, anchorOffset) {
  var i;
  var slice;
  var angle;

  var arc = 0;

  var nSlices = (slices) ? slices.length : 0;

  if (anchorOffset > 360)
    anchorOffset -= 360;

  if (anchorOffset < 0)
    anchorOffset += 360;

  var percentage = 0;
  var dataTotal = 0;
  if (nSlices > 0) {
    dataTotal = DvtPieChart.__computeTotal(slices[0].getPieChart());
  }

  for (i = 0;i < nSlices;i++) {
    slice = slices[i];

    value = slice.getSliceInfo().getValue();
    percentage = (dataTotal == 0) ? 0 : ((Math.abs(value) / dataTotal) * 100);

    arc = percentage * 3.60;// 3.60 = 360.0 / 100.0 - percentage of a 360 degree circle
    angle = anchorOffset - arc;

    if (angle < 0)
      angle += 360;

    slice.setAngleStart(angle);
    slice.setAngleExtent(arc);

    anchorOffset = slice.getAngleStart();// update anchor position for next slice
  }

}

/**
 * Sort slices by walking slices in a clockwise and then counterclockwise fashion,
 * processing the bottom-most slice last.  Each slice is responsible for sorting its
 * own surfaces so that they get rendered in the proper order.
 *
 * @param {Array} slices The array of DvtPieSlices to order
 * @return {Array} A z-ordered array of DvtPieSlices
 *
 * @private
 */
DvtPieChart._orderSlicesForRendering = function (slices) {
  var zOrderedSlices = [];
  var i;
  var nSlices = (slices) ? slices.length : 0;
  var slice;

  var rotateIdx =  - 1;
  var angleStart;
  var angleExtent;
  var sliceSpanEnd;

  // the amount of the slice, in degrees, by which the slice that spans the 12 o'clock position spans 
  // counterclockwise from 12 o'clock (i.e., how much of the slice is "before noon")
  var sliceSpanBeforeNoon;

  // if we have any sort of pie rotation, then we need to rotate a copy of the _slices array
  // so that the first entry in the array is at 12 o'clock, or spans 12 o'clock position
  // to do this, we just check the angle start and angle extent of each slice. The first element in
  // the array would be that angle whose start + extent = 90 or whose start < 90 and 
  // start + extent > 90.
  // find the index of the slice that spans the 12 o'clock position
  for (i = 0;i < nSlices;i++) {
    slice = slices[i];
    angleStart = slice.getAngleStart();
    angleExtent = slice.getAngleExtent();
    sliceSpanEnd = angleStart + angleExtent;

    if (sliceSpanEnd > 360)
      sliceSpanEnd -= 360;

    if (sliceSpanEnd < 0)
      sliceSpanEnd += 360;

    if ((sliceSpanEnd == 90) || ((angleStart < 90) && (sliceSpanEnd > 90))) {
      rotateIdx = i;
      sliceSpanBeforeNoon = sliceSpanEnd - 90;
      break;
    }
  }

  // now create an array in which the slices are ordered clockwise from the 12 o'clock position
  var rotatedSlices = [];
  for (i = rotateIdx;i < nSlices;i++) {
    rotatedSlices.push(slices[i]);
  }
  for (i = 0;i < rotateIdx;i++) {
    rotatedSlices.push(slices[i]);
  }

  //total accumulated angle of slice so far
  var accumAngle = 0;

  // the bottom-most slice index, whose extent either spans the two bottom
  // quadrants across the 270 degree mark (the "6 o'clock" position), 
  // or is tangent to the 270 degree mark
  var lastSliceIndexToProcess = 0;

  //
  // process slices clockwise, starting at the top, series 0
  //    
  var accumAngleThreshold = 180 + sliceSpanBeforeNoon;
  for (i = 0;i < nSlices;i++) {
    slice = rotatedSlices[i];

    if (slice) {
      // if this slice makes the accumAngle exceed 180 degrees, 
      // then save it for processing later because this is the
      // bottom-most slice (it crosses the 6 o'clock mark), 
      // which means it should be in front in the z-order
      if (accumAngle + slice.getAngleExtent() > accumAngleThreshold) {
        lastSliceIndexToProcess = i;
        break;
      }

      // add this slice to the rendering queue for slices
      zOrderedSlices.push(slice);

      //add the current slice extent to the accumulated total
      accumAngle += slice.getAngleExtent();
    }
  }

  for (i = nSlices - 1;i >= lastSliceIndexToProcess;i--) {
    slice = rotatedSlices[i];
    if (slice) {
      zOrderedSlices.push(slice);
    }
  }

  return zOrderedSlices;
}

/**
 * Returns a boolean indicating if the DvtPieChart is a 3D pie
 *
 * @return {boolean} true If the pie is to be rendered as a 3D pie
 */
DvtPieChart.prototype.is3D = function () {
  return this._is3D;
}

/**
 * Sets whether this DvtPieChart is a 3D pie or not
 *
 * @param {boolean} is3D true if this is a 3D pie, false if not
 */
DvtPieChart.prototype.set3D = function (is3D) {
  this._is3D = is3D;
}

/**
 * Determine the maximum distance a pie slice can be exploded from the pie
 *
 * @return {number} The maximum distance a pie slice can be exploded from the pie
 */
DvtPieChart.prototype.__calcMaxExplodeDistance = function () {
  var pieWidth = this._frame.w;
  var pieHeight = this._frame.h;
  var minDiameter = Math.min(pieWidth, pieHeight);
  var minDiameterConversionConstant = 0.047;// constant originally used in Flash impl
  var maxExplodeDistance = minDiameter * minDiameterConversionConstant;
  var explodeMax = maxExplodeDistance;
  if (explodeMax < 0)
    explodeMax = 0;

  return explodeMax;
}

/**
 * Explode all pie slices
 */
DvtPieChart.prototype.explodeAll = function () {
  this.animateExplode(0,  - 1);// explode all
}

/**
 * Unite all pie slices
 */
DvtPieChart.prototype.uniteAll = function () {
  this.animateExplode(1,  - 1);// unite/implode all
}

/**
 * returns true if the chart/pie has all its slices exploded
 */
DvtPieChart.prototype.isPieAllExploded = function () {
  var allExploded = true;
  var slice;
  var i;
  var length = this._slices.length;

  for (i = 0;i < length;i++) {
    slice = this._slices[i];
    if (slice.getExplode() == 0) {
      allExploded = false;
      break;
    }
  }

  return allExploded;
}

/**
 * returns true if the chart/pie has all its slices united (imploded, not exploded)
 */
DvtPieChart.prototype.isPieAllUnited = function () {
  var allUnited = true;
  var slice;
  var i;
  var length = this._slices.length;

  for (i = 0;i < length;i++) {
    slice = this._slices[i];
    if (slice.getExplode() > 0) {
      allUnited = false;
      break;
    }
  }

  return allUnited;
}

/**
 * Convenience function to explode a single slice by slice id
 *
 * @param {number} op Either DvtPieChart.EXPLODE or DvtPieChart.UNITE
 * @param {String} sliceId
 */
DvtPieChart.prototype.animateSingleSliceExplode = function (op, sliceId) {
  // find the seriedIdx corresponding to this sliceId
  var i;
  var slice;
  var length = this._slices.length;
  var seriesIdx =  - 1;

  for (i = 0;i < length;i++) {
    slice = this._slices[i];
    sliceInfo = slice.getSliceInfo();
    if (sliceInfo.getId() == sliceId) {
      seriesIdx = i;
      break;
    }
  }

  if (seriesIdx >  - 1)
    this.animateExplode(op, seriesIdx);
}

DvtPieChart.prototype.getAnimationDuration = function () {
  return this._animationDuration;
}

/**
 * Animate the explode/unite for a single slice, or for all slices
 *
 * @param {number} op Either DvtPieChart.EXPLODE or DvtPieChart.UNITE
 * @param {number} seriesIdx Either -1 for all slices, or the specific slice
 */
DvtPieChart.prototype.animateExplode = function (op, seriesIdx) {
  var length = this._slices.length;
  var slice;
  var i;

  // Animate the explode
  this._animationDuration = 2;

  // The defualt animation duration is 2 seconds. However, due to bug 12622757, we need 
  // to shorten the animation duration in order to get smooth slice animation without jitter.
  // Bug 12622757 is specific to Google Chrome, and it requires us to poke the DOM element 
  // that contains a filter in order to show animation of children of that DOM element.
  // However, due to bug 12715889, just poking the DOM also causes jitter in the
  // slice animation.  To get rid of the jitter, we round the amount of the translation we
  // apply to the pie slice and we also shorten the duration of the animation to visually
  // smooth out the effect of the rounding
  // Bug fix 12715889
  if (this.getCtx() instanceof DvtSvgContext && DvtAgent.isPlatformWebkit()) {
    this._animationDuration = 1;
  }

  var animator = new DvtAnimator(this.getCtx(), this._animationDuration);

  var exEnd = 0;

  var extent = this.__calcMaxExplodeDistance();// bug fix 8394428
  exEnd = (op == DvtPieChart.EXPLODE) ? extent : 0;

  if (seriesIdx ==  - 1) {
    var arPoints = [];
    for (i = 0;i < length;i++) {
      arPoints[i] = exEnd;
    }
    // have the animator animate all slices
    animator.addProp(DvtAnimator.TYPE_NUMBER_ARRAY, this, this._getSliceExplodes, this._setSliceExplodes, arPoints);
  }
  else if (this._pieInfo.isSliceInteractive() && seriesIdx >= 0 && seriesIdx < length) {
    slice = this._slices[seriesIdx];

    // animate just one slice
    animator.addProp(DvtAnimator.TYPE_NUMBER, slice, slice.getExplode, slice.explodeSlice, exEnd);
  }

  animator.play();
}

/**
 * Perform initial fade-in effects of labels
 */
DvtPieChart.prototype._doInitFx = function () {

  var ar = [];

  var len = this._slices.length;
  for (var i = 0;i < len;i++) {
    var label = this._slices[i].getSliceLabel();
    ar[i] = label;

    if (label)
      label.setAlpha(0);
  }

  var anim = new DvtAnimFadeIn(this._context, ar, this.getPieInfo().getAnimationDuration());
  anim.play();
}

/** Labeling management **/

// based on processOutsideLabels in PieChart.as
// this method replaces processOutsideLabels in the actionscript
/**
 * Layout the pie labels and feelers by delegating to DvtSliceLabelLayout
 *
 * @param {DvtPieChart} pie The pie whose labels we are going to layout
 *
 * @private
 */
DvtPieChart._layoutLabelsAndFeelers = function (pie) {
  var slices = pie._slices;

  if (slices && slices.length > 0) {
    var layout = new DvtSliceLabelLayout(pie);
    layout.layoutLabelsAndFeelers();
  }

}

/** end labeling code **/

/** Getters and setters **/

/**
 * @param {number} offset initial rotation
 */
DvtPieChart.prototype.setAnchorOffset = function (offset) {
  this._anchorOffset = offset;
}

/**
 * @return {DvtPoint} The center of this pie chart
 */
DvtPieChart.prototype.getCenter = function () {
  return this._center;
}

// no-op method; only here because the parser assumes every object has a setFill method
// we don't need it because the pie gets it from its chart object - this._Dlo.getChart()
// TODO - glook: remove when we make pie stand alone
DvtPieChart.prototype.setFill = function (fillType) {
  return;
}

DvtPieChart.prototype.setSolidFill = function (color, alpha) {
  return;
}

/**
 * @return {DvtRectangle} This DvtPieChart's pie frame
 */
DvtPieChart.prototype.__getFrame = function () {
  return this._frame;
}

/**
 * @return {number} the maximum explode distance of all slices
 */
DvtPieChart.prototype.__getMaxExplodePixels = function () {
  return this._maxExplodePixels;
}

/**
 * @return {DvtPieInfo} Non-positional pie attributes passed from the middle tier
 */
DvtPieChart.prototype.getPieInfo = function () {
  return this._pieInfo;
}

/**
 * @param {DvtPieInfo} pieInfo Non-positional pie attributes passed from the middle tier
 */
DvtPieChart.prototype.setPieInfo = function (pieInfo) {
  this._pieInfo = pieInfo;
}

/**
 * @return {number} the length of the pie chart's x-radius
 */
DvtPieChart.prototype.getRadiusX = function () {
  return this._xRadius;
}

/**
 * Return the slice color for the given series
 *
 * @param {Number} seriesIdx
 * @return {String}
 */
DvtPieChart.prototype.getSliceColor = function (seriesIdx) {
  var slice = this._slices[seriesIdx];

  if (slice)
    return slice.getSliceInfo().getFillColor();
  else 
    return null;
}

/**
 * Return the top surface displayable belonging to the slice with the given seriesIdx.
 * Internal API used for Automation purposes.
 *
 * @param {Number} seriesIdx
 * @return {DvtDisplayable}
 * @private
 */
DvtPieChart.prototype._getSliceDisplayable = function (seriesIdx) {
  if (seriesIdx >= 0 && seriesIdx < this._slices.length) {
    var topSurface = this._slices[seriesIdx].getTopSurface();
    if (topSurface && topSurface.length > 0)
      return topSurface[0];
  }

  return null;
}

/**
 * Dispatch the given event type on the pie slice with the given series index
 * Internal API used for Automation purposes
 *
 * @param {String} eventType
 * @param {Number} sliceIdx
 */
DvtPieChart.prototype.__dispatchTestEvent = function (eventType, sliceIdx) {
  var displayable = this._getSliceDisplayable(sliceIdx);
  displayable.dispatchDisplayableEvent(eventType);
}

/**
 * Return the slice label text for the given series
 *
 * @param {Number} seriesIdx
 * @return {String} Slice label
 */
DvtPieChart.prototype.getSliceText = function (seriesIdx) {
  var slice = this._slices[seriesIdx];

  if (slice) {
    var sliceLabel = slice.getSliceLabel();
    if (sliceLabel)
      return sliceLabel.getTextString();
  }

  return null;
}

/**
 * Return the slice tooltip for the given series
 *
 * @param {Number} seriesidx
 * @return {String} Slice tooltip
 */
DvtPieChart.prototype.getSliceTooltip = function (seriesIdx) {
  var slice = this._slices[seriesIdx];

  if (slice) {
    var tooltip = slice.getSliceInfo().getTooltip();
    if (tooltip)
      return DvtGraphParser.convertLineBreaks(tooltip);
  }

  return null;
}

/**
 * @return {DvtContainer} The DvtContainer where we add pie shapes and feeler lines to
 */
DvtPieChart.prototype.__getShapesContainer = function () {
  return this._shapesContainer;
}

/**
 * @return {number} the length of the pie chart's y-radius
 */
DvtPieChart.prototype.getRadiusY = function () {
  return this._yRadius;
}

/**
 * @return {Array} An array containing the DvtPieSlice objects in this pie chart
 */
DvtPieChart.prototype.__getSlices = function () {
  return this._slices;
}

//---------------------------------------------------------------------//
// Animation Support                                                   //
//---------------------------------------------------------------------//
/**
 * Creates the update animation for this object.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be used to chain animations.
 * @param {DvtPieChart} oldPie The old pie state to animate from.
 */
DvtPieChart.prototype.animateUpdate = function (handler, oldPie) {
  // Create a new handler for the slices.  This handler is created to provide
  // access to the new chart for deleted objects, and to isolate the playables
  // for the pie animation from the other playables in the handler.
  var sliceHandler = new DvtDataAnimationHandler(this.getCtx(), this);

  // Construct the animation to update slice values for the children
  sliceHandler.constructAnimation(oldPie.__getSlices(), this.__getSlices());
  var sliceAnim = sliceHandler.getAnimation();

  // Construct the animation to render the pie using the updated values
  var renderAnim = new DvtCustomAnimation(this.getCtx(), this, this.getPieInfo().getAnimationDuration());
  renderAnim.getAnimator().addProp(DvtAnimator.TYPE_NUMBER_ARRAY, this, this._getAnimationParams, this._setAnimationParams, this._getAnimationParams());

  // Combine and add to the chart handler
  var anim = new DvtParallelPlayable(this.getCtx(), sliceAnim, renderAnim);
  anim.setOnEnd(this.render, this);

  handler.add(anim, 0);
}

/**
 * Creates the insert animation for this object.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be used to chain animations.
 */
DvtPieChart.prototype.animateInsert = function (handler) {
  // This should never get called, since animation is only supported for a single pie
}

/**
 * Creates the delete animation for this object.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be used to chain animations.
 * @param {DvtContainer} container The container where deletes should be moved for animation.
 */
DvtPieChart.prototype.animateDelete = function (handler, container) {
  // This should never get called, since animation is only supported for a single pie
}

/**
 * @private
 */
DvtPieChart.prototype._getAnimationParams = function () {
  return [1];
}

/**
 * Called by the animation loop with a dummy parameter to force the chart to re-render.
 * @private
 */
DvtPieChart.prototype._setAnimationParams = function (params) {

  // First delete the current contents
  this.removeChildren();

  if (this._shapesContainer)
    this._shapesContainer.destroy();

  // Clear references to the removed displayables
  this._shapesContainer = null;

  // Then render the new ones
  this.render();
}

//---------------------------------------------------------------------//
// End Animation Support                                               //
//---------------------------------------------------------------------//
/**
 * Pseudo-method used for animation (see animateExplode)
 *
 * @return {Array} An array containing the explode values for each slice in the pie chart
 * @private
 */
DvtPieChart.prototype._getSliceExplodes = function () {
  var explodeValues = [];
  var len = this._slices.length;
  for (var i = 0;i < len;i++) {
    explodeValues[i] = this._slices[i].getSliceInfo().getExplode();
  }

  return explodeValues;
}

/**
 * Pseudo-method used for animation (see animateExplode)
 *
 * @param {Array} newExplodeValues An array of numbers corresponding to the explode values for each slice
 * @private
 */
DvtPieChart.prototype._setSliceExplodes = function (newExplodeValues) {
  // check if the new explode value is different from the old, and if so, update   
  var len = this._slices.length;
  for (var i = 0;i < len;i++) {
    var slice = this._slices[i];
    var newExplodeVal = newExplodeValues[i];

    if (slice.getSliceInfo().getExplode() != newExplodeVal) {
      slice.explodeSlice(newExplodeVal);

      // Find the maximum explosion, used to layout labels
      if (newExplodeVal > this._maxExplodePixels) {
        this._maxExplodePixels = newExplodeVal;
      }
    }
  }
}

/**
 * @return {number} The pie chart's depth
 */
DvtPieChart.prototype.getDepth = function () {
  return this._depth;
}

/**
 * @param {number} depth The pie chart's depth
 */
DvtPieChart.prototype.setDepth = function (depth) {
  this._depth = depth;
}

//---------------------------------------------------------------------//
// Ordering Support: ZOrderManager impl                                //
//---------------------------------------------------------------------//
/**
 * @override
 */
DvtPieChart.prototype.bringToFrontOfSelection = function (slice) {
  var par = slice.getPieChart()._shapesContainer;
  if (par) {
    var parentChildCount = par.getNumChildren();
    if (parentChildCount - this._numFrontObjs > 1) {
      // Only change z-order for top surface
      par.removeChild(slice._topSurface[0]);
      var newIndex = parentChildCount - this._numFrontObjs - 1;
      par.addChildAt(slice._topSurface[0], newIndex);
    }
  }
};

/**
 * @override
 */
DvtPieChart.prototype.pushToBackOfSelection = function (slice) {
  var len = this._slices.length;
  var counter = 0;
  for (i = 0;i < len;i++) {
    if (this._slices[i].isSelected())
      counter++;
  }
  this._numSelectedObjsInFront = counter;
  //move the object to the first z-index before the selected objects
  var par = slice.getPieChart()._shapesContainer;
  if (par) {
    var parentChildCount = par.getNumChildren();
    var newIndex = parentChildCount - this._numFrontObjs - 1 - this._numSelectedObjsInFront;
    if (newIndex >= 0) {
      par.removeChild(slice._topSurface[0]);
      par.addChildAt(slice._topSurface[0], newIndex);
    }
  }
};

/**
 * @override
 */
DvtPieChart.prototype.setNumFrontObjs = function (num) {
  this._numFrontObjs = num;
};

DvtPieChart.prototype.getShapesForViewSwitcher = function(bOld) {
  var shapes = {};
  
  if (this._slices) {
    for (var iSlice = 0; iSlice < this._slices.length; iSlice++) {
      var slice = this._slices[iSlice];
      if (slice) {
        var chartDataItem = slice.getId();
        if (chartDataItem) {
          var id = chartDataItem.getId();
          if (id) {
            if (slice._topSurface && slice._topSurface.length > 0) {
              var topShape = slice._topSurface[0];
              shapes[id] = topShape;
            }
            //if (slice._crustSurface && slice._crustSurface.length > 0) {
            //  var crustShape = slice._crustSurface[0];
            //  shapes[id + "_crust"] = crustShape;
            //}
            //if (slice._leftSurface && slice._leftSurface.length > 0) {
            //  var leftShape = slice._leftSurface[0];
            //  shapes[id + "_left"] = leftShape;
            //}
            //if (slice._rightSurface && slice._rightSurface.length > 0) {
            //  var rightShape = slice._rightSurface[0];
            //  shapes[id + "_right"] = rightShape;
            //}
          }
        }
      }
    }
  }
  
  return shapes;
};
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/* Class DvtPieInfo       Pie information from the parser              */
/*---------------------------------------------------------------------*/

/**
 * Property bag to hold non-positional pie attributes passed to the client from the middle tier
 *
 * @constructor
 */

var DvtPieInfo = function () {
  this._init();
};

DvtObj.createSubclass(DvtPieInfo, DvtObj, "DvtPieInfo");

/**
 * Private helper function to initialize the pie info's private fields
 *
 * @private
 */
DvtPieInfo.prototype._init = function () {
  this._labelPosition = DvtPieChart.LABEL_POS_UNSPEC;
  this._labelType = DvtPieChart.LABEL_TYPE_UNSPEC;
  this._feelerColor = "#000000";

  this._style = null;// DvtCSSStyle
  this._converter = null;

  this._arSliceLabels = null;
  this._labelVF = null;// DvtValueFormat
  this._isSliceInteractive = false;// boolean indicating whether or not interactive slice behavior is enabled
  this._fillType = DvtFill.NONE;// represents the series effect; one of DvtFill.COLOR, DvtFill.GRADIENT, 
  // DvtFill.IMAGE, DvtFill.NONE, or DvtFill.PATTERN 
  this._hasVisualEffects = true;// boolean flag for visual effects                  
  this._innerColor = null;
  this._outerColor = null;

  this._id = null;// String
  this._isInitFx = false;// boolean indicating whether initial display animation is on
};

/**
 * Returns a boolean indicating if animation on display has been enabled
 *
 * @return {boolean} true if animation on display has been enabled, false otherwise
 */
DvtPieInfo.prototype.isInitFx = function () {
  return this._isInitFx;
}

/**
 * Enables animation on display
 *
 * @param {boolean} enable
 */
DvtPieInfo.prototype.setInitFx = function (initfx) {
  this._isInitFx = initfx;
}

/**
 * Returns the feeler color
 *
 * @return {String}
 */
DvtPieInfo.prototype.getFeelerColor = function () {
  return this._feelerColor;
}

/**
 * Sets the feeler color
 *
 * @param {String} color
 */
DvtPieInfo.prototype.setFeelerColor = function (color) {
  this._feelerColor = color;
}

/**
 * Returns the fill type (series effect) applied to this pie chart
 *
 * @return {number} One of DvtFill.COLOR, DvtFill.GRADIENT, DvtFill.IMAGE, DvtFill.NONE, or DvtFill.PATTERN
 */
DvtPieInfo.prototype.getFillType = function () {
  return this._fillType;
}

/**
 * Sets the fill type (series effect)
 *
 * @param {number} fillType One of DvtFill.COLOR, DvtFill.GRADIENT, DvtFill.IMAGE, DvtFill.NONE, or DvtFill.PATTERN
 */
DvtPieInfo.prototype.setFillType = function (fillType) {
  this._fillType = fillType;
}

/**
 * Returns the inner color of the selection feedback.
 * @return {string}
 */
DvtPieInfo.prototype.getSelectedInnerColor = function () {
  return this._innerColor;
}

/**
 * Specifies the inner color of the selection feedback.
 * @param {string} color
 */
DvtPieInfo.prototype.setSelectedInnerColor = function (color) {
  this._innerColor = color;
}

/**
 * Returns the inner color of the selection feedback.
 * @return {string}
 */
DvtPieInfo.prototype.getSelectedOuterColor = function () {
  return this._outerColor;
}

/**
 * Specifies the inner color of the selection feedback.
 * @param {string} color
 */
DvtPieInfo.prototype.setSelectedOuterColor = function (color) {
  this._outerColor = color;
}

/**
 * Returns the CSS style font attributes
 * @return {DvtCSSStyle}
 */
DvtPieInfo.prototype.getCSSStyle = function () {
  return this._style;
}

/**
 * Sets the CSS style font attributes
 * @param {DvtCSSStyle} style
 */
DvtPieInfo.prototype.setCSSStyle = function (style) {
  this._style = style;
}

/**
 * Returns the default label color to be used when color isn't specified via setCSSStyle and when text position is 
 * outside the slices.
 * @return {string}
 */
DvtPieInfo.prototype.getDefaultLabelColor = function() {
  return this._defaultLabelColor;
}

/**
 * Specifies the default label color to be used when color isn't specified via setCSSStyle and when text position is 
 * outside the slices.
 * @param {string} color
 */
DvtPieInfo.prototype.setDefaultLabelColor = function(color) {
  this._defaultLabelColor = color;
}

/**
 * Returns the id for this pie.
 *
 * @return {String}
 */
DvtPieInfo.prototype.getId = function () {
  return this._id;
}

/**
 * Sets the id for this pie.
 *
 * @param {String} id
 */
DvtPieInfo.prototype.setId = function (id) {
  this._id = id;
}

/**
 * Returns the location of the labels
 *
 * @return {number} One of DvtPieChart.LABEL_POS_UNSPEC, DvtPieChart.LABEL_POS_NONE, DvtPieChart.LABEL_POS_INSIDE,
 *                  DvtPieChart.LABEL_POS_OUTSIDE, or DvtPieChart.LABEL_POS_OUTSIDE_FEELERS
 */
DvtPieInfo.prototype.getLabelPosition = function () {
  return this._labelPosition;
}

/**
 * Sets the location of the labels
 *
 * @param {number} One of DvtPieChart.LABEL_POS_UNSPEC, DvtPieChart.LABEL_POS_NONE, DvtPieChart.LABEL_POS_INSIDE,
 *                 DvtPieChart.LABEL_POS_OUTSIDE, or DvtPieChart.LABEL_POS_OUTSIDE_FEELERS
 */
DvtPieInfo.prototype.setLabelPosition = function (pos) {
  this._labelPosition = pos;
}

/**
 * Returns the slice labels
 *
 * @return {Array} An array of strings listing the slice labels
 */
DvtPieInfo.prototype.getLabels = function () {
  return this._arSliceLabels;
}

/**
 * Sets the slice labels
 *
 * @param {Array} labels An array of strings listing the slice labels
 */
DvtPieInfo.prototype.setLabels = function (labels) {
  this._arSliceLabels = labels;
}

/**
 * Returns the type of label contents
 *
 * @return {number} One of DvtPieChart.LABEL_TYPE_UNSPEC, DvtPieChart.LABEL_TYPE_PERCENT, DvtPieChart.LABEL_TYPE_VALUE,
 *                  DvtPieChart.LABEL_TYPE_TEXT, DvtPieChart.LABEL_TYPE_TEXT_PERCENT,
 *                  DvtPieChart.LABEL_TYPE_TEXT_VALUE, or DvtPieChart.LABEL_TYPE_CUSTOM
 */
DvtPieInfo.prototype.getLabelType = function () {
  return this._labelType;
}

/**
 * Sets the type of label contents
 *
 * @param {number} One of DvtPieChart.LABEL_TYPE_UNSPEC, DvtPieChart.LABEL_TYPE_PERCENT, DvtPieChart.LABEL_TYPE_VALUE,
 *                 DvtPieChart.LABEL_TYPE_TEXT, DvtPieChart.LABEL_TYPE_TEXT_PERCENT,
 *                 DvtPieChart.LABEL_TYPE_TEXT_VALUE, or DvtPieChart.LABEL_TYPE_CUSTOM
 */
DvtPieInfo.prototype.setLabelType = function (type) {
  this._labelType = type;
}

/**
 * Returns the converter for the label
 *
 * @return {object} the converter for the label
 */
DvtPieInfo.prototype.getConverter = function () {
  return this._converter;
}

/**
 * Sets the converter for the label
 *
 * @param {object} the converter for the label
 */
DvtPieInfo.prototype.setConverter = function (converter) {
  this._converter = converter;
}

/**
 * Returns a boolean indicating if interactiveSliceBehavior has been enabled
 *
 * @return {boolean} true if interactive slice behavior has been enabled, false otherwise
 */
DvtPieInfo.prototype.isSliceInteractive = function () {
  return this._isSliceInteractive;
}

/**
 * Enables interactive slice behavior
 *
 * @param {boolean} enable
 */
DvtPieInfo.prototype.setSliceInteractive = function (enable) {
  this._isSliceInteractive = enable;
}

/**
 * Returns the DvtValueFormat object for this DvtPieChart
 *
 * @return {DvtValueFormat}
 */
DvtPieInfo.prototype.getValueFormat = function () {
  return this._labelVF
}

/**
 * Sets the DvtValueFormat object for this DvtPieChart
 *
 * @param {DvtValueFormat} format
 */
DvtPieInfo.prototype.setValueFormat = function (format) {
  this._labelVF = format;
}

/**
 * Returns a boolean indicating if visual effects are enabled on this pie (visualEffects=AUTO)
 *
 * return {boolean} true if visualEffects is set to AUTO, false if visualEffects is set to NONE
 */
DvtPieInfo.prototype.hasVisualEffects = function () {
  return this._hasVisualEffects;
}

/**
 * Sets the visual effects on this pie chart
 *
 * @param {boolean} hasVisualEffects
 */
DvtPieInfo.prototype.setVisualEffects = function (hasVisualEffects) {
  this._hasVisualEffects = hasVisualEffects;
}

/**
 * Return the skin to use for rendering.  If the value is not 'skyros', the simplified gradient effects will be used.
 * @return {string} skin
 */
DvtPieInfo.prototype.getSkin = function () {
  return this._skin ? this._skin : DvtCSSStyle.SKIN_SKYROS;
}

/**
 * Specifies the skin to use for rendering.  If the value is not 'skyros', the simplified gradient effects will be used.
 * @param {string} skin
 */
DvtPieInfo.prototype.setSkin = function (skin) {
  this._skin = skin;
}

/**
 * Returns the animation duration for this pie.
 * @return {number}
 */
DvtPieInfo.prototype.getAnimationDuration = function () {
  return this._duration;
}

/**
 * Specifies the animation duration for this pie.
 * @param {number} duration
 */
DvtPieInfo.prototype.setAnimationDuration = function (duration) {
  this._duration = duration;
}
// Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*   DvtPieSlice                                                       */
/*---------------------------------------------------------------------*/

/*
 * Call chain:
 * 
 * DvtPieChart creates each logical DvtPieSlice object.  The physical surface objects are 
 * then created in DvtPieChart.render, by calling DvtPieSlice.preRender()
 * 
 * In DvtPieSlice.preRender() we
 * 
 * 1. setup the gradient used for this DvtPieSlice
 * 2. create the physical objects representing each surface
 * 
 * The labels are then created and laid out by DvtSliceLabelLayout.layoutLabelsAndFeelers.
 * 
 * After the label layout is complete, DvtPieChart then calls
 * 
 * 1. render() to render the pie slice itself
 * 2. renderLabelAndFeeler() to render the pie label and feeler (if necessary)
 * 
 */

/**
 * Creates an instance of DvtPieSlice
 *
 * @param {DvtPieChart} pieChart A reference to the DvtPieChart that contains this slice
 * @param {DvtSliceInfo} sliceInfo Slice parameters sent down from the middle tier
 * @class DvtPieSlice
 * @constructor
 * @implements {DvtLogicalObject}
 * @implements {DvtCategoricalObject}
 * @implements {DvtSelectable}
 * @implements {DvtTooltipSource}
 * @implements {DvtPopupSource}
 * @implements {DvtContextMenuSource}
 */
var DvtPieSlice = function (pieChart, sliceInfo) {
  this.Init(pieChart, sliceInfo);
}

DvtObj.createSubclass(DvtPieSlice, DvtObj, "DvtPieSlice");

/**
 * Object initializer
 *
 * @param {DvtPieChart} pieChart A reference to the DvtPieChart that contains this slice
 * @param {DvtSliceInfo} sliceInfo Slice parameters sent down from the middle tier
 * @private
 */
DvtPieSlice.prototype.Init = function (pieChart, sliceInfo) {
  this._pieChart = pieChart;
  this._sliceInfo = sliceInfo;

  this._angleStart = 0;
  this._angleExtent = 0;

  this._topSurface = null;// an array of DvtShapes representing the top of the slice
  this._leftSurface = null;// an array of DvtShapes representing the left side of the slice 
  // ("left" as seen from the tip of the slice)
  this._rightSurface = null;// an array of DvtShapes representing the right side of the slice 
  // ("right" as seen from the tip of the slice)
  this._crustSurface = null;// an array of DvtShapes representing the crust of the slice                              
  this._explodeOffsetX = 0;
  this._explodeOffsetY = 0;

  this._sliceLabel = null;
  this._sliceLabelPosition = null;
  this._sliceLabelString = null;

  this._hasFeeler = false;
  this._feelerRad = null;// the section of the feeler closest to the pie
  this._feelerHoriz = null;// the section of the feeler closest to the label
  this._outsideFeelerStart = null;// a point class with x and y fields. This represents the point on the pie 
  // from which the feeler originates in the unexploded state
  this._outsideFeelerMid = null;// a point class with x and y fields. This represents the point on the pie 
  // from which the feeler bends                           
  this._outsideFeelerEnd = null;// a point class with x and y fields. This represents the point not on the pie 
  // at which the feeler ends
  this._selected = false;
  this._selecting = false;

  this._radiusX = this._pieChart.getRadiusX();
  this._radiusY = this._pieChart.getRadiusY();
};

/**
 * Returns the owning DvtPieChart object.
 * @return {DvtPieChart}
 */
DvtPieSlice.prototype.getPieChart = function () {
  return this._pieChart;
}

/**
 * Render the pie slice only; rendering of label and feeler
 * occurs in DvtPieSlice.renderLabelAndFeelers
 */
DvtPieSlice.prototype.render = function () {
  var sortedSurfaces = DvtPieSlice._sortPieSurfaces(this._topSurface, this._leftSurface, this._rightSurface, this._crustSurface, this._angleStart, this._angleExtent);
  var len = sortedSurfaces.length;
  for (var i = 0;i < len;i++) {
    var shapeArray = sortedSurfaces[i];
    // shapeArray is an array of DvtShapes representing the given surface. 
    // Iterate through this array and add each shape to the pieChart
    shapeCount = shapeArray.length;
    for (var j = 0;j < shapeCount;j++) {
      var shapesContainer = this._pieChart.__getShapesContainer();
      shapesContainer.addChild(shapeArray[j]);
      if (shapeArray[j].render)// render is only defined on certain shape subclasses
        shapeArray[j].render();
    }
  }

};

/**
 * Render the pie slice's label and feeler (if necessary)
 */
DvtPieSlice.prototype.renderLabelAndFeeler = function () {
  // assume that all layout and text truncation has already been done
  // so in theory, we just need to call addChild with the feeler and label
  var sliceLabel = this._sliceLabel;
  if (sliceLabel) {
    this._pieChart.addChild(this._sliceLabel);

    // Associate the shapes with the slice for use during event handling
    DvtPieSlice._associate(this, [this._sliceLabel]);

    if (this._pieChart.getPieInfo().getLabelPosition() == DvtPieChart.LABEL_POS_OUTSIDE_FEELERS) {
      this._renderOutsideFeeler();
    }
  }

}

DvtPieSlice.prototype._feelerFromPts = function (pt1, pt2) {
  var context = this._pieChart.getCtx();
  var feeler = new DvtLine(context, pt1.x, pt1.y, pt2.x, pt2.y);

  var color = this._pieChart.getPieInfo().getFeelerColor();
  var stroke = new DvtSolidStroke(color);
  feeler.setStroke(stroke);

  if (!this._pieChart.contains(feeler)) {
    //add the feeler in the z-order just below this slice so the
    //slice will overlap it
    var shapesContainer = this._pieChart.__getShapesContainer();
    var sliceTopIndex = shapesContainer.getChildIndex(this._topSurface[0]);
    shapesContainer.addChildAt(feeler, sliceTopIndex);
  }

  return feeler;
}

/**
 * Render a feeler outside the slice.
 * @private
 */
DvtPieSlice.prototype._renderOutsideFeeler = function () {
  if (!this._hasFeeler) {
    return;
  }

  var feelerRad = this._feelerFromPts(this._outsideFeelerStart, this._outsideFeelerMid);
  var feelerHoriz = this._feelerFromPts(this._outsideFeelerMid, this._outsideFeelerEnd);

  // Store a reference to it so that we can remove
  this._feelerRad = feelerRad;
  this._feelerHoriz = feelerHoriz;
}

// ported over from PieSlice.as
/**
 * Creates the gradients and physical shapes for the pie surfaces. Z-Ordering of the shapes
 * and layout and creation of the pie label is done elsewhere.
 */
DvtPieSlice.prototype.preRender = function () {
  // adjust center for exploded slice
  this.explodeSlice(this._sliceInfo.getExplode());
  this._createSurfaces();
};

/**
 * Create the shapes representing the surfaces for this DvtPieSlice.
 * @private
 */
DvtPieSlice.prototype._createSurfaces = function () {
  var pieInfo = this._pieChart.getPieInfo();
  var sliceInfo = this.getSliceInfo();
  var fillType = pieInfo.getFillType();
  var pattern = sliceInfo.getFillPattern();
  
  // Create the fills
  var topFill, lateralFill;
  if (fillType == DvtFill.PATTERN || pattern != null) 
    topFill = new DvtPatternFill(pattern, sliceInfo.getFillColor())
  else if (fillType == DvtFill.GRADIENT) {
    var skin = pieInfo.getSkin();
    var grAngle = (skin == DvtCSSStyle.SKIN_SKYROS) ? 297 : 270;
    var style = (!this._pieChart.is3D()) ? "2D" : "3D";

    var arColors = DvtPieRenderUtils.getGradientColors(sliceInfo.getFillColor(), style, skin);
    var arAlphas = DvtPieRenderUtils.getGradientAlphas(sliceInfo.getFillAlpha(), style);
    var arRatios = DvtPieRenderUtils.getGradientRatios(style, skin);
    var arBounds = [Math.floor(this._pieChart.getCenter().x - this._pieChart.getRadiusX()), 
                    Math.floor(this._pieChart.getCenter().y - this._pieChart.getRadiusY()), 
                    Math.ceil(2 * this._pieChart.getRadiusX()), 
                    Math.ceil(2 * this._pieChart.getRadiusY())];

    topFill = new DvtLinearGradientFill(grAngle, arColors, arAlphas, arRatios, arBounds);
  }
  else {
    topFill = new DvtSolidFill(sliceInfo.getFillColor(), sliceInfo.getFillAlpha());
    lateralFill = new DvtSolidFill(DvtColorUtils.getRGB(DvtColorUtils.getDarker(sliceInfo.getFillColor(), 0.60)), sliceInfo.getFillAlpha());
  }

  // Create the Top Surface
  this._topSurface = DvtPieSlice.createTopSurface(this, topFill);

  // 3D Effect Support
  if (this._pieChart.is3D() && (this._pieChart.getDepth() > 0 || this._pieChart.getRadiusX() != this._pieChart.getRadiusY())) {
    var useGradientFill = (fillType == DvtFill.GRADIENT || fillType == DvtFill.PATTERN);
    var sideFill = useGradientFill ? DvtPieSlice._generateLateralGradientFill(this, DvtPieRenderUtils.SIDE) : lateralFill;
    var crustFill = useGradientFill ? DvtPieSlice._generateLateralGradientFill(this, DvtPieRenderUtils.CRUST) : lateralFill;

    // Create the side surfaces for the slice, which will be sorted later
    this._leftSurface = DvtPieSlice._createLateralSurface(this, DvtPieSlice.SURFACE_LEFT, sideFill);
    this._rightSurface = DvtPieSlice._createLateralSurface(this, DvtPieSlice.SURFACE_RIGHT, sideFill);
    this._crustSurface = DvtPieSlice._createLateralSurface(this, DvtPieSlice.SURFACE_CRUST, crustFill);
  }
}

// This logic is ported over from PieChart.sortSliversBySlice, and pushed
// from the PieChart to the PieSlice
/**
 * Sorts this DvtPieSlice's surfaces by z-order
 * 
 * @param {Array} topSurface An array of DvtShapes representing the top of this DvtPieSlice
 * @param {Array} leftSurface An array of DvtShapes representing the left side of this DvtPieSlice (as seen from
                  the tip of the slice)
 * @param {Array} rightSurface An array of DvtShapes representing the right side of this DvtPieSlice (as seen from
                  the tip of the slice)
 * @param {Array} crustSurface An array of DvtShapes representing the crust of this DvtPieSlice
 * @param {number} angleStart The starting position of this pie slice
 * @param {number} angleExtent The angular size of this pie slice
 * 
 * @return {Array} A sorted array of arrays (two-dimensional array) of DvtShape objects for this slice to render.
 * @private
 */
DvtPieSlice._sortPieSurfaces = function (topSurface, leftSurface, rightSurface, crustSurface, angleStart, angleExtent) {
  var sortedSurfaces = [];

  if (leftSurface && rightSurface && crustSurface) {
    // ported from PieChart.sortSliversBySlice
    // NOTE that instead of relying on the order in which the surfaces were 
    // originally created, we just get them from associative array by name    
    // the last slice to render, 
    // or if a slice starts at 270 degrees/6 o'clock (Fix for BUG 12577691)
    if (angleStart <= 270 && (angleStart + angleExtent > 270)) {
      //we're in the bottom-most slice, so add surfaces in back-to-front z-order:
      //left edge, right edge, crust
      sortedSurfaces.push(leftSurface);
      sortedSurfaces.push(rightSurface);
      sortedSurfaces.push(crustSurface);
    }

    // right-side of the pie
    else if (angleStart > 270 || (angleStart + angleExtent <= 90)) {
      //we're in the right side of the pie, so add surfaces in back-to-front z-order:
      //left edge, crust, right edge
      sortedSurfaces.push(leftSurface);
      sortedSurfaces.push(crustSurface);
      sortedSurfaces.push(rightSurface);
    }

    else {
      //we're in the left side of the pie, so add surfaces in back-to-front z-order:
      //right edge, crust, left edge
      sortedSurfaces.push(rightSurface);
      sortedSurfaces.push(crustSurface);
      sortedSurfaces.push(leftSurface);
    }

  }

  // top is rendered last
  sortedSurfaces.push(topSurface);

  return sortedSurfaces;
}

/**
 * Returns true if (x-y1) and (x-y2) have different signs
 */
DvtPieSlice.oppositeDirection = function (x, y1, y2) {
  var positive1 = (x - y1) > 0;
  var positive2 = (x - y2) > 0;
  return positive1 != positive2;
}

/**
 * Explodes the DvtPieSlice and feeler line by the given explode amount from the center of the pie.
 *
 * @param {number} explode Amount by which to explode this DvtPieSlice out. 0 means slice is not exploded.
 */
DvtPieSlice.prototype.explodeSlice = function (explode) {
  this._sliceInfo.setExplode(explode);

  if (explode != 0) {
    var arc = this._angleExtent;
    var angle = this._angleStart;
    var fAngle = angle + (arc / 2);
    var radian = (360 - fAngle) * DvtMath.RADS_PER_DEGREE;

    var tilt = this._pieChart.getRadiusY() / this._pieChart.getRadiusX();

    // bug fix 8394428
    var explodeOffset = this._sliceInfo.getExplode();
    var maxExplodeDistance = this._pieChart.__calcMaxExplodeDistance();

    if (maxExplodeDistance < explodeOffset)
      explodeOffset = maxExplodeDistance;

    this._explodeOffsetX = ((Math.cos(radian) * explodeOffset));
    this._explodeOffsetY = ((Math.sin(radian) * tilt * explodeOffset));

    // To work around bug 12715831, in the 2D pie case, we need to poke the 
    // DOM element that contains the shadow filter that is applied to the pie slices.
    // However, due to bug 12715889, just poking the DOM also causes jitter in the
    // slice animation.  To get rid of the jitter, we round the amount of the translation we
    // apply to the pie slice and we also shorten the duration of the animation to visually smooth
    // out the result of the rounding.
    // Bug fix 12715889
    if (this._pieChart.getCtx() instanceof DvtSvgContext && DvtAgent.isPlatformWebkit()) {
      this._explodeOffsetX = Math.round(this._explodeOffsetX);
      this._explodeOffsetY = Math.round(this._explodeOffsetY);
    }
  }
  else {
    this._explodeOffsetX = 0;
    this._explodeOffsetY = 0;
  }

  // now update each surface
  if (this._topSurface) {
    DvtPieSlice._translateShapes(this._topSurface, this._explodeOffsetX, this._explodeOffsetY);
  }

  if (this._rightSurface) {
    DvtPieSlice._translateShapes(this._rightSurface, this._explodeOffsetX, this._explodeOffsetY);
  }

  if (this._leftSurface) {
    DvtPieSlice._translateShapes(this._leftSurface, this._explodeOffsetX, this._explodeOffsetY);
  }

  if (this._crustSurface) {
    DvtPieSlice._translateShapes(this._crustSurface, this._explodeOffsetX, this._explodeOffsetY);
  }

  // update the feeler line
  if (this._hasFeeler) {
    // get current starting x and y, and then update the feeler line only
    var oldStartX = this._outsideFeelerStart.x;
    var oldStartY = this._outsideFeelerStart.y;

    var newStartX = oldStartX + this._explodeOffsetX;
    var newStartY = oldStartY + this._explodeOffsetY;

    this._feelerRad.setX1(newStartX);
    this._feelerRad.setY1(newStartY);

    var oldMidX = this._outsideFeelerMid.x;
    var oldMidY = this._outsideFeelerMid.y;

    //The midpoint of the feeler has to be updated if the new radial feeler is pointing towards an opposite direction;
    //otherwise, the feeler will go through the slice.
    //The easy solution is to set the x/y of the midPt to be the same as startPt.
    if (DvtPieSlice.oppositeDirection(oldMidX, oldStartX, newStartX)) {
      this._feelerRad.setX2(newStartX);
      this._feelerHoriz.setX1(newStartX);
    }
    else {
      this._feelerRad.setX2(oldMidX);
      this._feelerHoriz.setX1(oldMidX);
    }

    if (DvtPieSlice.oppositeDirection(oldMidY, oldStartY, newStartY)) {
      this._feelerRad.setY2(newStartY);
      this._feelerHoriz.setY1(newStartY);
    }
    else {
      this._feelerRad.setY2(oldMidY);
      this._feelerHoriz.setY1(oldMidY);
    }

  }
  else if (this._sliceLabel) {
    //update the label position
    this._translateLabel(this._sliceLabel, this._explodeOffsetX, this._explodeOffsetY);
  }
}

/**
 * Translates slice label by the delta x and delta y
 *
 * @param {DvtTextArea} slice label to translate
 * @param {number} tx
 * @param {number} ty
 *
 * @private
 */
DvtPieSlice.prototype._translateLabel = function (sliceLabel, tx, ty) {
  if (!sliceLabel)
    return;

  sliceLabel.setTranslateX(this._sliceLabelPosition.x + tx);
  sliceLabel.setTranslateY(this._sliceLabelPosition.y + ty);
}

/**
 * Translates each element in an array of shapes by the same delta x and delta y
 *
 * @param {Array} shapes An array of DvtShape objects to translate
 * @param {number} tx
 * @param {number} ty
 *
 * @private
 */
DvtPieSlice._translateShapes = function (shapes, tx, ty) {
  if (!shapes)
    return;

  var len = shapes.length;

  for (var i = 0;i < len;i++) {
    var shape = shapes[i];
    shape.setTranslateX(tx);
    shape.setTranslateY(ty);
  }

}

/** Labeling code; most of the real work is done in DvtSliceLabelLayout **/

/**
 * Returns the untruncated label text for a given pie slice
 *
 * @param {DvtPieSlice} slice
 * @return {String} The full, untruncated label string, or null if the slice's pie chart
 is configured to not display labels
 */
DvtPieSlice.generateSliceLabelString = function (slice) {
  var pieChart = slice.getPieChart();

  if (pieChart.getPieInfo().getLabelPosition() == DvtPieChart.LABEL_POS_NONE) {
    return null;
  }

  var pieChartLabelType = pieChart.getPieInfo().getLabelType();
  if (pieChartLabelType == DvtPieChart.LABEL_TYPE_CUSTOM)// bug 9011108 (ER)
  {
    var customText = slice.getSliceInfo().getText();
    return (customText) ? customText : "?";
  }

  var spercent = "";
  var svalue = "";
  var stext = "";
  var s = "";

  var converter = pieChart.getPieInfo().getConverter();
  var formatter = pieChart.getPieInfo().getValueFormat();
  var value = slice.getSliceInfo().getValue();

  // BUG fix 7349510 - PSLVF attribute
  //
  var percentage = 0;
  var dataTotal = DvtPieChart.__computeTotal(pieChart);

  percentage = (dataTotal == 0) ? 0 : ((Math.abs(value) / dataTotal) * 100);

  if (converter && converter['getAsString']) {
    // if converter is set, we delegate the formatting to the converter
    spercent = converter['getAsString'](percentage / 100);
  }
  else {
    // if no converter is found, we do default formatting
    try {
      spercent = formatter.getFormattedValue(percentage / 100);
    }
    catch (vf1Error)// TODO glook (from hugh) cleanup this catch.  Catches like this hide errors, and this code should not be necessary.
    {
      // warning: US decimal point only
      s = percentage.toString();

      if (s.length > 5) {
        s = s.slice(0, 5);
      }
      else {
        if (s.indexOf(".", 0) < 0 && s.length < 4)
          s += ".";

        while (s.length < 5)
          s += "0";
      }
      s += "%";
      spercent = s;
    }
  }

  // get data value
  if (!isNaN(value)) {
    if (converter && converter['getAsString']) {
      // if converter is set, we delegate the formatting to the converter
      svalue = converter['getAsString'](value);
    }
    else {
      // if no converter is found, we do default formatting
      // BUG fix 7349510 -  - PSLVF attribute
      try {
        svalue = formatter.getFormattedValue(value);
      }
      catch (vf2Error)// TODO glook (from hugh) cleanup this catch.  Catches like this hide errors, and this code should not be necessary.
      {
        // warning: US decimal point only
        svalue = value.toString();

        if (formatter) {
          svalue = formatter.getFormattedValue(value);
        }

        formatter = null;
      }
    }
  }

  // Bug 13098740 set text to what was parsed from data xml
  var parsedText = slice.getSliceInfo().getText();
  if (parsedText)
    stext = parsedText;

  if (pieChartLabelType == DvtPieChart.LABEL_TYPE_PERCENT || pieChartLabelType == DvtPieChart.LABEL_TYPE_UNSPEC) {
    return spercent;
  }
  else if (pieChartLabelType == DvtPieChart.LABEL_TYPE_VALUE) {
    return svalue;
  }
  else if (pieChartLabelType == DvtPieChart.LABEL_TYPE_TEXT) {
    return stext;
  }
  else if (pieChartLabelType == DvtPieChart.LABEL_TYPE_TEXT_PERCENT) {
    return stext + ", " + spercent;
  }
  else if (pieChartLabelType == DvtPieChart.LABEL_TYPE_TEXT_VALUE)// bug 8445625
  {
    return stext + ", " + svalue;
  }
  else {
    return null;
  }
}

/** End labeling code **/

/** Getters and setters **/

/**
 * @return {number} The size of this pie slice's angle
 */
DvtPieSlice.prototype.getAngleExtent = function () {
  return this._angleExtent;
}

/**
 * @param {number} extent The size of this pie slice's angle
 */
DvtPieSlice.prototype.setAngleExtent = function (extent) {
  this._angleExtent = extent;
}

/**
 * @return {number} The starting angle location of this pie slice
 */
DvtPieSlice.prototype.getAngleStart = function () {
  return this._angleStart;
}

/**
 * @param {number} start The starting angle location of this pie slice
 */
DvtPieSlice.prototype.setAngleStart = function (start) {
  this._angleStart = start;
}

/**
 * @return {number} A convenience method to access this slice's explode amount
 */
DvtPieSlice.prototype.getExplode = function () {
  // when we pass in methods to the animator to animate the pie slice on explode, the methods
  // have to be defined on the pie slice
  return this._sliceInfo.getExplode();
}

/**
 * @return {number} The x-offset for this pie slice. Zero if the slice is not exploded.
 */
DvtPieSlice.prototype.__getExplodeOffsetX = function () {
  return this._explodeOffsetX;
}

/**
 * @return {number} The y-offset for this pie slice. Zero if the slice is not exploded.
 */
DvtPieSlice.prototype.__getExplodeOffsetY = function () {
  return this._explodeOffsetY;
}

/**
 * Set the points for a feeler outside the slice.
 *
 * @param {object} startPt The starting point of the feeler, located on the pie slice. Point has an x and y field
 * @param {object} midPt The mid point of the feeler, located between the slice and the label. Point has an x and y field
 * @param {object} endPt The ending point of the feeler, located on the pie label. Point has an x and y field
 */
DvtPieSlice.prototype.setOutsideFeelerPoints = function (startPt, midPt, endPt) {
  this._outsideFeelerStart = startPt;
  this._outsideFeelerMid = midPt;
  this._outsideFeelerEnd = endPt;
  this._hasFeeler = true;
}

/**
 * Set the slice without feeler.
 */
DvtPieSlice.prototype.setNoOutsideFeeler = function () {
  this._outsideFeelerStart = null;
  this._outsideFeelerMid = null;
  this._outsideFeelerEnd = null;
  this._hasFeeler = false;
}

/**
 * Returns a DvtSliceInfo object which contains all the slice properties that come down from the middle tier
 *
 * @return {DvtSliceInfo}
 */
DvtPieSlice.prototype.getSliceInfo = function () {
  return this._sliceInfo;
}

/**
 * Sets this slice's DvtSliceInfo object containing all of this slice's properties sent from the middle tier
 *
 * @param {DvtSliceInfo} sliceInfo
 */
DvtPieSlice.prototype.setSliceInfo = function (sliceInfo) {
  this._sliceInfo = sliceInfo;
}

/**
 * @return {DvtTextArea} The label for this slice
 */
DvtPieSlice.prototype.getSliceLabel = function () {
  return this._sliceLabel;
}

/**
 * @param {DvtTextArea} sliceLabel
 */
DvtPieSlice.prototype.setSliceLabel = function (sliceLabel) {
  this._sliceLabel = sliceLabel;

  // need to store the initial position in order to animate the label during explosion
  if (sliceLabel) {
    this._sliceLabelPosition = {
      x : sliceLabel.getTranslateX(), y : sliceLabel.getTranslateY()
    };
  }
}

/**
 * @return {String} Untruncated slice label if slice label is truncated.
 */
DvtPieSlice.prototype.getSliceLabelString = function () {
  return this._sliceLabelString;
}

/**
 * @param {String} labelStr Untruncated slice label if slice label is truncated.
 */
DvtPieSlice.prototype.setSliceLabelString = function (labelStr) {
  this._sliceLabelString = labelStr;
}

/**
 * @return {Array} The top surface displayables of this Pie Slice
 */
DvtPieSlice.prototype.getTopSurface = function () {
  return this._topSurface;
}

/**
 * @return {String} The series id
 */
DvtPieSlice.prototype.getId = function () {
  return this._sliceInfo.getId();
}

/**
 * Returns true if this slice contains the given bounding box, used for labels.
 * @param {object} bbox The bounding box to check containment for.
 * @param {DvtPieSlice} node The pie slice on which to check containment.
 */
DvtPieSlice.containsRect = function(bbox, slice) {
  return (slice.contains(bbox.x, bbox.y) && slice.contains(bbox.x + bbox.w, bbox.y) && 
          slice.contains(bbox.x + bbox.w, bbox.y + bbox.h) && slice.contains(bbox.x, bbox.y + bbox.h));
}

/**
 * Returns true if the specified displayable can be selected or hovered.
 * @param {DvtDisplayable} displayable
 * @return {boolean}
 */
DvtPieSlice._shapeIsSelectable = function(shape) {
  return ((shape instanceof DvtGraphSelectableArc) 
    || (shape instanceof DvtGraphSelectablePolygon)
    || (shape instanceof DvtGraphSelectablePath));
}

/**
 * Returns true if this slice contains the given coordinates.
 * @param {number} x
 * @param {number} y
 * @return {boolean}
 */
DvtPieSlice.prototype.contains = function(x, y) {
  var c = this._pieChart.getCenter();
  var cos = (x - c.x) / this._radiusX;
  var sin = (y - c.y) / this._radiusY;
  
  // Compute the angle
  var angle = -Math.atan2(sin, cos) * (180/Math.PI); // in degrees
  // First adjust angle to be greater than the start angle.
  while(angle < this._angleStart)
    angle += 360;
  // Then adjust to be within 360 degrees of it  
  while(angle - this._angleStart >= 360)
    angle -= 360;
  
  var containsRadius = Math.pow(cos, 2) + Math.pow(sin, 2) <= 1;
  var containsAngle = angle <= this._angleStart + this._angleExtent;
  return containsRadius && containsAngle;
}

//---------------------------------------------------------------------//
// Animation Support                                                   //
//---------------------------------------------------------------------//
DvtPieSlice.prototype.GetAnimationParams = function () {
  return [this.getSliceInfo().getValue(), this._radiusX, this._radiusY];
}

DvtPieSlice.prototype.SetAnimationParams = function (params) {
  this.getSliceInfo().setValue(params[0]);
  this._radiusX = params[1];
  this._radiusY = params[2];
}

/**
 * Creates the update animation for this object.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be used to chain animations.
 * @param {DvtPieSlice} oldSlice The old pie state to animate from.
 */
DvtPieSlice.prototype.animateUpdate = function (handler, oldSlice) {
  var startState = oldSlice.GetAnimationParams();
  var endState = this.GetAnimationParams();

  if (!DvtArrayUtils.equals(startState, endState)) {
    // Create the animation
    var anim = new DvtCustomAnimation(this._pieChart.getCtx(), this, this.getPieChart().getPieInfo().getAnimationDuration());
    anim.getAnimator().addProp(DvtAnimator.TYPE_NUMBER_ARRAY, this, this.GetAnimationParams, this.SetAnimationParams, endState);
    handler.add(anim, 0);

    // Initialize to the start state
    this.SetAnimationParams(startState);
  }
}

/**
 * Creates the insert animation for this object.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be used to chain animations.
 */
DvtPieSlice.prototype.animateInsert = function (handler) {
  // Create the animation
  var anim = new DvtCustomAnimation(this._pieChart.getCtx(), this, this.getPieChart().getPieInfo().getAnimationDuration());
  anim.getAnimator().addProp(DvtAnimator.TYPE_NUMBER_ARRAY, this, this.GetAnimationParams, this.SetAnimationParams, this.GetAnimationParams());
  handler.add(anim, 0);

  // Initialize to the start state
  this.SetAnimationParams([0, 0, 0]);
}

/**
 * Creates the delete animation for this object.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be used to chain animations.
 * @param {DvtPieChart} container The new container where the pie slice should be moved for animation.
 */
DvtPieSlice.prototype.animateDelete = function (handler, container) {
  // Reparent the slice to the new pie by moving the slice info
  var slice = container.addSliceInfo(this.getSliceInfo());
  var newSlices = container.__getSlices();
  newSlices.splice(newSlices.length - 1, 1);// remove the slice so that it can be added in the right spot
  // We need to reorder the new slice so that it's in the right relative order
  var oldSlices = this.getPieChart().__getSlices();
  var oldIndex = DvtArrayUtils.getIndex(oldSlices, this);
  var prevIndex = oldIndex - 1;

  // Add the new slice in the right spot
  if (prevIndex >= 0) {
    var prevId = oldSlices[prevIndex].getId();
    // Find the location of the previous slice
    for (var i = 0;i < newSlices.length;i++) {
      if (newSlices[i].getId()==prevId) {
        newSlices.splice(i + 1, 0, slice);
        break;
      }
    }
  }
  else 
    newSlices.splice(0, 0, slice);

  // Create the animation to delete the slice
  var anim = new DvtCustomAnimation(container.getCtx(), this, this.getPieChart().getPieInfo().getAnimationDuration());
  anim.getAnimator().addProp(DvtAnimator.TYPE_NUMBER_ARRAY, slice, slice.GetAnimationParams, slice.SetAnimationParams, [0, 0, 0]);

  // Set the onEnd listener so that the slice can be deleted
  anim.setOnEnd(slice._removeDeletedSlice, slice);

  // Finally add the animation
  handler.add(anim, 0);
}

/**
 * Removes a deleted slice from the owning pie chart.  A re-render must be performed for the
 * results to be visible.
 */
DvtPieSlice.prototype._removeDeletedSlice = function () {
  var slices = this.getPieChart().__getSlices();
  var index = DvtArrayUtils.getIndex(slices, this);

  if (index >= 0)
    slices.splice(index, 1);
}

/**
 * Return the action string for the data item, if any exists.
 * @returns {string} the action outcome for the data item.
 */
DvtPieSlice.prototype.getAction = function () {
  return this.getSliceInfo().getAction();
}

//---------------------------------------------------------------------//
// DvtLogicalObject impl                                               //
//---------------------------------------------------------------------//
/**
 * @override
 */
DvtPieSlice.prototype.getDisplayables = function () {
  var ret = new Array();

  if (this._topSurface)
    ret = ret.concat(this._topSurface);

  if (this._leftSurface)
    ret = ret.concat(this._leftSurface);

  if (this._rightSurface)
    ret = ret.concat(this._rightSurface);

  if (this._crustSurface)
    ret = ret.concat(this._crustSurface);

  if (this._sliceLabel)
    ret.push(this._sliceLabel);

  if (this._feelerRad)
    ret.push(this._feelerRad);

  if (this._feelerHoriz)
    ret.push(this._feelerHoriz);

  return ret;
}

//---------------------------------------------------------------------//
// Selection Support: DvtSelectable impl                               //
//---------------------------------------------------------------------//
/**
 * @return return the physical shape
 */
DvtPieSlice.prototype.getPhysicalShape = function (obj) {
  var pieInfo = this.getPieChart().getPieInfo();
  obj.setDataColor(this.getSliceInfo().getFillColor(), pieInfo.getSelectedInnerColor(), pieInfo.getSelectedOuterColor());
  obj.setZorder(false);
  obj.setSelectedShadow(false);
  obj.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
  return obj;
}
/**
 * @override
 */
DvtPieSlice.prototype.isSelectable = function () {
  // Only called by the selection manager when selection is enabled
  return true;
};

/**
 * @override
 */
DvtPieSlice.prototype.isSelected = function () {
  return this._selected;
};

/**
 * @override
 */
DvtPieSlice.prototype.setSelected = function (bSelected) {
  this._selected = bSelected;
  if (this._selected) {
    this._pieChart.bringToFrontOfSelection(this);
  }
  else if (!this._selecting) {
    this._pieChart.pushToBackOfSelection(this);
  }
  var shapeArr = this.getDisplayables();
  for (i = 0;i < shapeArr.length;i++) {
    if (DvtPieSlice._shapeIsSelectable(shapeArr[i])) {
      var shape = this.getPhysicalShape(shapeArr[i]);
      shape.setSelected(bSelected);
    }
  }
};

/**
 * @override
 */
DvtPieSlice.prototype.showHoverEffect = function () {
  this._selecting = true;
  this._pieChart.bringToFrontOfSelection(this);
  var shapeArr = this.getDisplayables();
  for (i = 0;i < shapeArr.length;i++) {
    if (DvtPieSlice._shapeIsSelectable(shapeArr[i])) {
      var shape = this.getPhysicalShape(shapeArr[i]);
      shape.showHoverEffect();
    }
  }
};

/**
 * @override
 */
DvtPieSlice.prototype.hideHoverEffect = function () {
  this._selecting = false;
  if (!this._selected) {
    this._pieChart.pushToBackOfSelection(this);
  }
  var shapeArr = this.getDisplayables();
  for (i = 0;i < shapeArr.length;i++) {
    if (DvtPieSlice._shapeIsSelectable(shapeArr[i])) {
      var shape = this.getPhysicalShape(shapeArr[i]);
      shape.hideHoverEffect();
    }
  }
};

//---------------------------------------------------------------------//
// Tooltip Support: DvtTooltipSource impl                              //
//---------------------------------------------------------------------//
/**
 * @override
 */
DvtPieSlice.prototype.getDatatip = function (target, x, y) {
  if (target == this._sliceLabel) {
    if (this._sliceLabel && this._sliceLabel.isTruncated())
      return this.getSliceLabelString();
  }
  return this.getSliceInfo().getTooltip();
};

/**
 * @override
 */
DvtPieSlice.prototype.getDatatipColor = function () {
  return this.getSliceInfo().getFillColor();
};

//---------------------------------------------------------------------//
// Popup Support: DvtPopupSource impl                                  //
//---------------------------------------------------------------------//
/**
 * @override
 */
DvtPieSlice.prototype.getShowPopupBehaviors = function () {
  // First try to retrieve from sliceInfo, used by the new charts.  Other get from the pieChart.
  var spb = this.getSliceInfo().getShowPopupBehaviors();
  return spb ? spb : this._pieChart.__getShowPopupBehaviors();
};

//---------------------------------------------------------------------//
// Context Menu Support: DvtContextMenuSource impl                     //
//---------------------------------------------------------------------//
/**
 * @override
 */
DvtPieSlice.prototype.getContextMenuId = function () {
  return this.getSliceInfo().getContextMenuId();
};

//---------------------------------------------------------------------//
// Rollover and Hide/Show Support: DvtCategoricalObject impl           //
//---------------------------------------------------------------------//
/**
 * @override
 */
DvtPieSlice.prototype.getCategories = function () {
  return [this.getSliceInfo().getId().getSeries()];
};

//---------------------------------------------------------------------//
// Keyboard Support: DvtKeyboardNavigables impl                        //
//---------------------------------------------------------------------//
/**
 * @override
 */
DvtPieSlice.prototype.getNextNavigable = function(event) 
{
  if(event.type == DvtMouseEvent.CLICK)
  {
    return this;
  }
  else if(event.keyCode == DvtKeyboardEvent.SPACE && event.ctrlKey)
  {
    // multi-select node with current focus; so we navigate to ourself and then let the selection handler take
    // care of the selection
    return this;
  }
  
  var slices = this._pieChart.__getSlices();
  var idx = slices.indexOf(this);
  var next = null;

  if (event.keyCode == DvtKeyboardEvent.RIGHT_ARROW) {
  	if (idx < slices.length-1) 
      	  next = slices[idx+1];
  	else
          next = slices[0];
  } else if (event.keyCode == DvtKeyboardEvent.LEFT_ARROW) {
	if (idx == 0) 
	  next = slices[slices.length - 1];
	else
          next = slices[idx-1];
  }
  return next;
}

/**
 * @override
 */
DvtPieSlice.prototype.getKeyboardBoundingBox = function () {
  var displayables = this.getDisplayables();
  if (displayables[0])
    return displayables[0].getDimensions();
  else 
    return new DvtRectangle(0, 0, 0, 0);
}

/**
 * @override
 */
DvtPieSlice.prototype.showKeyboardFocusEffect = function() 
{
  this._isShowingKeyboardFocusEffect = true;
  this.showHoverEffect();

};

/**
 * @override
 */
DvtPieSlice.prototype.hideKeyboardFocusEffect = function() 
{
  this._isShowingKeyboardFocusEffect = false;
  this.hideHoverEffect();
};

/**
 * @override
 */
DvtPieSlice.prototype.isShowingKeyboardFocusEffect = function () {
  return this._isShowingKeyboardFocusEffect;
}
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/* A partitioning of the DvtPieSlice code. Static methods associated   */
/* with generating pie slice surfaces live in this file                */
/*---------------------------------------------------------------------*/

/*
 * Static methods for generating the physical shapes that make up the different pieces of a DvtPieSlice
 */

// surface types
DvtPieSlice.SURFACE_CRUST = 0;
DvtPieSlice.SURFACE_LEFT = 1;
DvtPieSlice.SURFACE_RIGHT = 2;
DvtPieSlice.SURFACE_TOP = 3;

/*****************************************************/
/* Publicly exposed static methods                   */
/*****************************************************/

/**
 * @this {DvtPieSlice}
 * Returns an array of DvtShape objects representing the top of a pie slice
 *
 * @param {DvtFill} fill The fill for the top
 * @param {DvtPieSlice} slice The slice to generate the top for
 * @return {Array} An array of DvtShape objects representing the top of this pie slice
 */
DvtPieSlice.createTopSurface = function (slice, fill) {
  var pieChart = slice.getPieChart();
  var pieInfo = pieChart.getPieInfo();
  var context = pieChart.getCtx();

  var sliceInfo = slice._sliceInfo;

  var pieCenter = pieChart.getCenter();

  // Bug fix 12419047
  var sliceClosureType = DvtArc.PIE;

  if (slice.getAngleExtent() == 360) {
    sliceClosureType = DvtArc.OPEN;
  }

  var wedge = new DvtGraphSelectableArc(context, pieCenter.x, pieCenter.y, slice._radiusX, slice._radiusY, slice.getAngleStart(), slice.getAngleExtent(), sliceClosureType);
  wedge.setDataColor(sliceInfo.getFillColor(), pieInfo.getSelectedInnerColor(), pieInfo.getSelectedOuterColor());
  wedge.setZOrderManager(this._Chart);

  wedge.setTranslateX(slice.__getExplodeOffsetX());
  wedge.setTranslateY(slice.__getExplodeOffsetY());
  wedge.setFill(fill);
  if (sliceInfo.getStrokeColor()) {
    wedge.setSolidStroke(sliceInfo.getStrokeColor(), sliceInfo.getStrokeAlpha());
  }

  // WAI-ARIA
  wedge.setAriaRole('img');
  var ariaLabel = slice.getSliceInfo().getTooltip();
  wedge.setAriaProperty('label', ariaLabel);

  var shapes = [wedge];

  if (!slice.getSliceInfo().getStrokeColor() && pieChart.getPieInfo().hasVisualEffects() && pieChart.is3D() && pieChart.getDepth() > 0 && pieChart.getPieInfo().getFillType() == DvtFill.GRADIENT && (slice.getAngleStart() >= 180 || (slice.getAngleStart() + slice.getAngleExtent()) >= 180 || slice.getAngleExtent() == 360))// Bug fix 12419047; slice.getAngleExtent() == 360 means we only have one slice
  {
    // create arc for the pie border with border gradient and add arc to shapes array
    var edge = DvtPieSlice._createGradientPieBorder(slice, fill);
    edge.setTranslateX(slice.__getExplodeOffsetX());
    edge.setTranslateY(slice.__getExplodeOffsetY());
    shapes.push(edge);
  }

  // Associate the shapes with the slice for use during event handling
  DvtPieSlice._associate(slice, shapes);

  return shapes;
}

/*****************************************************/
/* Private methods                                   */
/*****************************************************/

/**
 * Associates the specified displayables with the specified slice.
 * @param {DvtPieSlice} slice The owning slice.
 * @param {array} displayables The displayables to associate.
 */
DvtPieSlice._associate = function (slice, displayables) {
  if (!displayables)
    return;

  for (var i = 0;i < displayables.length;i++)
    slice._pieChart.__getEventManager().associate(displayables[i], slice);
}

/**
 * Private helper method to generate the gradient border for a pie slice
 * @param {DvtPieSlice} slice
 * @param {DvtGradientFill} topFill
 * @return {DvtArc} The gradient border to apply on top of the pie slice at the edge
 * @private
 */
DvtPieSlice._createGradientPieBorder = function (slice, topFill) {
  var sliceInfo = slice.getSliceInfo();

  // first create the gradient to apply
  var style = DvtPieRenderUtils.BORDER;
  var arColors = DvtPieRenderUtils.getGradientColors(null, style);// base color ignored for border gradient
  var arAlphas = DvtPieRenderUtils.getGradientAlphas(sliceInfo.getStrokeAlpha(), style);
  var arRatios = DvtPieRenderUtils.getGradientRatios(style);

  var arBounds = topFill.getBounds();

  grAngle = 120;// constant used in Flash impl originally 120
  //  grAngle = 360 - grAngle ;        // Html5 toolkit rotates from 0 clockwise so
  // convert to anticlockwise convention
  var gradBorder = new DvtLinearGradientStroke(grAngle, arColors, arAlphas, arRatios, arBounds);
  gradBorder.setWidth(1);

  // now create the arc for the border
  // only show border on the front-top-edge (180-360 degrees)
  var sliceAngleStart = slice.getAngleStart();
  var sliceAngleExtent = slice.getAngleExtent();

  var diff = (sliceAngleStart < 180) ? 180 - sliceAngleStart : 0;
  var angStart = (diff > 0) ? 180 : sliceAngleStart;
  var angExtent = sliceAngleExtent - diff;

  if (angStart + angExtent > 360)
    angExtent = 360 - angStart;

  var pieChart = slice.getPieChart();
  var pieCenter = pieChart.getCenter();

  var edge = new DvtArc(pieChart.getCtx(), pieCenter.x, pieCenter.y, pieChart.getRadiusX(), pieChart.getRadiusY(), angStart, angExtent, DvtArc.OPEN);

  edge.setStroke(gradBorder);

  return edge;
}

/**
 * Private method that generates any lateral (non-top) pie surface
 *
 * @param {DvtFill} fill The fill for the lateral surface
 * @param {DvtPieSlice} slice
 * @param {number} pathType One of DvtPieSlice.SURFACE_CRUST,
 *                          DvtPieSlice.SURFACE_LEFT, or DvtPieSlice.SURFACE_RIGHT
 *
 * @return {Array} An array of DvtShape objects representing this lateral surface
 *
 * @private
 */
// replaces PieSlice._draw 
DvtPieSlice._createLateralSurface = function (slice, pathType, fill) {
  // handle the case where we are animating a slice insert
  // initially, this slice will have 0 extent. in this case
  // don't generate any surface
  if (slice.getAngleExtent() == 0) {
    return [];
  }

  var talpha = slice.getSliceInfo().getFillAlpha();
  var shapes = [];

  if (talpha > 0) {

    if (pathType == DvtPieSlice.SURFACE_LEFT || pathType == DvtPieSlice.SURFACE_RIGHT) {
      shapes.push(DvtPieSlice._generateLateralShape(slice, pathType, null, fill));
    }
    else if (pathType == DvtPieSlice.SURFACE_CRUST) {
      var pathCommands = DvtPieSlice._createCrustPathCommands(slice);

      var len = pathCommands.length;
      for (var i = 0;i < len;i++) {
        shapes.push(DvtPieSlice._generateLateralShape(slice, pathType, pathCommands[i], fill));
      }

    }
  }

  // Associate the shapes with the slice for use during event handling
  DvtPieSlice._associate(slice, shapes);

  return shapes;
}

/**
 * Create the gradient fill used for lateral surfaces.
 * @param {DvtPieSlice} slice
 * @param {String} objType One of DvtPieRenderUtils.CRUST or DvtPieRenderUtils.SIDE
 * @return {DvtLinearGradientFill}
 * @private
 */
DvtPieSlice._generateLateralGradientFill = function (slice, objType) {
  var pieChart = slice.getPieChart();
  var sliceInfo = slice.getSliceInfo();
  var skin = pieChart.getPieInfo().getSkin();
  var yOffset = (objType == DvtPieRenderUtils.CRUST) ? pieChart.getDepth() : 0;

  var angle = (skin == DvtCSSStyle.SKIN_SKYROS) ? 0 : 270;
  var arColors = DvtPieRenderUtils.getGradientColors(sliceInfo.getFillColor(), objType, skin);
  var arAlphas = DvtPieRenderUtils.getGradientAlphas(sliceInfo.getFillAlpha(), objType);
  var arRatios = DvtPieRenderUtils.getGradientRatios(objType, skin);
  var arBounds = null;
  if (skin == DvtCSSStyle.SKIN_SKYROS) {
    arBounds = [Math.floor(pieChart.getCenter().x - pieChart.getRadiusX()), 
                Math.floor(pieChart.getCenter().y - pieChart.getRadiusY()) + yOffset, 
                Math.ceil(2 * pieChart.getRadiusX()), 
                Math.ceil(2 * pieChart.getRadiusY())];
  }

  return new DvtLinearGradientFill(angle, arColors, arAlphas, arRatios, arBounds);
}

/**
 * Private method that generates an array of DvtShape objects for different lateral pie surfaces
 *
 * @param {DvtPieSlice} slice
 * @param {number} pathType One of DvtPieSlice.SURFACE_CRUST,
 *                          DvtPieSlice.SURFACE_LEFT, or DvtPieSlice.SURFACE_RIGHT
 * @param {String} pathCommand  A string of SVG commands in SVG "d" attribute format. Used when pathType is
 *                              DvtPieSlice.SURFACE_CRUST. Can be set to null otherwise
 * @param {DvtFill} fill The fill to apply to the shapes
 *
 * @return {DvtShape} A right or left pie surface, or a piece of a crust, as described in pathCommands
 *
 * @private
 */
DvtPieSlice._generateLateralShape = function (slice, pathType, pathCommand, fill) {
  var pie = slice.getPieChart();
  var context = pie.getCtx();
  var pieInfo = pie.getPieInfo();
  var sliceInfo = slice.getSliceInfo();

  // left side points and right side points
  if (pathType == DvtPieSlice.SURFACE_LEFT || pathType == DvtPieSlice.SURFACE_RIGHT) {
    var angle = slice.getAngleStart();
    var arc = slice.getAngleExtent();
    var xCenter = pie.getCenter().x;
    var yCenter = pie.getCenter().y;
    var xRadius = slice._radiusX;
    var yRadius = slice._radiusY;
    var depth = pie.getDepth();

    var pt = (pathType == DvtPieSlice.SURFACE_LEFT) ? DvtPieRenderUtils.reflectAngleOverYAxis(angle + arc, xCenter, yCenter, xRadius, yRadius) : 
                                                      DvtPieRenderUtils.reflectAngleOverYAxis(angle, xCenter, yCenter, xRadius, yRadius);
    var pointArray = DvtPieSlice._generateInnerPoints(xCenter, yCenter, pt.x, pt.y, depth);
    
    var points = [];
    for (var i=0; i<pointArray.length; i++) {
      points.push(pointArray[i].x, pointArray[i].y);
    }

    var polygon = new DvtGraphSelectablePolygon(context, points);
    polygon.setDataColor(sliceInfo.getFillColor(), pieInfo.getSelectedInnerColor(), pieInfo.getSelectedOuterColor());
    polygon.setZOrderManager(pie);
    polygon.setTranslateX(slice.__getExplodeOffsetX());
    polygon.setTranslateY(slice.__getExplodeOffsetY());

    polygon.setFill(fill);
    if(sliceInfo.getStrokeColor())
      polygon.setSolidStroke(sliceInfo.getStrokeColor(), sliceInfo.getStrokeAlpha());

    return polygon;
  }
  else // draw piece of pie crust
  {
    if (pathCommand) {
      var path = new DvtGraphSelectablePath(context, null);
      path.setDataColor(sliceInfo.getFillColor(), pieInfo.getSelectedInnerColor(), pieInfo.getSelectedOuterColor());
      path.setZOrderManager(pie);
      path.setCmds(pathCommand);
      path.setTranslateX(slice.__getExplodeOffsetX());
      path.setTranslateY(slice.__getExplodeOffsetY());

      path.setFill(fill);
      if (sliceInfo.getStrokeColor()) {
        path.setSolidStroke(sliceInfo.getStrokeColor(), sliceInfo.getStrokeAlpha());
      }

      return path;
    }
  }

  return null;
}

/**
 * Returns an array of path commands describing how to draw a pie crust
 *
 * @param {DvtPieSlice} slice
 *
 * @return {Array} An array of strings of SVG commands in SVG "d" attribute format.
 *                 e.g., [ [command1 x1, y1, ..., commandi xn, yn, ...], [commandj xs, ys, ...] ]
 *
 * @private
 */
DvtPieSlice._createCrustPathCommands = function (slice) {
  var angle = slice.getAngleStart();
  var arc = slice.getAngleExtent();
  var angleEnd = angle + arc;
  var pie = slice.getPieChart();
  var xCenter = pie.getCenter().x;
  var yCenter = pie.getCenter().y;
  var xRadius = slice._radiusX;
  var yRadius = slice._radiusY;
  var depth = pie.getDepth();

  // If slice crosses 0 degrees (right horizontal x-axis), we need to break crust into 2 pieces joined at the crossing 
  // point so that the right side of the slice appears to be a solid 3D wall. If slice crosses 180 degrees (left 
  // horizontal x-axis), we need to break crust into 2 pieces joined at the crossing point so that the left side of the 
  // slice appears to be a solid 3D wall.
  var arOuterPath = [];
  if (angle < 180.0 && angleEnd > 360.0) {
    arOuterPath.push(DvtPieSlice._makeOuterPath(xCenter, yCenter, xRadius, yRadius, depth, angle, 180.0 - angle)); // left
    arOuterPath.push(DvtPieSlice._makeOuterPath(xCenter, yCenter, xRadius, yRadius, depth, 360.0, angleEnd - 360.0)); // right
    arOuterPath.push(DvtPieSlice._makeOuterPath(xCenter, yCenter, xRadius, yRadius, depth, 180.0, 180.0)); // center
  }
  else if (angleEnd > 360.0) {
    arOuterPath.push(DvtPieSlice._makeOuterPath(xCenter, yCenter, xRadius, yRadius, depth, angle, 360.0 - angle));
    arOuterPath.push(DvtPieSlice._makeOuterPath(xCenter, yCenter, xRadius, yRadius, depth, 360.0, angleEnd - 360.0));
  }
  else if (angle < 180.0 && angleEnd > 180.0) {
    arOuterPath.push(DvtPieSlice._makeOuterPath(xCenter, yCenter, xRadius, yRadius, depth, angle, 180.0 - angle));
    arOuterPath.push(DvtPieSlice._makeOuterPath(xCenter, yCenter, xRadius, yRadius, depth, 180.0, angleEnd - 180.0));
  }
  else 
    arOuterPath.push(DvtPieSlice._makeOuterPath(xCenter, yCenter, xRadius, yRadius, depth, angle, arc));

  return arOuterPath;
}

/**
 * Returns the path string for a segment of the outer crust of a pie slice.
 * @param {number} cx The x coordinate of the center of the pie.
 * @param {number} cy The y coordinate of the center of the pie.
 * @param {number} rx The radius of the pie.
 * @param {number} ry The radius of the pie.
 * @param {number} depth The depth of the pie.
 * @param {number} startAngle The start angle in degrees.
 * @param {number} angleExtent The angular extent in degrees.  Always less than 180 degrees (half the pie).
 * @returns {String} An SVG string that represents part of the crust.
 * @private
 */
DvtPieSlice._makeOuterPath = function (cx, cy, rx, ry, depth, startAngle, angleExtent) {
  var angleExtentRads = DvtMath.degreesToRads(angleExtent);
  var endAngleRads = -(DvtMath.degreesToRads(startAngle)+angleExtentRads);
  
  // Adjust the cy to reduce rendering artifacts between the top and crust
  cy -= 1;
  
  // Calculate the start and end points on the top curve
  var startPointTop = DvtPieRenderUtils.reflectAngleOverYAxis(startAngle, cx, cy, rx, ry);
  var endPointTopX = cx + Math.cos(endAngleRads) * rx;
  var endPointTopY = cy + Math.sin(endAngleRads) * ry;
  
  // Top Curve
  var pathCommands = DvtPathUtils.moveTo(startPointTop.x, startPointTop.y);
  pathCommands += DvtPathUtils.arcTo(rx, ry, angleExtentRads, 0, endPointTopX, endPointTopY);

  // Line to Bottom Curve
  pathCommands += DvtPathUtils.lineTo(endPointTopX, endPointTopY + depth);
  
  // Bottom Curve
  pathCommands += DvtPathUtils.arcTo(rx, ry, angleExtentRads, 1, startPointTop.x, startPointTop.y + depth);
  
  // Line to Top Curve
  pathCommands += DvtPathUtils.lineTo(startPointTop.x, startPointTop.y);
  
  return pathCommands;
}

/**
 * Private function to generate the points for the left or right pie surface
 *
 * @param {number} cx The x-coordinate of the center of the pie slice
 * @param {number} cy The y-coordinate of the center of the pie slice
 * @param {number} x The x-coordinate of the top, outside (left or right) edge of the pie slice
 * @param {number} y The y-coordinate of the top, outside (left or right) edge of the pie slice
 * @param {number} tilt Pie tilt
 * @param {number} side Either DvtPieSlice.SURFACE_LEFT or DvtPieSlice.SURFACE_RIGHT
 *
 * @return {Array} An array of points that are the coordinates for the left or right surface of a pie slice
 *
 * @private
 */
DvtPieSlice._generateInnerPoints = function (cx, cy, xpos, ypos, tilt) {
  var pointArray = [];
  pointArray.push({x: cx, y: cy});
  pointArray.push({x: xpos, y: ypos});
  pointArray.push({x: xpos, y: ypos + tilt});
  pointArray.push({x: cx, y: cy + tilt});
  return pointArray;
}
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/* Class DvtSliceInfo       Slice information from the parser          */
/*---------------------------------------------------------------------*/

/**
 * Property bag to hold slice information passed to the pie from the middle tier
 *
 * @constructor
 */
var DvtSliceInfo = function () {
  this.Init();
};

DvtObj.createSubclass(DvtSliceInfo, DvtObj, "DvtSliceInfo");

/**
 * Private helper function to initialize the slice info's private fields
 *
 * @protected
 */
DvtSliceInfo.prototype.Init = function () {
  this._explode = 0;

  this._id = null;// String
  this._index = null;// int
  this._fillColor = null;// String
  this._fillAlpha = null;
  this._fp = null;// String for fill pattern
  this._strokeColor = null;
  this._strokeAlpha = null;
  this._value = null;
  this._tooltip = null;// tooltip string
  this._contextMenuId = null;

  this._text = null;// custom marker text returned by CustomMarkerTextCallback. 
  // if not found, then pie specific series label.
  // if that is not found, then the series label.
  // if that is not found, then the series id
  this._action = null;
};

/**
 * Returns the slice text from the parsed xml.  This text is the custom marker text
 * from a CustomMarkerTextCallback.  If not found, then returns the pie specific
 * series label for the specified series index.  If not found, returns the series label,
 * or if not found the series ID is returned.
 *
 * @return {String}
 */
DvtSliceInfo.prototype.getText = function () {
  return this._text;
}

/**
 * Sets the slice text from the parsed xml.  This text is the custom marker text
 * from a CustomMarkerTextCallback.  If not found, then the pie specific
 * series label.  If not found, then the series label, or if not found then series ID
 *
 * @param {String} text
 */
DvtSliceInfo.prototype.setText = function (text) {
  this._text = text;
}

/**
 * Returns the explode value for this pie slice, as sent from the middle tier
 *
 * @return {number}
 */
DvtSliceInfo.prototype.getExplode = function () {
  return this._explode;
}

/**
 * Sets the explode value for this pie slice, as sent from the middle tier
 *
 * @param {number} explode
 */
DvtSliceInfo.prototype.setExplode = function (explode) {
  this._explode = explode;
}

/**
 * Returns the opacity of this pie slice
 *
 * @return {number}
 */
DvtSliceInfo.prototype.getFillAlpha = function () {
  return this._fillAlpha;
}

/**
 * Sets the opacity of this pie slice
 *
 * @param {number} clr
 */
DvtSliceInfo.prototype.setFillAlpha = function (alpha) {
  this._fillAlpha = alpha;
}

/**
 * Returns the color of this pie slice, represented as a String
 *
 * @return {String}
 */
DvtSliceInfo.prototype.getFillColor = function () {
  return this._fillColor;// String
}

/**
 * Sets the color of this pie slice
 *
 * @param {String} clr
 */
DvtSliceInfo.prototype.setFillColor = function (clr) {
  this._fillColor = clr;// String
}

/**
 * Returns the name of the fill pattern for this pie slice
 *
 * @return {string}
 */
DvtSliceInfo.prototype.getFillPattern = function () {
  return this._fp;
}

/**
 * Sets the name of the fill pattern for this pie slice
 *
 * @param {string} fillPattern
 */
DvtSliceInfo.prototype.setFillPattern = function (fillPattern) {
  this._fp = fillPattern;
}

/**
 * Returns the series id object
 *
 * @return {Object}
 */
DvtSliceInfo.prototype.getId = function () {
  return this._id;
}

/**
 * Sets the index of this pie slice
 *
 * @param {int} index
 */
DvtSliceInfo.prototype.setIndex = function (index) {
  this._index = index;
}

/**
 * Returns the index
 *
 * @return {int}
 */
DvtSliceInfo.prototype.getIndex = function () {
  return this._index;
}

/**
 * Sets the series id object associated with this pie slice
 *
 * @param {Object} series
 */
DvtSliceInfo.prototype.setId = function (series) {
  this._id = series;
}

/**
 * Returns the opacity of this pie slice border
 *
 * @return {number}
 */
DvtSliceInfo.prototype.getStrokeAlpha = function () {
  return this._strokeAlpha;
}

/**
 * Sets the opacity of this pie slice border
 *
 * @param {number} clr
 */
DvtSliceInfo.prototype.setStrokeAlpha = function (alpha) {
  this._strokeAlpha = alpha;
}

/**
 * Returns the color of this pie slice border
 *
 * @return {String}
 */
DvtSliceInfo.prototype.getStrokeColor = function () {
  return this._strokeFill;// String
}

/**
 * Sets the color of this pie slice border
 *
 * @param {String} clr
 */
DvtSliceInfo.prototype.setStrokeColor = function (clr) {
  this._strokeFill = clr;// String
}

/**
 * Returns the tooltip string associated with this slice
 *
 * @return {String}
 */
DvtSliceInfo.prototype.getTooltip = function () {
  return this._tooltip;
}

/**
 * Sets the tooltip string associated with this slice
 *
 * @param {String} tooltip
 */
DvtSliceInfo.prototype.setTooltip = function (tooltip) {
  this._tooltip = tooltip;
}

/**
 * Returns the context menu id for this slice.
 * @return {String}
 */
DvtSliceInfo.prototype.getContextMenuId = function () {
  return this._contextMenuId;
}

/**
 * Specifies the context menu id for this slice.
 * @param {String} id
 */
DvtSliceInfo.prototype.setContextMenuId = function (id) {
  this._contextMenuId = id;
}

/**
 * Returns the numeric data value associated with this slice
 *
 * @return {number}
 */
DvtSliceInfo.prototype.getValue = function () {
  return this._value;
}

/**
 * Sets the numeric data value associated with this slice
 *
 * @param {number} val
 */
DvtSliceInfo.prototype.setValue = function (val) {
  this._value = val;
}

/**
 * Return the action string for the data item, if any exists.
 * @return {string} the action outcome for the data item.
 */
DvtSliceInfo.prototype.getAction = function () {
  return this._action
}

/**
 * Specifies the action string for the data item, if any exists.
 * @param {string} action the action outcome for the data item.
 */
DvtSliceInfo.prototype.setAction = function (action) {
  this._action = action;
}

/**
 * Returns the popup behaviors for this pie.
 * @return {array}
 */
DvtSliceInfo.prototype.getShowPopupBehaviors = function () {
  return this._popupBehaviors;
}

/**
 * Specifies the popup behaviors for this pie.
 * @param {array} popupBehaviors
 */
DvtSliceInfo.prototype.setShowPopupBehaviors = function (popupBehaviors) {
  this._popupBehaviors = popupBehaviors;
}
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/* Class DvtSliceLabelInfo       Slice label information               */
/*---------------------------------------------------------------------*/

/** A property bag used to pass around information used for label placement
 *
 * @constructor
 */
var DvtSliceLabelInfo = function () {
  this._init();
};

DvtObj.createSubclass(DvtSliceLabelInfo, DvtObj, "DvtSliceLabelInfo");

/**
 * Private initializer
 * @private
 */
DvtSliceLabelInfo.prototype._init = function () {
  this._sliceLabel = null;// instance of DvtTextArea
  this._slice = null;// DvtSlice we will associate _sliceLabel with, if we can fit the label
  this._angle =  - 1;

  // this._position is the normalized midpoint angle, where 0 degrees is at 12 o'clock
  //    and angular measures are degrees away from 12 o'clock (so 90 degrees
  //    can be either at 3 o'clock or 9 o'clock on the unit circle)
  this._position =  - 1;
  this._width =  - 1;
  this._height =  - 1;
  this._x =  - 1;
  this._y =  - 1;

  this._initialNumLines =  - 1;

  this._hasFeeler = false;

  this._maxY =  - 1;
  this._minY =  - 1;
};

/**
 * @return {number} Angle of the text in this slice label
 */
DvtSliceLabelInfo.prototype.getAngle = function () {
  return this._angle;
}

/**
 * @param {number} angle Sets the angle of the text in this slice label
 */
DvtSliceLabelInfo.prototype.setAngle = function (angle) {
  this._angle = angle;
}

/**
 * @return {number} The height of this slice label
 */
DvtSliceLabelInfo.prototype.getHeight = function () {
  return this._height;
}

/**
 * @param {number} height The height of this slice label
 */
DvtSliceLabelInfo.prototype.setHeight = function (height) {
  this._height = height;
}

/**
 * @return {number}
 */
DvtSliceLabelInfo.prototype.getInitialNumLines = function () {
  return this._initialNumLines;
}

/**
 * @param {number} numLines
 */
DvtSliceLabelInfo.prototype.setInitialNumLines = function (numLines) {
  this._initialNumLines = numLines;
}

/**
 * @return {number} The maximum Y position of this slice label
 */
DvtSliceLabelInfo.prototype.getMaxY = function () {
  return this._maxY;
}

/**
 * @param {number} The maximum Y position of this slice label
 */
DvtSliceLabelInfo.prototype.setMaxY = function (maxY) {
  this._maxY = maxY;
}

/**
 * @return {number} The minimum Y position of this slice label
 */
DvtSliceLabelInfo.prototype.getMinY = function () {
  return this._minY;
}

/**
 * @param {number} The minimum Y position of this slice label
 */
DvtSliceLabelInfo.prototype.setMinY = function (minY) {
  this._minY = minY;
}

/**
 * bound the value of y within minY and maxY
 * assumes that maxY > minY
 * @param {number} a y value
 */
DvtSliceLabelInfo.prototype.boundY = function (y) {
  y = Math.max(y, this._minY);
  y = Math.min(y, this._maxY);
  return y;
}

/**
 * @return {boolean}
 */
DvtSliceLabelInfo.prototype.hasFeeler = function () {
  return this._hasFeeler;
}

/**
 * @param {boolean}
 */
DvtSliceLabelInfo.prototype.setHasFeeler = function (hasFeeler) {
  this._hasFeeler = hasFeeler;
}

/**
 * Returns the normalized midpoint angle, where 0 degrees is at 12 o'clock
 * and angular measures are degrees away from 12 o'clock (so 90 degrees
 * can be either at 3 o'clock or 9 o'clock on the unit circle)
 *
 * @return {number}
 */
DvtSliceLabelInfo.prototype.getPosition = function () {
  return this._position;
}

/**
 * Sets the normalized midpoint angle, where 0 degrees is at 12 o'clock
 * and angular measures are degrees away from 12 o'clock (so 90 degrees
 * can be either at 3 o'clock or 9 o'clock on the unit circle)
 *
 * @param {number} position
 */
DvtSliceLabelInfo.prototype.setPosition = function (position) {
  this._position = position;
}

/**
 * The slice that we want to associate the label with
 *
 * @return {DvtPieSlice}
 */
DvtSliceLabelInfo.prototype.getSlice = function () {
  return this._slice;
}

/**
 * @param {DvtPieSlice} slice
 */
DvtSliceLabelInfo.prototype.setSlice = function (slice) {
  this._slice = slice;
}

/**
 * The DvtTextArea associated with this SliceLabelInfo
 *
 * @return {DvtTextArea}
 */
DvtSliceLabelInfo.prototype.getSliceLabel = function () {
  return this._sliceLabel;
}

/**
 * Sets the DvtTextArea this label info will layout
 *
 * @param {DvtTextArea} label
 */
DvtSliceLabelInfo.prototype.setSliceLabel = function (label) {
  this._sliceLabel = label;
}

/**
 * @return {number} The width of this label
 */
DvtSliceLabelInfo.prototype.getWidth = function () {
  return this._width;
}

/**
 * @param {number} width
 */
DvtSliceLabelInfo.prototype.setWidth = function (width) {
  this._width = width;
}

/**
 * @return {number} The x-coordinate of the reference point for this label
 */
DvtSliceLabelInfo.prototype.getX = function () {
  return this._x;
}

/**
 * @param {number} x
 */
DvtSliceLabelInfo.prototype.setX = function (x) {
  this._x = x;
}

/**
 * @return {number} The y-coordinate of hte reference point for this label
 */
DvtSliceLabelInfo.prototype.getY = function () {
  return this._y;
}

/**
 * @param {number} y
 */
DvtSliceLabelInfo.prototype.setY = function (y) {
  this._y = y;
}
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*   DvtSliceLabelLayout                                               */
/*---------------------------------------------------------------------*/

/**
 * Class that creates pie slice labels and lays these labels out for a given pie chart
 *
 * @constructor
 * @param {DvtPieChart} pieChart
 */
var DvtSliceLabelLayout = function (pieChart) {
  this._init(pieChart);
}

DvtObj.createSubclass(DvtSliceLabelLayout, DvtObj, "DvtSliceLabelLayout");

// used for setting up font metrics
DvtSliceLabelLayout._FONT_METRIC_CHAR = "M";
DvtSliceLabelLayout._FEELER_MINLENGTH = 9;
DvtSliceLabelLayout._ISA_FULL_RATIO = 0.9;

DvtSliceLabelLayout._MAX_LINES_PER_LABEL = 3;

DvtSliceLabelLayout._COLLISION_MARGIN = 1;

DvtSliceLabelLayout._LEFT_SIDE_LABELS = 1;
DvtSliceLabelLayout._RIGHT_SIDE_LABELS = 2;

DvtSliceLabelLayout._LABEL_LEADING =  - 1.8;// space between lines
// BUGFIX 12403570
// when calculating angles, we sometimes lose accuracy due to rounding, so introduce this tolerance
DvtSliceLabelLayout._ROUNDING_TOLERANCE = 0.0001;

//constants for column layout
DvtSliceLabelLayout._FEELER_RAD_MINSIZE = 0.1;// ratio to the pie diameter
DvtSliceLabelLayout._FEELER_HORIZ_MINSIZE = 0.1;// ratio to the pie diameter
DvtSliceLabelLayout._LABEL_TO_FEELER_OFFSET = 0.5;// ratio to the label height
DvtSliceLabelLayout._LABEL_TO_FEELER_DISTANCE = 3;// in pixels
DvtSliceLabelLayout._NO_COLLISION = 0;
DvtSliceLabelLayout._HALF_COLLISION = 1;
DvtSliceLabelLayout._ALL_COLLISION = 2;

/**
 * Private intialization method
 *
 * @param {DvtPieChart} pieChart The pie chart to perform label layout on.
 *
 * @private
 */
DvtSliceLabelLayout.prototype._init = function (pieChart) {
  this._pieChart = pieChart;

  // ISA spacing parameters (ISA = ideal space available)
  this._isaTop;
  this._isaBottom;
  this._isaLeft;
  this._isaRight;
  this._isaHeight;
  this._isaXRadius;
  this._isaYRadius;

  this._avgHeightFont =  - 1;
}

/**
 * Public entry point called by DvtPieChart to layout the pie chart's labels and feelers.
 */
DvtSliceLabelLayout.prototype.layoutLabelsAndFeelers = function () {
  var labelPosition = this._pieChart.getPieInfo().getLabelPosition();
  if (labelPosition == DvtPieChart.LABEL_POS_NONE) {
    return;
  }
  else if (labelPosition == DvtPieChart.LABEL_POS_INSIDE) {
    this._layoutInsideLabels();
  }

  else {
    this._layoutOutsideLabelsAndFeelers();
  }
}

/**
 *
 * Lays out labels that appear within the pie slices.
 *
 * @private
 */
DvtSliceLabelLayout.prototype._layoutInsideLabels = function () {
  if (this._pieChart == null)
    return;

  slices = this._pieChart.__getSlices();
  var n = slices.length;

  if (n > 0) {
    for (i = 0;i < n;i++) {
      var slice = slices[i];
      var midPt;
      var midAngle = slice.getAngleStart() + (slice.getAngleExtent() / 2);
      var center = this._pieChart.getCenter();
      var posX = 0;
      var posY = 0;

      var sliceLabel = DvtSliceLabelLayout._createLabel(slice);
      
      if (n==1) {
        // Center the label
        posX = center.x;
        posY = center.y;
      }
      else {
        // The flash Graph tries to center the label manually.
        // With the toolkit, we can just set the alignment on the label
        // to center, middle
        var offset = 0.66;// Constant originally used in flash impl
        midPt = DvtPieRenderUtils.reflectAngleOverYAxis(midAngle, center.x, center.y, this._pieChart.getRadiusX() * offset, this._pieChart.getRadiusY() * offset);
  
        posX = midPt.x;
        posY = midPt.y;
      }

      sliceLabel.setX(posX);
      sliceLabel.setY(posY);
      sliceLabel.alignMiddle();
      sliceLabel.alignCenter();
      
      // Truncate the label to fit the pie
      sliceLabel.truncateToCallback(this._pieChart, DvtPieSlice.containsRect, slice);

      slice.setSliceLabel(sliceLabel);
    }
  }
}


/**
 *
 * Lays out labels (and feelers if necessary) that appear outside the pie slices
 *
 * @private
 */
DvtSliceLabelLayout.prototype._layoutOutsideLabelsAndFeelers = function () {

  if (this._pieChart == null)
    return;

  var leftLabels = [];
  var rightLabels = [];

  var slice;
  var i;

  var txtArea;// an instance of DvtTextArea 
  // get first label for some text metrics        
  var slices = this._pieChart.__getSlices();
  slice = slices[0];
  txtArea = DvtSliceLabelLayout._createLabel(slice);

  txtArea.setText(DvtSliceLabelLayout._FONT_METRIC_CHAR);

  var tempPt = DvtSliceLabelLayout._getTextDimension(this._pieChart, txtArea,  - 1,  - 1,  - 1);

  this._avgHeightFont = tempPt.y;

  // The magic numbers below, 0.75 and 0.6, we also used in the original Flash impl
  var feelerSpacing = this._avgHeightFont * 0.75 + 0.6 * this._pieChart.__getMaxExplodePixels();

  if (feelerSpacing < DvtSliceLabelLayout._FEELER_MINLENGTH) {
    feelerSpacing = DvtSliceLabelLayout._FEELER_MINLENGTH;
  }

  // setup an ISA - Ideal Space Available
  this._isaXRadius = this._pieChart.getRadiusX();// + feelerSpacing ;
  this._isaYRadius = this._pieChart.getRadiusY();// + feelerSpacing ; 
  var pieFrame = this._pieChart.__getFrame();

  this._isaLeft = pieFrame.x;
  this._isaRight = pieFrame.x + pieFrame.w;
  this._isaTop = pieFrame.y;
  this._isaBottom = pieFrame.y + pieFrame.h;

  this._isaHeight = this._isaBottom - this._isaTop;

  // -----------------------------------------------------------
  // Build arrays of Left side and Right side Labels
  //
  // When computing the positioning of the labels, we consider 
  // angles to be measured from the 12 o'clock position, 
  // i.e., 12 o'clock is 0 degrees.
  // Angular measurements then range from 0 to 180.  
  // A value of 90 degrees can then be either at the
  // 3 o'clock position or at the 9 o'clock position
  // -----------------------------------------------------------
  this._originalLabels = [];
  var alabels = this._generateInitialLayout();

  leftLabels = alabels[0];
  rightLabels = alabels[1];

  // -----------------------------------------------------------
  // Evaluate initial label layout from generateInitialLayout
  // -----------------------------------------------------------
  var leftColl = this._refineInitialLayout(leftLabels, DvtSliceLabelLayout._LEFT_SIDE_LABELS);
  var rightColl = this._refineInitialLayout(rightLabels, DvtSliceLabelLayout._RIGHT_SIDE_LABELS);
  if (leftColl == DvtSliceLabelLayout._HALF_COLLISION && rightColl != DvtSliceLabelLayout._NO_COLLISION) {
    this._columnLabels(leftLabels, true, true, true);
  }
  if (leftColl != DvtSliceLabelLayout._NO_COLLISION && rightColl == DvtSliceLabelLayout._HALF_COLLISION) {
    this._columnLabels(rightLabels, false, true, true);
  }

  //
  // Left Side Labels ----------------------------------
  //
  this._setLabelsAndFeelers(leftLabels, DvtSliceLabelLayout._LEFT_SIDE_LABELS);

  for (i = 0;i < leftLabels.length;i++)
    leftLabels[i] = null;

  leftLabels = null;

  //
  // Right Side Labels ---------------------------------
  //          
  this._setLabelsAndFeelers(rightLabels, DvtSliceLabelLayout._RIGHT_SIDE_LABELS);

  for (i = 0;i < rightLabels.length;i++)
    rightLabels[i] = null;

  rightLabels = null;

  // clean up
  alabels[0] = null;
  alabels[1] = null;
  alabels = null;

}

/**
 * Create a label for the given pie slice. Label positioning is done elsewhere
 * @param {DvtPieSlice} slice
 * @return {DvtTextArea}
 * @private
 */
DvtSliceLabelLayout._createLabel = function (slice) {
  var pieChart = slice.getPieChart();
  var pieInfo = pieChart.getPieInfo();

  if (pieInfo.getLabelPosition() == DvtPieChart.LABEL_POS_NONE) {
    return null;
  }

  var context = pieChart.getCtx();

  var sliceLabel = new DvtTextArea(context, 0, 0);// In Flash, the fix for bug 7593102 was to create another
  // label class to maintain truncated label state. We don't
  // need that in html 5
  sliceLabel.alignTop();
  sliceLabel.setWordWrap(true);

  // If color is not defined, apply a default color
  var style = pieInfo.getCSSStyle();
  if(style) {
    var styleStr = style.toString();
    if(styleStr.indexOf('color') == -1) {
      var defaultColor = pieInfo.getDefaultLabelColor();
      
      // Default color for interior label is based on slice color
      if(pieInfo.getLabelPosition() == DvtPieChart.LABEL_POS_INSIDE) {
        var backgroundColor = slice.getSliceInfo().getFillColor();
        defaultColor = DvtColorUtils.getContrastingTextColor(backgroundColor);
      }
      
      // If a default color is found, apply it
      styleStr += "color: " + defaultColor + ";";
      style = new DvtCSSStyle(styleStr);
    }
  }
  
  DvtSliceLabelLayout._setFontPropsOnLabel(style, sliceLabel);

  var labelStr = DvtPieSlice.generateSliceLabelString(slice);
  sliceLabel.setText(labelStr);
  slice.setSliceLabelString(labelStr);

  return sliceLabel;
}

/**
 * Helper function to set DvtCSSStyle on slice label, given a DvtFont object
 *
 * @param {DvtCSSStyle} style The CSS style to set the slice label text
 * @param {DvtTextArea} sliceLabel The slice label to set font properties on
 * @private
 */
DvtSliceLabelLayout._setFontPropsOnLabel = function (style, sliceLabel) {
  if (sliceLabel == null || style == null)
    return;

  sliceLabel.setCSSStyle(style);
}

/**
 *
 * Called after initial naive layout is generated to resolve label collisions
 *
 * @param {Array} labelInfoArray An array of DvtSliceLabelInfo objects
 * @param {number} side Either DvtSliceLabelLayout._LEFT_SIDE_LABELS or DvtSliceLabelLayout._RIGHT_SIDE_LABELS
 * @private
 */
DvtSliceLabelLayout.prototype._refineInitialLayout = function (labelInfoArray, side) {

  if (labelInfoArray && labelInfoArray.length > 0) {
    var lastY = this._isaTop;//think again!!
    var collisionTop = false;
    var collisionCentral = false;
    var collisionBottom = false;
    var labelBottom = 0;

    var labelInfo;

    var bottomQuarter = false;
    var prevBottomQuarter = bottomQuarter;
    var collide = false;
    var isLeftSideLabels = (side == DvtSliceLabelLayout._LEFT_SIDE_LABELS);

    for (var i = 0;i < labelInfoArray.length;i++) {
      labelInfo = labelInfoArray[i];

      prevBottomQuarter = bottomQuarter;
      if (labelInfo.getPosition() > 90) {
        bottomQuarter = true;
      }

      labelBottom = labelInfo.getY() + labelInfo.getHeight();

      collide = (lastY - labelInfo.getY()) > DvtSliceLabelLayout._COLLISION_MARGIN;

      if (collide) {
        if (!bottomQuarter) {
          collisionTop = true;
        }
        else if (bottomQuarter && !prevBottomQuarter) {
          collisionCentral = true;
        }
        else {
          collisionBottom = true;
        }
      }

      if (labelBottom > lastY) {
        lastY = labelBottom;
      }
    }

    if ((collisionTop && collisionBottom) || collisionCentral) {
      this._columnLabels(labelInfoArray, isLeftSideLabels, true, true);
      return DvtSliceLabelLayout._ALL_COLLISION;
    }
    else if (collisionTop) {
      this._columnLabels(labelInfoArray, isLeftSideLabels, true, false);//top only
      return DvtSliceLabelLayout._HALF_COLLISION;
    }
    else if (collisionBottom) {
      this._columnLabels(labelInfoArray, isLeftSideLabels, false, true);//bottom only
      return DvtSliceLabelLayout._HALF_COLLISION;
    }
    else {
      return DvtSliceLabelLayout._NO_COLLISION;
    }
  }
}

// ported over from PieChart.as, renderLabelsAndFeelers
/**
 * 
 * Sets the location of the DvtTextArea objects as specified in the DvtSliceLabelInfo objects in alabels.
 * When this method returns, the DvtPieChart labels corresponding to the DvtSliceLabelInfo objects in alabels 
 * will have their layout positions set, and will be ready to render
 * 
 * @param {Array} alabels An array of DvtSliceLabelInfo objects. 
 * @private 
 */
DvtSliceLabelLayout.prototype._setLabelsAndFeelers = function (alabels, side) {
  if (alabels == null || alabels.length <= 0)
    return;

  var i;
  var slice;// instance of DvtPieSlice
  var sliceLabel;// instance of DvtTextArea
  var labelInfo;// instance of DvtSliceLabelInfo 
  var isLeftSide = (side == DvtSliceLabelLayout._LEFT_SIDE_LABELS);
  
  var excessWidth = Infinity;
  var excessLength;
  
  // Determine how much the horizontal feelers can be shortened
  for (i = 0; i < alabels.length; i++) {
    labelInfo = alabels[i];
    slice = labelInfo.getSlice();
    if (labelInfo.hasFeeler()) {
      excessLength = DvtSliceLabelLayout._calculateFeeler(labelInfo, slice, isLeftSide);
      excessWidth = Math.min(excessWidth, excessLength);
    }
    else
      slice.setNoOutsideFeeler();
  }
  
  for (i = 0; i < alabels.length; i++) {
    labelInfo = alabels[i];
    slice = labelInfo.getSlice();
    sliceLabel = labelInfo.getSliceLabel();
                                
    if (labelInfo.hasFeeler()) {      
      // shorten the horizontal feelers
      if (isLeftSide) {
        labelInfo.setX(labelInfo.getX() + excessWidth);
      } else {
        labelInfo.setX(labelInfo.getX() - excessWidth);
      }
      // setup the feeler line (let it clip if needed)
      DvtSliceLabelLayout._calculateFeeler(labelInfo, slice, isLeftSide);
      // do not wrap text if column layout is used
      sliceLabel.setWordWrap(false);
    }

    sliceLabel.setTranslateY(labelInfo.getY());
    sliceLabel.setTranslateX(labelInfo.getX());
    
    // perform 'logical' clipping ourselves
    if ((labelInfo.getY() < this._isaTop) || (labelInfo.getY() + labelInfo.getHeight()) > this._isaBottom) {
      slice.setSliceLabel(null);
      slice.setNoOutsideFeeler(); // Bug fix 8358713 - don't show feelers if the label is 'clipped' (invisible)
    }
    else {
      this._truncateSliceLabel(slice, labelInfo, isLeftSide);
      slice.setSliceLabel(sliceLabel);
    }
  }
}

// replaces PieChart.drawFeeler
/**
 * 
 * Sets the feeler line that extends from the pie to the targetPt on the given slice. This method computes where
 * on the pie the feeler line should start, and then the start point and targetPt are set on the input slice.
 * 
 * @param {object} targetPt An object with x and y fields describing where the feeler point should extend to.
 * @param {DvtPieSlice} slice A DvtPieSlice object
 * @return The excess length of the horizontal feeler, i.e. the length of the horizontal feeler minus the minimum length
 * @private
 */
DvtSliceLabelLayout._calculateFeeler = function (labelInfo, slice, isLeft) {
  var pieChart = slice.getPieChart();

  var targetX = labelInfo.getX();
  var targetY = labelInfo.getY() + labelInfo.getHeight() * DvtSliceLabelLayout._LABEL_TO_FEELER_OFFSET;
  var minHorizLength = DvtSliceLabelLayout._FEELER_HORIZ_MINSIZE * pieChart.getRadiusX();

  var midX;
  if (isLeft) {
    targetX += DvtSliceLabelLayout._LABEL_TO_FEELER_DISTANCE;
    midX = targetX + minHorizLength;
  }
  else {
    targetX -= DvtSliceLabelLayout._LABEL_TO_FEELER_DISTANCE;
    midX = targetX - minHorizLength;
  }

  if (pieChart.getPieInfo().getLabelPosition() == DvtPieChart.LABEL_POS_OUTSIDE_FEELERS) {
    var startPt = {
      x : 0, y : 0
    };
    var midPt = {
      x : midX, y : targetY
    };
    var endPt = {
      x : targetX, y : targetY
    };

    var ma = labelInfo.getAngle();
    var tilt = DvtSliceLabelLayout._adjustForDepth(ma, pieChart.getDepth());

    startPt = DvtPieRenderUtils.reflectAngleOverYAxis(ma, pieChart.getCenter().x, pieChart.getCenter().y + tilt, pieChart.getRadiusX(), pieChart.getRadiusY());

    // make set the first section of the feeler radial if possible 
    var pa = DvtMath.degreesToRads(labelInfo.getPosition());
    var radFeelerAngle = Math.abs(Math.atan2(midPt.x - startPt.x, startPt.y - midPt.y));
    var horizOffset = (startPt.y - midPt.y) * Math.tan(pa);// * pieChart.getRadiusX() / pieChart.getRadiusY();
    if ((pa > Math.PI / 2 && radFeelerAngle > Math.PI / 2 && radFeelerAngle < pa) || (pa < Math.PI / 2 && radFeelerAngle < Math.PI / 2 && radFeelerAngle > pa)) {
      if (isLeft) {
        midPt.x = startPt.x - horizOffset;
      }
      else {
        midPt.x = startPt.x + horizOffset;
      }
    }

    //store outside feeler points on slice
    //and let slice draw the feeler so that we can 
    //easily redraw it when selecting
    slice.setOutsideFeelerPoints(startPt, midPt, endPt);
    return Math.abs(endPt.x - midPt.x) - minHorizLength;
  }
  
  return 0;
}

/**
 
 * Generates the offset of a label feeler to account for the depth of a 3d pie.
 *
 * @param {number} ma The angle on the unit circle from which the leaderline should originate from
 * @param {number} depth The pie chart's depth
 *
 * @return {number} The offset for the feeler line
 * @private
 */
DvtSliceLabelLayout._adjustForDepth = function (ma, pieDepth) {
  var depth = 0;
  var leftMidHi = 189;
  var rightMidHi = 351;

  if (ma > leftMidHi && ma < rightMidHi) {
    depth = pieDepth;
  }

  return depth;
}

/**
 *  Finds the label corresponding to the most horizontal slice
 *
 *  @param {Array} alabels An array of DvtSliceLabelInfo objects
 *  @private
 */
DvtSliceLabelLayout.prototype._getMiddleLabel = function (alabels) {
  bestAngle = 91;
  bestIndex =  - 1
  for (var i = 0;i < alabels.length;i++) {
    pa = alabels[i].getPosition();
    if (Math.abs(pa - 90) < bestAngle) {
      bestAngle = Math.abs(pa - 90);
      bestIndex = i
    }
  }
  return bestIndex;
}

/**
 *
 * @private
 */
DvtSliceLabelLayout.prototype._setOptimalLabelPosition = function (labelInfo, vertX, isLeft) {
  //set optimal X
  labelInfo.setX(vertX);

  //set optimal Y
  //  var a = this._pieChart.getRadiusX() * (1 + DvtSliceLabelLayout._FEELER_RAD_MINSIZE);
  var b = this._pieChart.getRadiusY() * (1 + DvtSliceLabelLayout._FEELER_RAD_MINSIZE);
  var angleInRad = DvtMath.degreesToRads(labelInfo.getPosition());
  //  var heightFromCenter = a*b*Math.cos(angleInRad) / 
  //      Math.sqrt(Math.pow(a*Math.cos(angleInRad), 2) + Math.pow(b*Math.sin(angleInRad), 2)); //ellipse equation 
  var heightFromCenter = b * Math.cos(angleInRad);
  var tilt = DvtSliceLabelLayout._adjustForDepth(labelInfo.getAngle(), this._pieChart.getDepth());
  var optimalY = this._pieChart.getCenter().y - heightFromCenter - labelInfo.getHeight() * DvtSliceLabelLayout._LABEL_TO_FEELER_OFFSET + tilt;
  labelInfo.setY(labelInfo.boundY(optimalY));
}

/**
 *  Adjusts the label locations by positioning the labels vertically in a column
 *
 *  @param {Array} alabels An array of DvtSliceLabelInfo objects
 *  @param {boolean} isLeft Boolean indicating if these labels are on the left side of the pie
 *  @private
 */
DvtSliceLabelLayout.prototype._columnLabels = function (alabels, isLeft, isTop, isBottom) {

  var minY = this._isaTop;
  var maxY = this._isaBottom;
  var i;
  var labelInfo;

  //determine the position where the column will be aligned
  var vertX = this._pieChart.getCenter().x;
  var minFeelerDist = this._pieChart.getRadiusX() * (1 + DvtSliceLabelLayout._FEELER_RAD_MINSIZE + DvtSliceLabelLayout._FEELER_HORIZ_MINSIZE);
  
  if (isLeft) {
    vertX -= minFeelerDist;
  }
  else {
    vertX += minFeelerDist;
  }

  //set the minimum heights that ensures as many labels as possible are displayed
  for (i = 0;i < alabels.length;i++) {
    alabels[i].setMinY(minY);
    minY += alabels[i].getHeight();
  }

  //set the maximum heights that ensures as many labels as possible are displayed
  for (i = alabels.length - 1;i >= 0;i--) {
    maxY -= alabels[i].getHeight();
    alabels[i].setMaxY(maxY);
  }

  if (alabels[0].getMinY() >= alabels[0].getMaxY()) {
    // not all labels will fit on the screen
    for (i = 0;i < alabels.length;i++) {
      // fit as many as possible
      labelInfo = alabels[i];
      labelInfo.setX(vertX);
      labelInfo.setY((labelInfo.getMinY() + labelInfo.getMaxY()) / 2);
      labelInfo.setHasFeeler(true);
    }
    return;
  }

  var startIndex = this._getMiddleLabel(alabels);
  var startLabel = alabels[startIndex];
  var isStartAtTop = startLabel.getPosition() <= 90;

  //if the column is only partial but there are too many labels, then set the whole side as column
  if (isTop && !isBottom) {
    if (startLabel.getMinY() + startLabel.getHeight() > this._pieChart.getCenter().y) {
      isBottom = true;
    }
  }
  if (isBottom && !isTop) {
    if (startLabel.getMaxY() < this._pieChart.getCenter().y) {
      isTop = true;
    }
  }

  //position the start label at the column if the angle is suitable
  if ((isTop && isStartAtTop) || (isBottom && !isStartAtTop)) {
    this._setOptimalLabelPosition(startLabel, vertX, isLeft);
    startLabel.setHasFeeler(true);
  }

  var highestY = startLabel.getY();
  var lowestY = startLabel.getY() + startLabel.getHeight();

  var optimalY;
  var labelHeight;

  if (isTop) {
    //labels above the start label
    for (i = startIndex - 1;i >= 0;i--) {
      labelInfo = alabels[i];
      labelHeight = labelInfo.getHeight();
      this._setOptimalLabelPosition(labelInfo, vertX, isLeft);
      labelInfo.setHasFeeler(true);

      //avoid collision with the label below
      optimalY = labelInfo.getY();
      if (optimalY + labelHeight < highestY) {
        highestY = optimalY;
      }
      else {
        highestY -= labelHeight;
      }
      labelInfo.setY(highestY);
    }
  }

  if (isBottom) {
    //labels below the start label
    for (i = startIndex + 1;i < alabels.length;i++) {
      labelInfo = alabels[i];
      labelHeight = labelInfo.getHeight();
      this._setOptimalLabelPosition(labelInfo, vertX, isLeft);
      labelInfo.setHasFeeler(true);

      //avoid collision with the label above
      optimalY = labelInfo.getY();
      if (optimalY > lowestY) {
        lowestY = optimalY + labelHeight;
      }
      else {
        lowestY += labelHeight;
      }
      labelInfo.setY(lowestY - labelHeight);
    }
  }
}

/**
 *
 * Truncates the label for the last time after the final X position is calculated
 *
 * @param {DvtPieSlice} slice
 * @param {DvtSliceLabelInfo} labelInfo
 * @param {boolean} isLeft Boolean indicating whether or not this slice is on the left side of the pie
 *
 * @return {boolean} True if the height is modified after truncation, false otherwise
 * @private
 */

DvtSliceLabelLayout.prototype._truncateSliceLabel = function (slice, labelInfo, isLeft) {
  var textArea = labelInfo.getSliceLabel();
  var style = textArea.getCSSStyle();
  var maxLabelWidth = 0;
  var tmDimPt;

  // before setting the DvtTextArea, make sure it is added to the DOM
  // necessary because the DvtTextArea will try to wrap, and to do that, 
  // it needs to get the elements dimensions, which it can only do if it's
  // added to the DOM
  var numChildren = this._pieChart.getNumChildren();
  var removeTextArea = false;
  if (!this._pieChart.contains(textArea)) {
    this._pieChart.addChild(textArea);
    removeTextArea = true;
  }

  textArea.setCSSStyle(style);
  var labelStr = slice.getSliceLabelString();
  textArea.setText(labelStr);

  if (removeTextArea) {
    this._pieChart.removeChildAt(numChildren);
  }

  if (isLeft) {
    maxLabelWidth = labelInfo.getX() - this._isaLeft;
  }
  else {
    maxLabelWidth = this._isaRight - labelInfo.getX();
  }

  // truncates with larger space
  tmDimPt = DvtSliceLabelLayout._getTextDimension(this._pieChart, textArea, maxLabelWidth, -1, labelInfo.getInitialNumLines());

  // Update labelinfo
  labelInfo.setWidth(tmDimPt.x);

  if (labelInfo.getHeight() != tmDimPt.y) {
    labelInfo.setHeight(tmDimPt.y);// new height
    return true;
  }
  else {
    return false;
  }

}

/**
 *
 * Create initial layout, placing each label in its ideal location. Locations will be subsequently updated
 * to account for collisions
 
 * @return {Array}  An array with two elements. The first element is an array of DvtSliceLabelInfo objects for the
 *                  labels on the left side of the pie.  The second element is an array of DvtSliceLabelInfo objects
 *                  for the labels on the right side of the pie.
 *
 * @private
 */
DvtSliceLabelLayout.prototype._generateInitialLayout = function () {
  var arArrays = new Array(2);
  var leftLabels = [];
  var rightLabels = [];

  var slice;// DvtPieSlice 
  var labelPt;// generic objects with x and y fields
  var tmDimPt;

  var ma = 0;;
  var pa = 0;
  var i;
  var j;
  var n;

  var s_label;// an instance of DvtTextArea ;           // for bug 7593102
  var maxLabelWidth;

  slices = this._pieChart.__getSlices();
  n = slices.length;

  if (n > 0) {
    for (i = 0;i < n;i++) {
      slice = slices[i];
      s_label = DvtSliceLabelLayout._createLabel(slice);

      ma = slice.getAngleStart() + (slice.getAngleExtent() / 2);

      if (ma > 360)
        ma -= 360;
      if (ma < 0)
        ma += 360;

      labelPt = DvtPieRenderUtils.reflectAngleOverYAxis(ma, this._pieChart.getCenter().x, this._pieChart.getCenter().y, this._isaXRadius, this._isaYRadius);

      if (ma >= 90 && ma < 270) {
        maxLabelWidth = labelPt.x - this._isaLeft;
      }
      // right side
      else {
        maxLabelWidth = this._isaRight - labelPt.x;
      }

      tmDimPt = DvtSliceLabelLayout._getTextDimension(this._pieChart, s_label, maxLabelWidth, -1, DvtSliceLabelLayout._MAX_LINES_PER_LABEL);// set up for word wrap
      
      var midArea = 15;

      if (ma < 180-midArea && ma > midArea) {
        //upper half
        labelPt.y -= tmDimPt.y * 1;
      }
      else if (ma < midArea || ma > 360-midArea) {
        //right side, near horizontal
        labelPt.y -= tmDimPt.y * 0.5;
        labelPt.x += tmDimPt.y * 0.2;
      }
      else if (ma > 180-midArea && ma < 180+midArea) {
        //left side, near horizontal
        labelPt.y -= tmDimPt.y * 0.5;
        labelPt.x -= tmDimPt.y * 0.2;
      }

      var tilt = DvtSliceLabelLayout._adjustForDepth(ma, this._pieChart.getDepth());
      labelPt.y += tilt;

      if (n == 1) {
        //only 1 label
        labelPt.x -= tmDimPt.x / 2;//position the label at the center
      }

      if (labelPt.y < this._isaTop) {
        labelPt.y = this._isaTop;
      }

      if ((labelPt.y + tmDimPt.y) > this._isaBottom) {
        labelPt.y = this._isaBottom - tmDimPt.y;
      }

      if (ma >= 90.0 && ma < 270.0)// left side
      {
        // right align
        s_label.alignEnd();
        //        s_label.alignTop();  // alignTop impl buggy - too much interline space in FF
        // normalize from 0 to 180
        pa = ma - 90.0;

        DvtSliceLabelLayout._createLabelInfo(slice, s_label, ma, pa, tmDimPt, labelPt, leftLabels);
      }

      else // right side
      {
        // normalize from 0 to 180
        pa = (ma <= 90.0) ? Math.abs(90 - ma) : (180 - (ma - 270));

        DvtSliceLabelLayout._createLabelInfo(slice, s_label, ma, pa, tmDimPt, labelPt, rightLabels);
      }
    }

  }

  arArrays[0] = leftLabels;
  arArrays[1] = rightLabels;

  return arArrays;

}

/**
 * Private helper function used to determine if an angle aligns with the horizontal axis of the pie
 *
 * @param {Number} angle
 * @return {Boolean}
 */
DvtSliceLabelLayout._isOnHorizontalAxis = function (angle) {
  return angle < DvtSliceLabelLayout._ROUNDING_TOLERANCE || Math.abs(angle - 180) < DvtSliceLabelLayout._ROUNDING_TOLERANCE || Math.abs(angle - 360) < DvtSliceLabelLayout._ROUNDING_TOLERANCE;
}

/**
 * Create the DvtSliceLabelInfo property bag object for a given slice and inserts it into labelInfoArray,
 * it its properly sorted position (where top-most labels are at the start of the array)
 *
 * @param {DvtPieSlice} slice
 * @param {DvtTextArea} sliceLabel  The physical label we will associate with thie DvtSliceLabelInfo. This
 label will be the one eventually associated with the input slice, if this
 label gets rendered
 * @param {number} ma The angle for the feeler line, with 0 degrees being the standard
 *                    0 degrees in the trigonometric sense (3 o'clock position)
 * @param {number} pa The normalized midpoint angle, where 0 degrees is at 12 o'clock
 *                    and angular measures are degrees away from 12 o'clock (so 90 degrees
 *                    can be either at 3 o'clock or 9 o'clock on the unit circle. Used to order slice
 *                    labels from top to bottom
 * @param {object} tmDimPt Object representing the width and height of the slice label
 * @param {object} labelPit The outside endpoint of the feeler line
 * @param {Array} labelInfoArray Where we store the newly created DvtSliceLabelInfo
 *
 */

// method carefully refactored from the end of PieChart.prepareLabels
DvtSliceLabelLayout._createLabelInfo = function (slice, sliceLabel, ma, pa, tmDimPt, labelPt, labelInfoArray) {
  var insertPos =  - 1;
  var labelInfo;
  var s_label = sliceLabel;

  // insertion "sort"
  for (var j = 0;j < labelInfoArray.length;j++) {
    labelInfo = labelInfoArray[j];
    if (labelInfo.getPosition() > pa) {
      insertPos = j;
      break;
    }
  }

  if (insertPos ==  - 1)
    insertPos = labelInfoArray.length;

  labelInfo = new DvtSliceLabelInfo();

  labelInfo.setPosition(pa);
  labelInfo.setAngle(ma);
  labelInfo.setSliceLabel(s_label);
  labelInfo.setSlice(slice);
  labelInfo.setWidth(tmDimPt.x);
  labelInfo.setHeight(tmDimPt.y);
  labelInfo.setInitialNumLines(s_label.getNumLines());
  labelInfo.setX(labelPt.x);
  labelInfo.setY(labelPt.y);

  labelInfoArray.splice(insertPos, 0, labelInfo);
}

/**
 *
 * Wraps and truncates the text in the pieLabel, and returns a pt describing the new dimensions
 * @param {DvtPieChart} pieChart
 * @param {DvtTextArea} tf the DvtTextArea to wrap and truncate
 * @param {Number} conWidth the maxWidth of a line
 * @param {Number} conHeight the maxHeight of the DvtTextArea
 * @param {Number} maxLines the maximum number of lines to wrap, after which the rest of the text is truncated
 * @private
 */

DvtSliceLabelLayout._getTextDimension = function (pieChart, tf, conWidth, conHeight, maxLines) {
  var numChildren = pieChart.getNumChildren();
  // we need to make sure the label is added to the DOM to get the dimensions
  pieChart.addChild(tf);

  tf.setLeading(DvtSliceLabelLayout._LABEL_LEADING);

  // wrap the text and then return a point with the new dimension
  tf.setWordWrap(true);
  tf.setMaxLines(maxLines);
  tf.setMaxWidth(conWidth, 0);

  var pt = {
    x : 0, y : 0
  };
  var dimensions;

  dimensions = tf.getDimensions();

  pt.x = dimensions.w;
  pt.y = dimensions.h;
  pieChart.removeChildAt(numChildren);
  return pt;

}
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
/**
 * @class DvtPieRendeurUtils
 */
var DvtPieRenderUtils = function () {
};

DvtObj.createSubclass(DvtPieRenderUtils, DvtObj, "DvtPieRenderUtils");

/** @final  @type String  */
DvtPieRenderUtils.TWOD = "2D";
/** @final  @type String  */
DvtPieRenderUtils.THREED = "3D";
/** @final  @type String  */
DvtPieRenderUtils.CRUST = "CRUST";
/** @final  @type String  */
DvtPieRenderUtils.SIDE = "SIDE";
/** @final  @type String  */
DvtPieRenderUtils.BORDER = "BORDER";

/**
 * Returns a <code>Point</code> object representing a point at a given
 * angle at a distance specified by the rx and ry radius from the center cx, cy.
 *
 * Function reflects input angle over the y-axis.  It then scales the
 * cosine and sine of this angle by rx and ry, and then translates
 * the cosine and sine values by cx and cy.  The reflected, scaled, and
 * translated angle's cosine and sine values are then returned
 *
 * @param {number} angle A <code>Number</code> representing the desired angle in degrees.
 * @param {number} cx    A <code>Number</code> indicating the center horizontal position.
 * @param {number} cy    A <code>Number</code> indicating the center vertical position.
 * @param {number} rx    A <code>Number</code> indicating the horizontal radius.
 * @param {number} ry    A <code>Number</code> indicating the vertical radius.
 *
 * @return A point object with the calculated x and y fields.
 */

// original code taken from com.oracle.dvt.shape.draw.utils.RenderUtils
// function originally called rotatePoint -- but that was a serious misnomer
DvtPieRenderUtils.reflectAngleOverYAxis = function (angle, cx, cy, rx, ry) {
  var radian = DvtMath.degreesToRads(360 - angle);
  var cosine = Math.cos(radian);
  var sine = Math.sin(radian);

  return {x: cx+(cosine*rx), y: cy+(sine*ry)};
}

/**
 * Returns an array of colors (with no alphas) for use in creating a gradient, based on a base color and where the gradient
 * will be applied
 *
 * @param {String} baseColor
 * @param {String} style Either DvtPieRenderUtils.TWOD, DvtPieRenderUtils.THREED, DvtPieRenderUtils.CRUST,
 *                          DvtPieRenderUtils.SIDE, or DvtPieRenderUtils.BORDER
 * @param {string} skin
 * @return {Array}
 */
DvtPieRenderUtils.getGradientColors = function (baseColor, style, skin) {
  if (skin && skin != DvtCSSStyle.SKIN_SKYROS) {
    if (style == DvtPieRenderUtils.TWOD || style == DvtPieRenderUtils.THREED)
      return [DvtColorUtils.adjustHSL(baseColor, 0,  - 0.04,  - 0.05), DvtColorUtils.adjustHSL(baseColor, 0,  - 0.09, 0.04)];
    else if (style == DvtPieRenderUtils.CRUST)
      return [DvtColorUtils.adjustHSL(baseColor, 0,  - 0.04,  - 0.05), DvtColorUtils.adjustHSL(baseColor, 0, 0,  - 0.14)];
    else if (style == DvtPieRenderUtils.SIDE)
      return [DvtColorUtils.adjustHSL(baseColor, 0,  - 0.10, 0.06), DvtColorUtils.adjustHSL(baseColor, 0,  - 0.04,  - 0.05)];
  }
  else if (style == DvtPieRenderUtils.TWOD) {
    var arColors = [];
    arColors[0] = DvtColorUtils.getRGB(DvtColorUtils.getPastel(baseColor, 0.1));
    arColors[1] = arColors[0];
    arColors[2] = DvtColorUtils.getRGB(DvtColorUtils.getDarker(baseColor, 0.9));
    return arColors;
  }
  else if (style == DvtPieRenderUtils.BORDER)
    return ["#FFFFFF", "#000000", "#000000"];
  else {
    var c = DvtColorUtils.getRGB(DvtColorUtils.getDarker(baseColor, 0.88));
    var c1 = DvtColorUtils.getRGB(DvtColorUtils.getPastel(baseColor, .05));
    var c2 = DvtColorUtils.getRGB(DvtColorUtils.getPastel(baseColor, .15));
    var c3 = DvtColorUtils.getRGB(DvtColorUtils.getPastel(baseColor, .35));

    if (style == DvtPieRenderUtils.CRUST)
      return [c, c2, c3, c];
    else if (style == "SIDE")
      return [c, c3];
    else if (style == "3D")
      return [c3, c2, c, c1, c3];
  }
}

/**
 * Returns an array of alphas for use in creating a gradient, based on an initial alpha value and where the gradient
 * will be applied
 *
 * @param {number} baseAlpha
 * @param {String} style Either DvtPieRenderUtils.TWOD, DvtPieRenderUtils.THREED, DvtPieRenderUtils.CRUST,
 *                          DvtPieRenderUtils.SIDE, or DvtPieRenderUtils.BORDER
 *
 * @return {Array}
 */
DvtPieRenderUtils.getGradientAlphas = function (baseAlpha, style) {
  var alpha = (baseAlpha == null || isNaN(baseAlpha) || baseAlpha == 0) ? 1.0 : baseAlpha;
  if (style == DvtPieRenderUtils.TWOD)
    return [alpha, alpha, alpha]
  else if (style == DvtPieRenderUtils.BORDER)
    return [(alpha / (0xFF / 0xA0)), (alpha / (0xFF / 0x30)), (alpha / (0xFF / 0x60))];
  else if (style == DvtPieRenderUtils.THREED)
    return [alpha, alpha, alpha, alpha, alpha];
  else if (style == DvtPieRenderUtils.CRUST)
    return [alpha, alpha, alpha, alpha];
  else if (style == DvtPieRenderUtils.SIDE)
    return [alpha, alpha];
}

// ported over from PieUtils
/**
 * Returns an array of stops for use in creating a gradient, based on where the gradient will be applied
 * 
 * @param {String} style Either DvtPieRenderUtils.TWOD, DvtPieRenderUtils.THREED, DvtPieRenderUtils.CRUST, 
 *                          DvtPieRenderUtils.SIDE, or DvtPieRenderUtils.BORDER  
 * @param {string} skin
 * @return {Array}
 */
DvtPieRenderUtils.getGradientRatios = function (style, skin) {
  if (skin && skin != DvtCSSStyle.SKIN_SKYROS)
    return [0, 1.0];
  else if (style == DvtPieRenderUtils.TWOD)
    return [0.2, 0.5, 1.0];
  else if (style == DvtPieRenderUtils.BORDER)
    return [0, 0.5, 1.0];
  else if (style == DvtPieRenderUtils.THREED)
    return [0.0, 0.29, 0.55, 0.84, 1.0];
  else if (style == DvtPieRenderUtils.CRUST)
    return [0, 0.43, 0.91, 1.0];
  else if (style == DvtPieRenderUtils.SIDE)
    return [0, 1];
}
/**
  * Interactivity manager for pie slice explode.
  * @class DvtPieSliceExplodeHandler
  * @constructor
  */
var  DvtPieSliceExplodeHandler = function() {};

DvtObj.createSubclass(DvtPieSliceExplodeHandler, DvtObj, "DvtPieSliceExplodeHandler");


/**
 * Processes a click event.
 * @param {DvtPieSlice} target
 */
DvtPieSliceExplodeHandler.processClick = function(target) {

  if(! target instanceof DvtPieSlice)
    return;
  
  var pie = target.getPieChart();
  var sliceInfo = target.getSliceInfo();
  
  var explodeOp;
  
  if(sliceInfo.getExplode() > 0)
    explodeOp = DvtPieChart.UNITE;
  else
    explodeOp = DvtPieChart.EXPLODE;
  
  pie.animateSingleSliceExplode(explodeOp, sliceInfo.getId());
};


// Copyright (c) 2008, 2012, Oracle and/or its affiliates. 
// All rights reserved. 
/*---------------------------------------------------------------------------*/
/*   DvtGaugeData         This is the base gauge data class                  */
/*---------------------------------------------------------------------------*/
/*  History:                                                                 */
/*  --------                                                                 */
/*---------------------------------------------------------------------------*/

/**
  *  Base class for Gauge data.
  *  @class DvtGaugeData
  *  @constructor
  */
var  DvtGaugeData = function()
{
  this._Init() ;
};

DvtObj.createSubclass(DvtGaugeData, DvtObj, "DvtGaugeData");

DvtGaugeData.prototype._Init = function()
{
   this._id     = null ;

   this._minVal = 0 ;           // min and max data values
   this._maxVal = 0 ;           // represented by the gauge
   this._curVal = 0 ;           // current data value represnted
                                // by the gauge

   this._metricLabel = null  ;  // metric label
   this._topLabel    = null  ;  // top label
   this._bottomLabel = null  ;  // bottom label

   this._oMetricLabel = null ;  // metric label DvtDetText
   this._oTopLabel    = null ;  // top    label DvtDetText
   this._oBottomLabel = null ;  // bottom label DvtDetText

   this._arThresholdValues = null ; // array of Numbers 
   this._arThresholdColors = null ; // array of color uints

   this._oPlotArea         = null ; // Dvt Det obj
   this._sTooltip          = null;
};


/**
 * Returns the current value of the gauge indicator
 * 
 * @return {Number}
 */
DvtGaugeData.prototype.getValue = function()
{
  return this._curVal;
}
/*---------------------------------------------------------------------------*/
/*   DvtDialGaugeData         This is the dial gauge data class              */
/*---------------------------------------------------------------------------*/
/*  History:                                                                 */
/*  --------                                                                 */
/*---------------------------------------------------------------------------*/

/**
  *  @class DvtDialGaugeData
  *  @extends DvtGaugeData  
  *  @constructor
  */
var DvtDialGaugeData = function()
{
   this._Init() ;
};

DvtObj.createSubclass(DvtDialGaugeData, DvtGaugeData, "DvtDialGaugeData") ;

/**
  *   @private
  *
  */
DvtDialGaugeData.prototype._Init = function()
{
   DvtDialGaugeData.superclass._Init.call(this);

   this._dialAngleStart  = 0;       // angle for the needle at min position
   this._dialAngleExtent = 0;       // angular displacement to max position
   this._curAngle        = 0;       // currently drawn needle angle as a clockwise
                                   // displacement from the min position.
   this._pathNeedle      = null;   // DvtDetPath object to draw this needle at
                                   // its m_initangle position.
   this._objIndicatorBase = null;  // DvtDetOvalcircle at center - "the indicator base"

   //  For indicator threshold color change

   this._aThresholdAngles = null;
   this._iThresholdIndex  = 0;
   this._initAngle        = 0;     // the initial angle (clockwise rotation from
                                   // the min value.
};


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

/*--------------------------------------------------------------------------*/
/*   Class DvtValueFormat   Represents the rules and processing for format- */
/*                          ting a floating point value.                    */
/*--------------------------------------------------------------------------*/
/**   Represents the rules and processing for formatting a floating point value.
  *   <br><br>
  *   Usage:<br><br>
  *   <code>
  *   var  vf  = new DvtValueFormat(. . .) ;<br><br>
  *
  *   var  s   = vf.getFormattedValue(float) ;<br><br>
  *   or  <br> 
  *   var  s   = vf.getFormattedValue(float, axisMin, axisMax) ;<br><br>
  * 
  *   getFormattedValue() may be called multiple times on a DvtValueFormat
  *   object.<br><br>
  *
  *   @class DvtValueFormat
  *   @constructor
  */
var  DvtValueFormat = function(multiplier,      // 1 = SF_NONE, NaN = SF_AUTO
                               decSymbol, groupSeparator,
                               sfK, sfM, sfB, sfT, sfQ,
                               posPrefix, posSuffix, negPrefix, negSuffix,
                               groupSize, minFractionLength, maxFractionLength,
                               minIntegerLength, maxIntegerLength, padSymbol,
                               showScaleSymbol, invalidValue, baseMultiplier, bIsPowerPlusTwo)
{         
    //  Set constructor defaults (it is possible to receive an empty contructor from the parser).
 
    multiplier        = (multiplier !== undefined)?          multiplier        : 1 ;      // 1 = SF_NONE, NaN = SF_AUTO
    decSymbol         = (decSymbol !== undefined)?           decSymbol         : null ;
    groupSeparator    = (groupSeparator !== undefined)?      groupSeparator    : null ;
    sfK               = (sfK !== undefined)?                 sfK               : this.SFSYM_K ;
    sfM               = (sfM !== undefined)?                 sfM               : this.SFSYM_M ;
    sfB               = (sfB !== undefined)?                 sfB               : this.SFSYM_B ;
    sfT               = (sfT !== undefined)?                 sfT               : this.SFSYM_T ;
    sfQ               = (sfQ !== undefined)?                 sfQ               : this.SFSYM_Q ;
    posPrefix         = (posPrefix !== undefined)?           posPrefix         : '' ;
    posSuffix         = (posSuffix !== undefined)?           posSuffix         : '' ;
    negPrefix         = (negPrefix !== undefined)?           negPrefix         : '' ;
    negSuffix         = (negSuffix !== undefined)?           negSuffix         : '' ;
    groupSize         = (groupSize !== undefined)?           groupSize         : 3  ;
    minFractionLength = (minFractionLength  !== undefined)?  minFractionLength : 0 ;
    maxFractionLength = (maxFractionLength  !== undefined)?  maxFractionLength : 340 ;
    minIntegerLength  = (minIntegerLength !== undefined)?    minIntegerLength  : 1 ;
    maxIntegerLength  = (maxIntegerLength !== undefined)?    maxIntegerLength  : 390 ;
    padSymbol         = (padSymbol !== undefined)?           padSymbol         : '0' ;
    showScaleSymbol   = (showScaleSymbol !== undefined)?     showScaleSymbol   : true ;
    invalidValue      = (invalidValue !== undefined)?        invalidValue      : '' ;
    baseMultiplier    = (baseMultiplier !== undefined)?      baseMultiplier    : NaN ;
    bIsPowerPlusTwo   = (bIsPowerPlusTwo  !== undefined)?    bIsPowerPlusTwo   : false ;

    // Set default object state.

   this._nMultiplier  = NaN ;        // multiplier (-ve or +ve).
                                     // Note: NaN   = scale-auto.
                                     //       1     = no scaling required
                                     // 1e-3 = K scaling, 1e-6  = M scaling
                                     // 1e-9 = B scaling, 1e-12 = T scaling, 1e-15 = Q scaling
                                     // n = other scaling.
   this._nBaseMultiplier  = NaN;
   this._iMinFractLen     = 0;
   this._iMaxFractLen     = 0;
   this._iMinIntLen       = 0;
   this._iMaxIntLen       = 0;
   this._sPadSymbol       = null ;
   this._bShowScaleSymbol = false;   // if multiplier matches a known scaling,
                                     // append the scaling flag.
   this._sInvalidValueSym = null;    // representation of an invalid value                                                           
   this._decSymbol        = null;    // decimal symbol, or use period if null
   this._groupSeparator   = null;    // thousands separator
   this._posPrefix        = null;    // leading string if positive
   this._posSuffix        = null;    // trailing string if positive
   this._negPrefix        = null;    // leading string if negative
   this._negSuffix        = null;    // trailing string of negative
   this._iGroupSize       = 3 ;      // number of digits between grouping separators
                                     // (currently just thousands.)  This value will
                                     // likely come in on the args in the future.
   this._bIsPowerPlusTwo = false;
   this._scaleFlag       = this.SF_NONE ;
   this._arScaleSymbols  = [sfK, sfM, sfB, sfT, sfQ] ; // order must be preserved.
                                                       // See comments for the scaling
                                                       // flag definitions.
   this._decDigits      = 0 ;        // # of decimal digits to show
   this._round          = 0.5 ;      // rounding factor (is adjusted if
                                     // _decDigits != 0 or SF_AUTO)
   this._power          = 0 ;        // power of number to be formatted
   this._origScaleFlag  = 0 ;
   this._origDecDigits  = 0 ;
   this._origMultiplier = NaN ;

   this._minAxisValue   = NaN ;      // optional args for getFormattedValue()
   this._maxAxisValue   = NaN ;      // scope is within getFormattedValue() call only.

   this._bDefault       = (arguments.length === 0) ;  // true if format is a default format

    //  Scaling Flags

   this.SF_AUTO      = 0 ;    // auto scaling based on value to be scaled.
   this.SF_NONE      = 1 ;    // no scaling required.
   this.SF_K         = 2 ;    // thousands      )
   this.SF_M         = 3 ;    // millions       )    Must  stay in order,
   this.SF_B         = 4 ;    // billions       )    with SF_K first
   this.SF_T         = 5 ;    // trillions      )
   this.SF_Q         = 6 ;    // quadrillions   )
   this.SF_SPECIFIED = 99 ;   // we have a scaling, but not one of the standard ones.

    //  Default scaling symbols

   this.SFSYM_K  = 'K' ;
   this.SFSYM_M  = 'M' ;
   this.SFSYM_B  = 'B' ;
   this.SFSYM_T  = 'T' ;
   this.SFSYM_Q  = 'Q' ;

    //  Miscellaneous

   this.cPERIOD  = '.' ;
   this.cCOMMA   = ',' ;
   this.cZEROS   = '000000000000000000000000000000' ;    // to cover the majority of cases
   this.cEMPTY   = '' ;
   this.cTRUE    = 't' ;
   this.cE       = 'e' ;

   //  Update object state with constructor values (or their defaults)

   this._nMultiplier      = multiplier ;
   this._iGroupSize       = groupSize ;
   this._iMinFractLen     = minFractionLength ;
   this._iMaxFractLen     = maxFractionLength ;
   this._iMinIntLen       = minIntegerLength ;
   this._iMaxIntLen       = maxIntegerLength ;
   this._sPadSymbol       = padSymbol ;
   this._bShowScaleSymbol = showScaleSymbol ;
   this._nBaseMultiplier  = (baseMultiplier === 1)? NaN : baseMultiplier ;
   this._bIsPowerPlusTwo  = bIsPowerPlusTwo ;


   //  Inspect multiplier. Special cases are: 0   = no scaling,
   //                                         NaN = auto-scaling

   var  sf ;

   if (isNaN(multiplier))   {         // NaN multiplier signifies autoscaling
     sf = this.SF_AUTO ;
     this._nMultiplier = 1 ;
   }
   else if (multiplier === 1) {       // multiplier of 1 indicates no scaling
     sf = this.SF_NONE ;
   }
   else   { 
     sf = this.SF_SPECIFIED ;         // we have a multiplier

     if (showScaleSymbol)  {          // if we're showing scale symbols
                                      // note index to the symbol.
       switch (multiplier) {
         case  1e-3  :  sf = this.SF_K ;
                        break ;
         case  1e-6  :  sf = this.SF_M ;
                        break ;
         case  1e-9  :  sf = this.SF_B ;
                        break ;
         case  1e-12 :  sf = this.SF_T ;
                        break ;
         case  1e-15 :  sf = this.SF_Q ;
                        break ;
       }
     }
   }    
      
   this._scaleFlag  = sf ;


   //  Compute number of decimal digits required so that we compute the rounding factor.

   var  decDigits = 0 ;
   if (this._iMinFractLen !== -1)   {
     if (this._iMinFractLen > this._iMaxFractLen) {     // bullet-proofing
       this._iMaxFractLen = this._iMinFractLen ;
     }

     if (this._iMinFractLen === this._iMaxFractLen) {   // standard case of 0 to n fractional digits
      decDigits = this._iMinFractLen ;
     }
     else if (this._iMinFractLen < this._iMaxFractLen) {
      decDigits = this._iMaxFractLen ;
     }
   }  

   if (this._iMinFractLen === -1) {
     decDigits = -1 ;
   }

   // Note values that will need to be reinstated for another getFormattedValue()
   // call using this same object if autoscale is used.

   this._origScaleFlag  = sf ;
   this._origMultiplier = this._nMultiplier ;                                             
   this._origDecDigits  = decDigits ;
   this._decDigits      = decDigits;           // note caller's # of dec digits req'd.
                                               // If autoscale, this will be recomputed
                                               // later if dec digits == -1

   if (decSymbol === null) {
     this._decSymbol = this.cPERIOD ;          // use default value
   }
   else {
     this._decSymbol = decSymbol ;             // use supplied value
   }

   if (decDigits !== -1)   {
     this._round = Math.pow(10, decDigits) ;   // adjust rounding factor for round()
   }

   this._groupSeparator  = groupSeparator ;

   if (posPrefix === null) {
     posPrefix = this.cEMPTY ;
   }
   if (posSuffix === null) {
     posSuffix = this.cEMPTY ;
   }
   if (negPrefix === null) {
     negPrefix = this.cEMPTY ;
   }
   if (negSuffix === null) {
     negSuffix = this.cEMPTY ;
   }

   this._posPrefix  = posPrefix ;
   this._posSuffix  = posSuffix ;
   this._negPrefix  = negPrefix ;
   this._negSuffix  = negSuffix ;

};

DvtObj.createSubclass(DvtValueFormat, DvtObj, "DvtValueFormat");

/*-------------------------------------------------------------------------*/
/*  _autoDecDigits() Dynamically calculate the number of decimal digits of */
/*                   the supplied value.                                   */ 
/*-------------------------------------------------------------------------*/
/**   Dynamically calculate the number of decimal digits of the supplied value.
  *   @param {number}  val  the value.
  *   @private
  */
DvtValueFormat.prototype._autoDecDigits = function(val)
{

  var  decDigits = 0 ;

  // Use the value's power of 10 to calculate the # of decimal digits.
  // (dec digits is only set if minFractionLength === -1).

  this._power = this._getPowerOfTen(val) ;    // get the value's power of 10

  if (this._bIsPowerPlusTwo) {                // for parity with the middle-tier,
    this._power += 2 ;                        // bumps the power if a percentage
  }
  if (this._power <= 2)  {
    decDigits  = 3 - this._power ;
  }
  else if (this._power >= 16)   {
    if ((this._power - 15) < 3) {
      decDigits = 3 - (this._power - 15) ;
    }
    else {
      decDigits = 0 ;
    }
  }
  else
  {
    var  scale =  Math.floor((this._power - 1) / 3) * 3 ;
    decDigits  = 3 - (this._power - scale);
  }

  if (this._decDigits === -1)    {            // if dec digits was not specified 
    this._decDigits = decDigits ;             // use the computed value
  }


  //  Create scaled rounding factor

  if (this._decDigits !== 0) {
    this._round = Math.pow(10, this._decDigits) ; // adjust rounding factor for round()
  }

  //console.log("autoDecDigits(" + val + ") this._power=" + this._power +
  //            "  decDigits=" + this._decDigits + "  this._round =" + this._round) ;

} ;



/*-------------------------------------------------------------------------*/
/*  _autoScale()     Dynamically calculate the scale factor (multiplier)   */
/*                   and number of decimal digits of the supplied value.   */
/*                                                                         */
/*                   Returns true, if the multiplier was adjusted.         */
/*-------------------------------------------------------------------------*/
/**
  *  Dynamically calculate the scale factor (multiplier) and number of
  *  decimal digits of the supplied value.
  *  @param {number}  val  the value.
  *  @returns {boolean} true, if the multiplier was adjusted.
  *  @private
  */
DvtValueFormat.prototype._autoScale = function(val)
{
  var  bMultiplierChanged = false ;   // return value
  var value               = val ;     // default number to use to find the exponent
  
  // Were optional axis endpoint values supplied ?  If so, get the axis endpoint
  // with the largest absolute value, and find its base 10 exponent, instead of
  // using that of the number to be formatted. 

  if ( (! isNaN(this._maxAxisValue)) && (! isNaN(this._minAxisValue)) )   {
     var minAbs = Math.abs(this._minAxisValue) ;
     var maxAbs = Math.abs(this._maxAxisValue) ;                  
     
     value = (maxAbs > minAbs)? maxAbs : minAbs ;

     this._minAxisValue = NaN ;               // no longer required after this call
     this._maxAxisValue = NaN ;               // this specific call. 
     //console.log("adjusted power=" + this._getPowerOfTen(value) + " (was " + 
     //            this._getPowerOfTen(val) + ") for n=" + val) ;
  }
  
  this._power = this._getPowerOfTen(value) ;  // get the value's power of 10

  // Use power to set the scale factor and # of decimal digits (dec digits
  // is only set if dec digits === -1).

  if (this._bIsPowerPlusTwo) {                // for parity with the middle-tier,
    this._power += 2 ;                        // bumps the power if a percentage
  }

  if (this._power <= 2)   {
    this._scaleFlag  = this.SF_NONE ;
    this._nMultiplier = 1 ;
    if (this._decDigits === -1) {
      this._decDigits  = 3 - this._power ;
    }
  }
  else if (this._power >= 16)   {
    this._scaleFlag = this.SF_Q ;
    if (this._decDigits === -1)    {
      if ((this._power - 15) < 3) {
        this._decDigits = 3 - (this._power - 15) ;
      }
      else {
        this._decDigits = 0 ;
      }
    }
    this._nMultiplier  = 1e-15 ;
    bMultiplierChanged = true ;
  }
  else   {
    var  scale =  Math.floor((this._power - 1) / 3) * 3 ;

    switch (scale)  {
      case  3 :   this._scaleFlag    = this.SF_K ;
                  this._nMultiplier  = 1e-3 ;
                  bMultiplierChanged = true ;
                  break ;
      case  6 :   this._scaleFlag   = this.SF_M ;
                  this._nMultiplier  = 1e-6 ;
                  bMultiplierChanged = true ;
                  break ;
      case  9 :   this._scaleFlag    = this.SF_B ;
                  this._nMultiplier  = 1e-9 ;
                  bMultiplierChanged = true ;
                  break ;
      case 12 :   this._scaleFlag    = this.SF_T ;
                  this._nMultiplier  = 1e-12 ;
                  bMultiplierChanged = true ;
                  break ;
      case 15 :   this._scaleFlag    = this.SF_Q ;
                  this._nMultiplier  = 1e-15 ;
                  bMultiplierChanged = true ;
                  break ;
      default:    this._scaleFlag = this.SF_NONE;
                  break;
    }
    
    if (this._decDigits === -1) {                     // if dec digits was not specified 
      this._decDigits  = 3 - (this._power - scale);   // use the computed value. 
    }
  }

  //  Create scaled rounding factor

  if (this._decDigits !== 0) {
    this._round = Math.pow(10, this._decDigits) ;     // adjust rounding factor for round()
  }
  //console.log("_power=" + this._power + " scale=" + scale)  ;
  //console.log("decDigits=" + this._decDigits + " scaleFlag=" +
  //             this._scaleFlag + " _round =" + this._round) ;

  return bMultiplierChanged ;

} ;




/*-------------------------------------------------------------------------*/
/*   getFormattedValue()       Returns the specified number as a formatted */
/*                             string.                                     */
/*                                                                         */
/*   Note: minAxisValue/maxAxisValue are optional args, and if supplied    */
/*         are used to calculate the power of 10 used for autoscaling      */
/*         instead of that of the value to be formatted.                   */
/*-------------------------------------------------------------------------*/
/**
  *  Returns the specified number as a formatted string.  Note: minAxisValue/maxAxisValue
  *  are optional args, and if supplied are used to calculate the power of 10
  *  used for autoscaling instead of that of the value to be formatted.
  *  @param {number}  n   the value to be formatted.
  *  @param {number}  minAxisValue   optional
  *  @param {number}  maxAxisValue   optional
  *  @returns {String}  the string format of the number.
  */
DvtValueFormat.prototype.getFormattedValue = function(n, minAxisValue, maxAxisValue)
{
   minAxisValue  =  (minAxisValue !== undefined)?  minAxisValue : NaN ;
   maxAxisValue  =  (maxAxisValue !== undefined)?  maxAxisValue : NaN ;

   var  i ;                             // general loop counter
   var  bNegative          = false ;
   var  bMultiplierApplied = false ;

   var  minFractLen = this._iMinFractLen ;
   var  maxFractLen = this._iMaxFractLen ;

   if (isNaN(n) || (! isFinite(n))) {
     return this._sInvalidValueSym ;
   }

   this._minAxisValue = minAxisValue ;         // note optional args (see autoscale())
   this._maxAxisValue = maxAxisValue ;
   this._scaleFlag    = this._origScaleFlag ;  // restore flag (might change if SF_AUTO)

   //  Restore values that might have changed by a previous getFormattedValue()
   //  call to the same ValueFormat.

   if ((this._scaleFlag === this.SF_AUTO) || (this._scaleFlag !== this.SF_AUTO && minFractLen === -1))  {
     this._power = 0 ;
     if (this._scaleFlag === this.SF_AUTO) {
       this._nMultiplier = 1 ;
     }
     this._decDigits   = this._origDecDigits ;
     this._nMultiplier = this._origMultiplier ;
     // this._round is recomputed after autoscale()
   }

   if (n < 0)  {
     n = -n ;                    // convert to positive so we won't get
     bNegative = true ;          // a minus sign in the converted string.
   }

   //  Is a valid multiplier  specified together with minFractLen of -1

   if ((this._scaleFlag !== this.SF_AUTO) && (minFractLen === -1))  {
      this._autoDecDigits(n) ;   // update this._decDigits - note: middle-tier does it first!!! (not on result)

      n *= this._nMultiplier ;
      bMultiplierApplied = true ;

      if (minFractLen == -1) {
        minFractLen = this._decDigits ;
      }
      if (maxFractLen === -1) {
        maxFractLen = this._decDigits ;
      }
      this._round = Math.pow(10, this._decDigits) ;  // rounding factor for round()
   }


   if (this._scaleFlag === this.SF_AUTO)  {   // if auto formatting, then inspect the
                                              // value to determine the scaling factor
      this._autoScale(n) ;                    // and number of dec digits (if dec digits == -1).

      if (minFractLen === -1) {
        minFractLen = this._decDigits ;
      }
      if (maxFractLen === -1) {
        maxFractLen = this._decDigits ;
      }
      this._round = Math.pow(10, this._decDigits) ;  // rounding factor for round()
   }

   //  Perform scaling if specified   Note: there is a generic problem with floating point
   //  or computed.                   computation sometimes (mentioned in Adobe TechNote
   //                                 http://kb2.adobe.com/cps/139/tn_13989.html
   //                                 e.g.  n = 0.0055   but n *= pow(10, 2) = .54999999...
   //                                 and not 0.55 as expected. So if 1 place of decimals 
   //                                 is required, subsequent rounding gives 0.5 and not
   //                                 0.6 as expected!!! 
   //                                 The current hack used is to increase the multiplier
   //                                 by the number of dec dicits required, and then
   //                                 divide by the number of dec digits as a power of 10.

   if (! bMultiplierApplied)   {
     if (this._scaleFlag !== this.SF_NONE)   {
        if (this._nMultiplier !== 1)   {
          var adjust = (this._decDigits > 0)? this._decDigits +3 : 3 ;
          var xx     = this._nMultiplier * Math.pow(10, adjust) ;

          n *= xx ;
          n /= Math.pow(10, adjust) ;
        }
     }     
   }

   if (! isNaN(this._nBaseMultiplier)) {  // Apply the base multiplier if supplied
     n *= this._nBaseMultiplier ;         // by middle-tier.
   }


   // Sanity check in case result is out of range 

   if (isNaN(n) || (! isFinite(n))) {
     return this._sInvalidValueSym ;
   }

   //  Round the number with respect to required decimal digits

   n = Math.round(n * this._round)/this._round ;  // round place to the right of the
                                                  // last required decimal digit.

   // Will now apply formatting rules on a string representation of the value

   var s  = n + '' ;

   //console.log("VF16 s=\"" + s + "\"") ;


   //---------------------------------------------------------------//
   //              Apply min/max fraction length rules              //
   //---------------------------------------------------------------//

   var xDecPoint  = s.indexOf(this.cPERIOD) ;  // note location of decimal point
   var decDigits  = 0 ;                        // # of dec digits we actually have

   if (this._power < 0)   {                    // for small numbers, flash might return
                                               // string in scientific format.
      var xE = s.indexOf(this.cE) ;
      if (xE > 0)  {                           // if there is an exponent, remove it
        s = s.substr(0, xE) ;                  // remove the exponent part

        var nZeros = - this._power - xDecPoint;   // get # of preceding zeros to
                                                  // insert before the leftmost digit
        if (nZeros > 0) {
           //  Rebuild the string in standard notation with decimal point at the start.
           //  i.e. of the form .nnnnnnnnnnnn etc

           s = this.cPERIOD + this.cZEROS.substring(0, nZeros) + s.substr(0, xDecPoint) + s.substr(xDecPoint+1) ;
           xDecPoint = 0 ;
        }
      }
   }


   if (minFractLen === 0 && maxFractLen === 0)   // if no dec digits required
   {
     if (xDecPoint >= 0) {                       // if we have a decimal point
       s = s.substring(0, xDecPoint) ;           // remove it and any decimal digits
     }
   }
   else
   {
     //----------------------------------//
     //  We need to show decimal digits  //
     //----------------------------------//

     if (xDecPoint >= 0)   {                   // if we have a decimal point
       decDigits = (s.length -1 - xDecPoint) ; // get # of decimal digits we currently have

       if (decDigits < minFractLen)  {
         // need to add additional right padding

         decDigits = minFractLen - decDigits;  // # of extra zeros required
         if ((this._sPadSymbol === '0') && (decDigits <= this.cZEROS.length))   {
           // quick way
           s += this.cZEROS.substr(0, decDigits) ;  // add the decimal digit right padding
         }
         else   {
            // slower way for non-standard non-zero padding

            for (i = decDigits; i > 0; i--) {
               s += this._sPadSymbol ;
            }
         }
       }
       else  if (decDigits > maxFractLen)  {    // do we need to remove some dec digits?
                                                // yes.
          // To match the middle-tier, we introduce a somewhat non-mathematical
          // element related to trailing zeros.  123.00000 is technically different
          // to 123.00 since the former has a greater level of precision.  If
          // min/max fraction digits is set to 3 and 4, the algorithm would have
          // originally removed the rightmost digit since the max-fractional
          // digit constraint was not met.  The new request (5/28/10 Diar) is that
          // in the case of trailing zeros only, we use the min-fractional length
          // to set the number of fractional digits. That is in the above case,
          // a result of 123.000 would be produced.

          i = (xDecPoint + minFractLen + 1) ;  // index to digit past minFractLen

          var iStrLen = s.length ;
          
          while (i < iStrLen)  {         // see if all trailing zeros to right of minFractLen
             if (s.charAt(i) !== '0') {
                iStrLen = 0 ;            // use as flag to indicate non-zero digit found
                break ;
             }
             i++ ;
          }

          var  fractLen = (iStrLen === 0)? maxFractLen : minFractLen ;

          s = s.substring(0, (xDecPoint + fractLen + 1)) ;  // truncate to min or max fract len
       }
     }
     else   {          // no decimal point
       if (minFractLen > 0)    {
         s += this._decSymbol ;                      // add the decimal point

         if ((this._sPadSymbol === '0') && (minFractLen <= this.cZEROS.length)) {
           s += this.cZEROS.substr(0, minFractLen) ; // add decimal "zeros"
         }
         else   {
            // slower way for non-standard non-zero padding

            for (i = this._iMinFractLen; i > 0; i--) {
               s += this._sPadSymbol ;
            }
         }
         //  We now have a decimal point, so need to set index to it.

         xDecPoint = s.length - minFractLen - 1 ;
       }
     }

   }


   //---------------------------------------------------------------//
   //              Apply min/max integer length rules               //
   //---------------------------------------------------------------//

   var  intLen ;
   var  padCount = 0 ;
   var  sTemp ;
   var  maxIntLen = 0 ;

   if (xDecPoint >= 0) {
     intLen = (xDecPoint) ;
   }
   else {
     intLen = s.length ;
   }

   maxIntLen = this._iMaxIntLen ;           // get working copy

   // Special case - added 5/25/10 for parity with middle-tier.
   // If number is -1 < n < 1  AND minIntLen == 0, take this as
   // meaning no preceding zeros. e.g. using 0.1234 means show
   // .1234 and not 0.1234 as would have been allowbale using the
   // min/maxIntegerLength constraints.

   if ((intLen === 1) && (this._iMinIntLen === 0)) {  // if number is -1 < n < 1 and minIntLen == 0
     maxIntLen = 0 ;                                  // want no preceding zero.
   }

   if (intLen < this._iMinIntLen)  {             // are there "preceding "zeros" to pad?
      padCount = (this._iMinIntLen - intLen) ;         

      if ((this._sPadSymbol === "0") && (padCount <= this.cZEROS.length)) {
        sTemp = this.cZEROS.substr(0, padCount) ;
      }
      else {
         for (i = padCount; i > 0; i--) {
            sTemp += this._sPadSymbol ;
         }
      }
      s = sTemp + s ;

     if (xDecPoint >= 0) {             // is there a decimal point?
       xDecPoint += padCount ;         // yes, adjust index to it.
     }
   }  
   else  if (intLen > maxIntLen)  {    // are there integers to prune
      padCount = (intLen - maxIntLen) ;
      
      s = s.substring(padCount) ;

      if (xDecPoint >= 0) {            // is there a decimal point?
        xDecPoint -= padCount ;        // yes, adjust index to it.
      }
   }  


   //---------------------------------------------------------------//
   //                Apply grouping separator rule                  //
   //---------------------------------------------------------------//

   if ((this._groupSeparator !== null) && (this._iGroupSize > 0))   {
      if (xDecPoint < 0)  {                  // do we have a decimal point
        xDecPoint = s.length ;               // no assume to the right of the string 
      }                                      

      if (xDecPoint > this._iGroupSize)  {   // can we fit in at least 1 separator?
        var  xDP   = xDecPoint - this._iGroupSize ;  // index to separator position
        var  sRem  = s.substring(xDP) ;
        var  sNew  = this.cEMPTY;

        for (;;)  {
          sRem  = sNew + sRem ;
          xDP   -= this._iGroupSize ;          // find next separator position
          if (xDP <= 0)  {
            xDP += this._iGroupSize ;
            sRem = s.substring(0, xDP) + this._groupSeparator + sRem ;   // beginning incomplete group
            break ;
          }
          sNew   =  s.substr(xDP, this._iGroupSize) + this._groupSeparator ;
        }

        s = sRem ;
      }
   }


   //---------------------------------------------------------------//
   //                  Apply decimal separator rule                 //
   //---------------------------------------------------------------//

   if ((decDigits > 0) && (this._decSymbol !== null))  {
      //  We may have replaced the thousands separator with a period.
      //  Therefore we need to search from the right to find the 
      //  original decimal point.

      xDecPoint = s.lastIndexOf(this.cPERIOD) ;  // note location of decimal point

      if (xDecPoint >= 0)   {                    // defensive code
        var sLen   = s.length ;
        var sRight = this.cEMPTY ;
        var sLeft  = s.substr(0, xDecPoint) ;
        
        if (++xDecPoint <= s.length) {           // defensive code
          sRight = s.substr(xDecPoint) ;
        }

        s = sLeft +  this._decSymbol + sRight;
      }
   }


   //---------------------------------------------------------------//
   //                  Add scale factor symbol                      //
   //---------------------------------------------------------------//

   if (this._bShowScaleSymbol)   {
     if ((this._scaleFlag >= this.SF_K) && (this._scaleFlag <= this.SF_Q))  {
       var sfSym = this._arScaleSymbols[this._scaleFlag - this.SF_K] ;

       if (sfSym !== null) {
         s += sfSym ;
       }
     }
   }


   //---------------------------------------------------------------//
   //          Apply positive/negative  pre/post strings            //
   //---------------------------------------------------------------//

   if (bNegative)    {
      s =  this._negPrefix + s ;
      s += this._negSuffix ;
   }
   else    {
      s =  this._posPrefix + s ;
      s += this._posSuffix ;
   }

   //console.log("VF returning \"" + s + "\"") ;

   return s ;              // final formatted value string
} ;



//---------------------------------------------------------------//
//   getPowerOfTen()                                             //
//---------------------------------------------------------------//
/**
  *  Returns the power of 10 of the suppplied number
  *  @private
  *  @param {number}  val  The number to be evaluated.
  *  @returns {number} the power of 10 of the suppplied number
  */
DvtValueFormat.prototype._getPowerOfTen = function(val)
{
  var  x ;
  var  power = 0;

  if (val >= 10)   {
    x = Math.floor(val) ;                     // remove decimal digits
    power = x.toString().length - 1;
  }
  else if ((val < 1) && (val !== 0))    {
    var s = val.toExponential() ;

    x = s.indexOf("e") ;
    power = parseInt(s.substr(x+1), 10) ;
  }
  
  return power ;
}  ;



/*-------------------------------------------------------------------------*/
/*   isDefault()                                                           */
/*-------------------------------------------------------------------------*/
/**
  *  Returns true if this object is a default format.
  *  @returns {boolean}  true if this object is a default format.
  */
DvtValueFormat.prototype.isDefault = function()
{
    return this._bDefault ;
} ;



/*-------------------------------------------------------------------------*/
/*   setDefault()                                                          */
/*-------------------------------------------------------------------------*/
/**
  *  Marks this object as a default format.
  *  @param {boolean} bDefault  true if the object is to be marked as a default
  *  format, else false.
  */
DvtValueFormat.prototype.setDefault = function(bDefault)
{
    this._bDefault = bDefault ;
} ;


// Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*    DvtDetObj                Base Det list object                        */
/*-------------------------------------------------------------------------*/
/**
  *  Base Det List object. All Det objects in the chart's det list aggregate a DvtDetObj.
  *  @class DvtDetObj
  *  @constructor
  *  @implements {DvtCategoricalObject}
  *  @implements {DvtSelectable}
  *  @implements {DvtPopupSource}
  *  @implements {DvtContextMenuSource}
  */
var  DvtDetObj = function()
{
   this._Init() ;
} ;

DvtObj.createSubclass(DvtDetObj, DvtObj, "DvtDetObj");

/*---------------------------------------------------------------------*/
/*     _applyClipPath                                                  */
/*---------------------------------------------------------------------*/
/**  Apply a clipping path defined for the owning component/shape.
  *  @private
  */
DvtDetObj.prototype._applyClipPath = function()
{
   if (this._clipPath) {
     this._DetObj.setClipPath(this._clipPath) ;
   }
};



/*---------------------------------------------------------------------*/
/*  getChart() / setChart()                                            */
/*---------------------------------------------------------------------*/
/**
  *  Returns the owning {@link DvtGraphCore}.
  *  @type DvtGraphCore
  */
DvtDetObj.prototype.getChart = function()
{
   return this._Chart  ;
} ;
/**
  *  Sets the owning DvtGraphCore.  If the object has sub-objects that are derived
  *  from DvtDisplayable, this method will call the object method setChart() if
  *  exists, so that it can propagate the chart pointer to these object's DvtDetObj's.
  *  @param {DvtGraphCore}  chart  The owning chart.
  */
DvtDetObj.prototype.setChart = function(chart)
{
   this._Chart  = chart ;

   //  The object might be a compound component like Dvt3DBar or DvtPieChart 
   //  See if it wants to propagate the chart pointer to its sub-displayables.

   if (this._DetObj.setChart) {
     this._DetObj.setChart(chart) ;
   };

} ;


/*---------------------------------------------------------------------*/
/*  getDetObj() / setDetObj()                                          */
/*---------------------------------------------------------------------*/
/**
  *  Returns the owning Det Object (e.g. {@link DvtRect}).
  *  @type DvtObj 
  */
DvtDetObj.prototype.getDetObj = function()
{
   return this._DetObj;
} ;
/**
  *  Sets the owning Det Object (e.g. DvtRect).
  *  @param {DvtObj}  dobj  The object's (middle-tier) Id.
 */
DvtDetObj.prototype.setDetObj = function(dobj)
{
   this._DetObj  = dobj ;
} ;

/**
 * Returns the server side id for this object, which is used for multiple features
 * including popup support.  This id is based off of the group and series ids, and
 * the setter is intentionally left out.
 * @return {string} The server side id for this object.
 */
DvtDetObj.prototype.getId = function() {
  return DvtGraphEventUtils.getId(this.getSeriesId(), this.getGroupId());
}

/*---------------------------------------------------------------------*/
/*  getGroupId() / setGroupId()                                        */
/*---------------------------------------------------------------------*/
/**
  *  Returns the group Id
  *  @returns {String}  The group Id
  */
DvtDetObj.prototype.getGroupId = function()
{
   return this._groupId  ;
} ;
/**
  *  Sets the group Id
  *  @param {String}  gid  The group Id.
  */
DvtDetObj.prototype.setGroupId = function(gid)
{
   this._groupId  = gid ;
} ;

/*---------------------------------------------------------------------*/
/*  getGroupIndex() / setGroupIndex()                                  */
/*---------------------------------------------------------------------*/
/**
  *  Returns the group index
  *  @type number
 */
DvtDetObj.prototype.getGroupIndex = function()
{
   return this._groupIdx  ;
} ;
/**
  *  Sets the group Index.
  *  @param {number}  idx  The group index.
  */
DvtDetObj.prototype.setGroupIndex = function(idx)
{
   this._groupIdx  = idx ;
} ;


/*---------------------------------------------------------------------*/
/*  getMiscId() / setMiscId()                                          */
/*---------------------------------------------------------------------*/
/**
  *  Returns the miscellaneous Id
  *  @type number 
  */
DvtDetObj.prototype.getMiscId = function()
{
   return this._miscId  ;
} ;
/**
  *  sets the miscellaneous Id
  *  @param {number}  mid  The miscellaneous Id. 
  */
DvtDetObj.prototype.setMiscId = function(mid)
{
   this._miscId  = mid ;
} ;

/*---------------------------------------------------------------------*/
/*   getObjId() / setObjId()                                           */
/*---------------------------------------------------------------------*/
/**
  *  Returns the object's Id (from the middle-tier)
  *  @type String 
  */
DvtDetObj.prototype.getObjId = function()
{
   return this._objId  ;
} ;
/**
  *  Sets the object's Id (from the middle-tier)
  *  @param {String}  oid  The object's (middle-tier) Id.
 */
DvtDetObj.prototype.setObjId = function(oid)
{
   this._objId  = oid ;
} ;

//---------------------------------------------------------------------//
// Rollover and Hide/Show Support: DvtCategoricalObject impl           //
//---------------------------------------------------------------------//

/**
 * @override
 */
DvtDetObj.prototype.getCategories = function() {
  return [this.getSeriesId()];
};

//---------------------------------------------------------------------//
// Selection Support: DvtSelectable impl                               //
//---------------------------------------------------------------------//

/**
 * @override
 */ 
DvtDetObj.prototype.isSelectable = function() {
  if (this.getChart() && this.getChart().isSelectionSupported()) {
      if (this._objId == DvtGraphObjType.STOCK_VOLUME_RISER) {
          return true;
      } else if (this._DetObj instanceof DvtText || this._objId == DvtGraphObjType.PARETO_LINE_RISER) {
          return false;
      } else {
          return this.getSeriesId() && this.getGroupId();
      }
  }
  return false;
};

/**
 * @override
 */ 
DvtDetObj.prototype.isSelected = function()
{
  if(this._DetObj && this._DetObj.isSelected)
    return this._DetObj.isSelected();
  else
    return false;
};

/**
 * @override
 */ 
DvtDetObj.prototype.setSelected = function(bSelected) {
  if(this._DetObj && this._DetObj.setSelected)
    return this._DetObj.setSelected(bSelected);
};

/**
 * @override
 */ 
DvtDetObj.prototype.showHoverEffect = function()
{
  if(this._DetObj && this._DetObj.showHoverEffect)
    return this._DetObj.showHoverEffect();
};

/**
 * @override
 */ 
DvtDetObj.prototype.hideHoverEffect = function()
{
  if(this._DetObj && this._DetObj.hideHoverEffect)
    return this._DetObj.hideHoverEffect();
};

//---------------------------------------------------------------------//
// Popup Support: DvtPopupSource impl                                  //
//---------------------------------------------------------------------//

/**
 * @override
 */ 
DvtDetObj.prototype.getShowPopupBehaviors = function()
{
  if(this.getSeriesId() && this.getGroupId()) // only data objects are supported
    return this._Chart.__getShowPopupBehaviors();
};

//---------------------------------------------------------------------//
// Context Menu Support: DvtContextMenuSource impl                     //
//---------------------------------------------------------------------//

/**
 * @override
 */ 
DvtDetObj.prototype.getContextMenuId = function()
{
  return this._Chart.getMenuId(this.getSeriesIndex(), this.getGroupIndex());
};

/*---------------------------------------------------------------------*/
/*   getRefObjs() / setRefObjs()                                       */
/*---------------------------------------------------------------------*/
/**
  *  Returns the object's list of reference det objects.
  *  @type Array 
  */
DvtDetObj.prototype.getRefObjs = function()
{
   return this._aRefObjs  ;
} ;
/**
  *  Sets the object's reference det object list.
  *  @param {Array}  aRef  An array of reference det objects.
 */
DvtDetObj.prototype.setRefObjs = function(aRef)
{
   this._aRefObjs  = aRef ;
} ;


/*---------------------------------------------------------------------*/
/*  getSeriesId() / setSeriesId()                                      */
/*---------------------------------------------------------------------*/
/**
  *  Returns the series Id
  *  @type String 
  */
DvtDetObj.prototype.getSeriesId = function()
{
   return this._seriesId  ;
} ;
/**
  *  Sets the series Id
  *  @param {String}  sid  The series Id.
  */
DvtDetObj.prototype.setSeriesId = function(sid)
{
   this._seriesId  = sid ;
} ;


/*---------------------------------------------------------------------*/
/*  getSeriesIndex() / setSeriesIndex()                                */
/*---------------------------------------------------------------------*/
/**
  *  Returns the series index
  *  @type number
  */
DvtDetObj.prototype.getSeriesIndex = function()
{
   return this._seriesIdx  ;
} ;

/**
  *  Sets  the series index.
  *  @param {number}  idx  The series index.
  */
DvtDetObj.prototype.setSeriesIndex = function(idx)
{
   this._seriesIdx  = idx ;
} ;

/**
 * Returns the shape attributes for this object.
 * @param {DvtDisplayable} target The displayable on which the event was fired.
 * @return {DvtShapeAttr}
 */
DvtDetObj.prototype.getShapeAttr = function(target)
{
   return  this._shapeAttr ;
} ;


/**
  *  Sets shape attributes on this object.
  *  @param {DvtShapeAttr}  sa  a shapes attribute object.
  */
DvtDetObj.prototype.setShapeAttr = function(sa)
{
   this._shapeAttr = sa ;
} ;


/*---------------------------------------------------------------------*/
/*  getStackId() / setStackId()                                        */
/*---------------------------------------------------------------------*/
/**
  *  Returns the stack Id.
  *  @type number
  */
DvtDetObj.prototype.getStackId = function()
{
   return this._stackId  ;
} ;
/**
  *  Sets the stack Id.
  *  @param {number}  stid  The stackid.
  */
DvtDetObj.prototype.setStackId = function(stid)
{
   this._stackId  = stid ;
} ;

DvtDetObj.prototype.getDatatip = function(target, x, y)
{
   if (this._datatip)
    return this._datatip;
   // If a tooltip is defined, don't attempt to create a tooltip using chart utilities they sometimes 
   // return defaults which are non-null.
   if (this._tooltip)
    return null;
   // Obtain datatip
   if (this.isInitFxInProgress())
     return null;
   if(this.isUseTooltip()) {
       return DvtGraphParser.convertLineBreaks(DvtGraphEventUtils.GetTooltipText(this._Chart, this, x, y));
   }
} ;

DvtDetObj.prototype.setDatatip = function(dt)
{
   this._datatip  = DvtGraphParser.convertLineBreaks(dt) ;
} ;

DvtDetObj.prototype.getTooltip = function(target, x, y)
{
   return this._tooltip ;
} ;

DvtDetObj.prototype.setTooltip = function(tt)
{
   this._tooltip  = DvtGraphParser.convertLineBreaks(tt) ;
} ;

/*---------------------------------------------------------------------*/
/*  getTooltipText / setTooltipText()                                  */
/*---------------------------------------------------------------------*/
/**
  *   Returns specifically defined tooltip text for this object (if available).
  *   @type  String
  */
DvtDetObj.prototype.getTooltipText = function()
{
   return this._tooltipText ;
} ;
/**
  *  Set specific tooltip.
  *  @param {String} tt  Tooltip text.
  */
DvtDetObj.prototype.setTooltipText = function(tt)
{
   this._tooltipText  = tt ;
} ;


/**
  *  Returns true if specific tooltip text is available
  *  @type  boolean
  */
DvtDetObj.prototype.hasTooltipText = function()
{
   return ((this._tooltipText != null) && (this._tooltipText != undefined)) ;
} ;


/**
  *   Returns user props.
  *   @type  String
  */
DvtDetObj.prototype.getUserProps = function()
{
   return this._userProps ;
} ;
/**
  *  Set user props.
  *  @param {String} s  user props.
  */
DvtDetObj.prototype.setUserProps = function(s)
{
   this._userProps  = s ;
} ;


/*---------------------------------------------------------------------*/
/*  init()                                                             */
/*---------------------------------------------------------------------*/
/**
  *   Called once prior to first render() call to enable the object
  *   to perform any initialization required.  Associated Det object should
  *   implement init() if it needs to receive this call.
  */
DvtDetObj.prototype.init = function()
{
    if (this._DetObj.init) {
      this._DetObj.init() ;
    }

    this._applyClipPath() ;
} ;


/*---------------------------------------------------------------------*/
/*  isInitFxInProgress()                                               */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if initial display effects are in progress.
  *  @type  boolean
  */
DvtDetObj.prototype.isInitFxInProgress = function()
{
   return this._bInitFxInProgress
} ;


/*---------------------------------------------------------------------*/
/*   isMultiRiser()                                                    */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if object contains multiple riser values, such as a line
  *  or area.
  *  @type  boolean
  */
DvtDetObj.prototype.isMultiRiser = function()
{
  return this._bMultiRiser ;
};


/*---------------------------------------------------------------------*/
/*   isRefObj() / setRefObj()                                          */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if object is a reference object.
  *  @return {boolean} true if object is a reference object.
  */
DvtDetObj.prototype.isRefObj = function()
{
  return this._bRefObj ;
};

/**
  *  Sets whether the object is a reference object.
  *  @param  {boolean} bRef  True if the 'object is a reference object, else false.  
  */
DvtDetObj.prototype.setRefObj = function(bRef)
{
  this._bRefObj = bRef ;
};


/*---------------------------------------------------------------------*/
/*  isUseTooltip()                                                     */
/*---------------------------------------------------------------------*/
/**
  *   Returns true if object shows a tooltip.
  *   @type  boolean
  */
DvtDetObj.prototype.isUseTooltip = function()
{
   return this._bUseTooltip ;
} ;


/*---------------------------------------------------------------------*/
/*  setInitFx()                                                        */
/*---------------------------------------------------------------------*/
/**
  *   Enables/disables initial effects (animation on display).
  *   @param {boolean} bInitFx  True if initial effects supported, else false.
  */
DvtDetObj.prototype.setInitFx = function(bInitFx)
{
   this._bInitFx = (bInitFx || false);
} ;


/*---------------------------------------------------------------------*/
/*   setMultiRiser()                                                   */
/*---------------------------------------------------------------------*/
/**
  *  Sets the object as being multiriser (i.e contains multiple riser values,
  *  such as a line or area.
  *  @param {boolean} bMR  true if multiriser.
  */
DvtDetObj.prototype.setMultiRiser = function(bMR)
{
  this._bMultiRiser = bMR ;
};


/*---------------------------------------------------------------------*/
/*     get/setUnderlayFC()    get/setUnderlayType()                    */
/*---------------------------------------------------------------------*/
/**
  *  Returns the underlay fill color.
  *  @returns  {String}  the underlay color, or null if not specified.
  */
DvtDetObj.prototype.getUnderlayFC = function()
{
    return this._underlayClr ;
};

/**
  *  Sets the underlay fill color.
  *  @param  {String} uc  the underlay color. #aarrggbb
  */
DvtDetObj.prototype.setUnderlayFC = function(uc)
{
    this._underlayClr = uc ;
};


/**
  *  Returns the underlay type.
  *  @returns  {number}  the underlay type (such as (@link DvtMarker#SQUARE}).
  */
DvtDetObj.prototype.getUnderlayType = function()
{
    return this._underlayType ;
};

/**
  *  Sets the underlay type.
  *  @param  {number}  the underlay type (such as (@link DvtMarker#SQUARE}).
  */
DvtDetObj.prototype.setUnderlayType = function(ut)
{
    this._underlayType = ut ;
};




/*---------------------------------------------------------------------*/
/*  setUseTooltip()                                                    */
/*---------------------------------------------------------------------*/
/**
  *   Sets the Returns true if object shows a tooltip.
  *   @param {boolean} bUseTT  True if the object can generate tooltips, else false.
  */
DvtDetObj.prototype.setUseTooltip = function(bUseTT)
{
   this._bUseTooltip = bUseTT ;
} ;


/**
  *   Initial render call from the Chart. Associated displayable may
  *   implement render() and doInitFx().
  */
DvtDetObj.prototype.render = function()
{
    if (this._bInitFx && !this._bInitFxDone) {
      this._DetObj.doInitFx() ;
      this._bInitFxDone = true ;
    }

    if (this._DetObj.render) {
      this._DetObj.render() ;
    }
} ;


/*---------------------------------------------------------------------*/
/*  _Init()                                                            */
/*---------------------------------------------------------------------*/
/** @private */
DvtDetObj.prototype._Init = function()
{
   /** @private @type boolean*/
   this._bVisible          = true ;
   /** @private @type String */
   this._tooltipText       = null ;
   /** @private @type String */
   this._datatipText       = null ;
   /** @private @type boolean*/
   this._bUseTooltip       = false ;

   /** @private @type DvtGraphCore */
   this._Chart             = null ;
   /** @private @type String */
   this._objId             = null ;
   /** @private @type String */
   this._seriesId          = null ;
   /** @private @type number */
   this._seriesIdx         = -1 ;
   /** @private @type String */
   this._groupId           = null ;
   /** @private @type number */
   this._groupIdx          = -1 ;
   /** @private @type String */
   this._miscId            = -1 ;
   /** @private @type number */
   this._stackId           = null ;

   /** @private @type DvtGraphClipPath */
   this._clipPath          = null ;
   
   /** @private @type String */
   this._userProps         = null ;

   /** @private @type boolean */
   this._bInitFx           = false ;     // initFx supported
   this._bInitFxInProgress = false ;     // initFx complete

   /** @private @type boolean */
   this._bMultiRiser       = false;

   /** @private @type Array */
   this._bRefObj           = false ;     // true = this is a reference object.

   /** @private @type Array */
   this._aRefObjs          = null ;      // reference objects

   /** @private @type DvtShapeAttr */    // shape attributes
   this._shapeAttr         = null ;

   /** @private The owning det object (e.g. DvtRect) */
   this._DetObj            = null ;
};
// Copyright (c) 2008, 2012, Oracle and/or its affiliates. 
// All rights reserved. 
/*---------------------------------------------------------------------*/
/*   DvtGraphAnimation                                                 */
/*---------------------------------------------------------------------*/
/**
  *  @class DvtGraphAnimation
  *  @constructor
  */
var  DvtGraphAnimation = function()
{
  this._Init() ;
};

DvtObj.createSubclass(DvtGraphAnimation, DvtObj, "DvtGraphAnimation");

//  Animation types

/**  @type String  */
DvtGraphAnimation.NONE     = 'none' ;
/**  @type String  */
DvtGraphAnimation.AUTO     = 'auto' ;
/**  @type String  */
DvtGraphAnimation.ALL      = 'all';
/**  @type number  */
DvtGraphAnimation.ADS      = 1 ;
/**  @type number  */
DvtGraphAnimation.PPR      = 2 ;
/**  @type number  */
DvtGraphAnimation.DISPLAY  = 3 ;


//  Animation defaults

/**  @type number  */
DvtGraphAnimation.DURATION    = 1000 ;       // duration in ms

/**
  *     Initialize this instance
  */
DvtGraphAnimation.prototype._Init = function()
{
  this.animOnDataChange    =  DvtGraphAnimation.NONE;
  this.dataChangeDuration  =  DvtGraphAnimation.DURATION ;
  this.animOnDisplay       =  DvtGraphAnimation.NONE;
  this.displayDuration     =  DvtGraphAnimation.DURATION ;

  //  Indicators
  this.indicators          =  DvtGraphAnimation.ALL;
  this.upColor             =  '#0099FF' ;
  this.upAlpha             =  1 ;
  this.downColor           =  '#FF3300' ;
  this.downAlpha           =  1 ;
  this.twinkleColor        =  'FFFF00' ;
  this.twinkleAlpha        =  1 ;
};
// Copyright (c) 2008, 2012, Oracle and/or its affiliates. 
// All rights reserved. 
/*---------------------------------------------------------------------*/
/*   DvtGraphSysUtils          System level Graph package utils        */
/*---------------------------------------------------------------------*/

/**
 *   Miscellaneous graph system level routines
 *   @class DvtGraphSysUtils
 *   @constructor
 */
var DvtGraphSysUtils = function()
{} ;

DvtObj.createSubclass(DvtGraphSysUtils, DvtObj, "DvtGraphSysUtils");

/**
 *  Aggregates a framework shape into a graph det object.
 *  @param  {DvtObj}    obj    The DvtObj derivative to be aggregated.
 *  @param  {DvtGraphCore}  chart  Optional chart pointer.
 *  @param  {DvtGraphCore}  chart  Optional object Id (from {@link DvtGraphObjType}).
 */
DvtGraphSysUtils.makeDetObject = function(obj, chart, objId)
{
   var  dlo   = new DvtDetObj() ;

   dlo.setDetObj(obj) ;      // ptr to owning obj
   obj._Dlo   = dlo ;        // object to dlo pointer

   if (chart) {
     dlo.setChart(chart) ;
     chart.addDLO(dlo);
   }
   
   if (objId !== undefined) {
     dlo.setObjId(objId) ;
   }
} ;
// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*---------------------------------------------------------------------*/
/*  DvtHandleManager     singleton object managing handle instances    */
/*---------------------------------------------------------------------*/
/**
 *  @extends DvtObj
 *  @class DvtHandleManager is a singleton object for managing {@link DvtHandle}
 *         instances
 *  <code>
 *  @constructor
 */
var DvtHandleManager = function (chart) {
  this.Init(chart);
};

DvtObj.createSubclass(DvtHandleManager, DvtObj, "DvtHandleManager");

//
// Constants
//
DvtHandleManager.C_SCROLL = "s";
DvtHandleManager.C_RESIZE = "r";

DvtHandleManager.SCROLL_START = "__START__";
DvtHandleManager.SCROLL_SPAN  = "__SPAN__";
DvtHandleManager.SCROLL_TOTAL = "__TOTAL__";
DvtHandleManager.TOK_TYPE     = "__T__";    // type s=scroll/move, r=resize

/**
  *  Object initializer.
  *  @protected
  */
DvtHandleManager.prototype.Init = function (chart) {
    this._Chart = chart;
    this._handleList = new Array();

    this._hPA = null;
    this._hX1L = null;
    this._hY1PA = null;
    this._hY2PA = null;
    this._hOL = null;
    this._hY1L = null;
    this._hY2L = null;

    this._x1rs = null;
    this._y1rs = null;
    this._y2rs = null;
    this._o1rs = null;

    this._fullPlotPoints = null;
    this._fullPlotWidth  = 0;
    this._fullPlotHeight = 0;

    this._fullPlotPointsY2 = null;
    this._fullPlotWidthY2  = 0;
    this._fullPlotHeightY2 = 0;

    // We need to know when mouse up occurs even outside the scrollbar arrow 
    chart.addEvtListener(DvtMouseEvent.MOUSEUP, this.onGlobalMouseUp, false, this);

}



/**
 * Adds a new handle to the list of managed handles
 */
DvtHandleManager.prototype.addHandle = function(handle) {

    this._handleList.push(handle);

    switch(handle.getId()) {
        case DvtGraphHandle.ID_PA:
            this._hPA = handle;
            break;
        case DvtGraphHandle.ID_X1L:
            this._hX1L = handle;
            break;
        case DvtGraphHandle.ID_Y1PA:
            this._hY1PA = handle;
            break;
        case DvtGraphHandle.ID_Y2PA:
            this._hY2PA = handle;
            break;
        case DvtGraphHandle.ID_OL:
            this._hOL = handle;
            break;
        case DvtGraphHandle.ID_Y1L:
            this._hY1L = handle;
            break;
        case DvtGraphHandle.ID_Y2L:
            this._hY2L = handle;
            break;
    }

    if (handle.getId() == DvtGraphHandle.ID_PA
            && handle.getBoundingPoints() != null)
    {
        this._fullPlotPoints = handle.getBoundingPoints();
        this._fullPlotWidth  = this._fullPlotPoints[2] - this._fullPlotPoints[0];
        this._fullPlotHeight = this._fullPlotPoints[3] - this._fullPlotPoints[1];
    }
    else if (handle.getId() == DvtGraphHandle.ID_Y2PA
            && handle.getBoundingPoints() != null)
    {
        this._fullPlotPointsY2 = handle.getBoundingPoints();
        this._fullPlotWidthY2  = this._fullPlotPointsY2[2] - this._fullPlotPointsY2[0];
        this._fullPlotHeightY2 = this._fullPlotPointsY2[3] - this._fullPlotPointsY2[1];
    }

}


/**
 * Adds a reference to a DvtScrollbar to this handle manager. Required
 * for programmatic scroll support.
 */
DvtHandleManager.prototype.addScrollbar = function(sb) {
    var thumb = sb.getScrollbarThumb();
    DvtGraphSysUtils.makeDetObject(thumb, sb._Dlo._Chart);
    
    switch(sb._uiId) {    // TODO: change to a method call!
        case DvtScrollbar.ID_X1RS:
            this._x1rs = sb;
            break;
        case DvtScrollbar.ID_Y1RS:
            this._y1rs = sb;
            break;
        case DvtScrollbar.ID_Y2RS:
            this._y2rs = sb;
            break;
        case DvtScrollbar.ID_O1RS:
            this._o1rs = sb;
            break;
    }

    // register scrollbar listeners (TODO: should be done on Chart instead?)
    sb.addEvtListener(DvtScrollbarEvent.SB_MOVE, this.onScrollbarMove, false, this);
    sb.addEvtListener(DvtScrollbarEvent.SB_MOVE_COMPLETE, this.onScrollbarMoveComplete, false, this);
    sb.addEvtListener(DvtScrollbarEvent.SB_RESIZE_COMPLETE, this.onScrollbarResizeComplete, false, this);
    
}


/**
 * Returns true if this handle manager has any associated scrollbars.
 */
DvtHandleManager.prototype.hasScrollbars = function() {
    if (this._o1rs || this._x1rs || this._y1rs || this._y2rs)
        return true;
    else
        return false;
}

/**
 * mouse up event handler
 */
DvtHandleManager.prototype.onGlobalMouseUp = function(event) {
    if (this._o1rs)
        this._o1rs.processGlobalMouseUp();
    if (this._x1rs)
        this._x1rs.processGlobalMouseUp();
    if (this._y1rs)
        this._y1rs.processGlobalMouseUp();
    if (this._y2rs)
        this._y2rs.processGlobalMouseUp();
}

/**
 * scrollbarMove event handler
 */
DvtHandleManager.prototype.onScrollbarMove = function(event) {
    
    var movedDelta = event.getMovedDelta();

    if (event.type != DvtScrollbarEvent.SB_MOVE || movedDelta == 0)
        return;

    if (this._Chart.isMagnifyLensEnabled()) {
        var magnifyLens =  this._Chart.getCtx().getCustomTooltipManager().getMagnifyLens();
        magnifyLens.setDirty(true);
    }

    var scaledDelta = event.getScaledDelta();
    var sb = event.getScrollbar();

    var width = this._fullPlotWidth;
    var height = this._fullPlotHeight;

    if (sb.getUIId() == DvtScrollbar.ID_Y2RS) {
        width = this._fullPlotWidthY2;
        height = this._fullPlotHeightY2;
    }

    var offset;
    
    if (sb.isHorizontal())
        offset = scaledDelta * width;
    else
        offset = scaledDelta * height;
    
    //bug 13552685    
    if (offset > 0)
        offset = Math.ceil(offset);
    else
        offset = Math.floor(offset);

    switch (event.getScrollbarId()) {
        case DvtScrollbar.ID_X1RS:  // scroll PA and X1L handles
            this._scrollXAxis(offset, sb);
            break;
        case DvtScrollbar.ID_O1RS:
            this._scrollOAxis(offset, sb);
            break;
        case DvtScrollbar.ID_Y1RS:
            this._scrollYAxis(offset, sb);
            break;
        case DvtScrollbar.ID_Y2RS:
            this._scrollY2Axis(offset, sb);
            break;
    }

    // TODO: updateBackgroundEraser()  -- needed ?
}


/**
 * scrollbarMoveComplete event handler
 */
DvtHandleManager.prototype.onScrollbarMoveComplete = function(event) {
    this._emitMTEvent(event);
}

/**
 * scrollbarResizeComplete event handler
 */
DvtHandleManager.prototype.onScrollbarResizeComplete = function(event) {
    this._emitMTEvent(event);
}

/**
 * Emits an event to the middle-tier
 */
DvtHandleManager.prototype._emitMTEvent = function(event) {

    var sb = event.getScrollbar();
    var sJS;
    var cType;

    if (event.type == DvtScrollbarEvent.SB_MOVE_COMPLETE) {
        cType = DvtHandleManager.C_SCROLL;
        sJS = sb.getScrollEventJs();
    }
    else if (event.type == DvtScrollbarEvent.SB_RESIZE_COMPLETE) {
        cType = DvtHandleManager.C_RESIZE;
        sJS = sb.getZoomEventJs();
    }

    if (!sJS) {
        return;
    }

    var thumbDisp = event.getStartCoord();
    var thumbSize = event.getEndCoord() - thumbDisp;
    var scrollbarSize = sb.getScrollAreaLength();

    // flip start thumb position for vertical scrollbars
    if (!sb.isHorizontal()) {
        thumbDisp = scrollbarSize - thumbSize - thumbDisp;
    }

    // TODO: should use StrUtils token replace and EventTokens constants when available

    var sJS1 = sJS.replace(DvtHandleManager.SCROLL_START, thumbDisp);
    var sJS2 = sJS1.replace(DvtHandleManager.SCROLL_SPAN, thumbSize);
    var sJS3 = sJS2.replace(DvtHandleManager.SCROLL_TOTAL, scrollbarSize);
    var finalJS = sJS3.replace(DvtHandleManager.TOK_TYPE, cType);

    // execute the JS code
    //BUG 34184877 - MAKE DVT COMPATIBLE WITH CSP STANDARDS
    var jsArray = finalJS.match(/'(.*?)'/g);
    var src = jsArray[0].replace(/\'/g, "");
    var eventData = jsArray[2].replace(/\'/g, "");
    var bi_gdAData = jsArray[3].replace(/\'/g, "");
    var bi_gdsStData = jsArray[4].replace(/\'/g, "");
    var bi_gdsSpData = jsArray[5].replace(/\'/g, "");
    var bi_gdsCData = jsArray[6].replace(/\'/g, "");
    var bi_gdsTData = jsArray[7].replace(/\'/g, "");
     
    AdfPage.PAGE.findComponent(src).getPeer().sendToServerPPR2({source:src,event: eventData ,bi_gdsA: bi_gdAData ,bi_gdsSt: bi_gdsStData ,bi_gdsSp: bi_gdsSpData ,bi_gdsC: bi_gdsCData ,bi_gdsT: bi_gdsTData });

}

/**
 * Scrolls the plotArea on the O-axis by the specified number of pixels.
 *
 * @param offset  the number of pixels to scroll the plot area
 * @param sb      the scrollbar for this axis
 */
DvtHandleManager.prototype._scrollOAxis = function(offset, sb) {

    if (this._hPA == null || this._hPA.getBoundingPoints() == null)
        return;     // bounds must be specified for scrolling to work

    var bounds = this._hPA.getBoundingPoints();
    var boundedOffset;
    var frameRect = this._Chart.getFrameRect();

    if (sb.isHorizontal()) {
        // figure out the offset bounded by the plot area
        boundedOffset = this._getBoundedOffset(offset, bounds[0], bounds[2], frameRect.x, frameRect.x + frameRect.w);

        this._hPA.moveX(-boundedOffset);
        if (this._hOL != null)
            this._hOL.moveX(-boundedOffset);
        if (this._hY2PA != null)
            this._hY2PA.moveX(-boundedOffset);
    }
    else {
        // figure out the offset bounded by the plot area
        boundedOffset = this._getBoundedOffset(offset, bounds[1], bounds[3], frameRect.y, frameRect.y + frameRect.h);

        this._hPA.moveY(-boundedOffset);
        if (this._hOL != null)
            this._hOL.moveY(-boundedOffset);
        if (this._hY2PA != null)
            this._hY2PA.moveY(-boundedOffset);
    }
}

/**
 * Scrolls the plotArea on the x-axis by the specified number of pixels.
 *
 * @param offset  the number of pixels to scroll the plot area
 * @param sb      the scrollbar for this axis
 */
DvtHandleManager.prototype._scrollXAxis = function(offset, sb) {

    if (this._hPA == null || this._hPA.getBoundingPoints() == null)
        return;     // bounds must be specified for scrolling to work

    var bounds = this._hPA.getBoundingPoints();
    var boundedOffset;
    var frameRect = this._Chart.getFrameRect();

    if (sb.isHorizontal()) {
        // figure out the offset bounded by the plot area
        boundedOffset = this._getBoundedOffset(offset, bounds[0], bounds[2], frameRect.x, frameRect.x + frameRect.w);

        this._hPA.moveX(-boundedOffset);
        if (this._hX1L != null)
            this._hX1L.moveX(-boundedOffset);
        if (this._hY2PA != null)
            this._hY2PA.moveX(-boundedOffset);

        // TODO: also update the min axis coord ???
        // chart.m_xPxMin -= boundedOffset
    }
    else {
        // figure out the offset bounded by the plot area
        boundedOffset = this._getBoundedOffset(offset, bounds[1], bounds[3], frameRect.y, frameRect.y + frameRect.h);

        this._hPA.moveY(-boundedOffset);
        if (this._hX1L != null)
            this._hX1L.moveY(-boundedOffset);
        if (this._hY2PA != null)
            this._hY2PA.moveY(-boundedOffset);

        // TODO: also update the min axis coord ???
    }
}

/**
 * Scrolls the plotArea on the y-axis by the specified number of pixels.
 *
 * @param offset  the number of pixels to scroll the plot area
 * @param sb      the scrollbar for this axis
 */
DvtHandleManager.prototype._scrollYAxis = function(offset, sb) {

    if (this._hPA == null || this._hPA.getBoundingPoints() == null)
        return;     // bounds must be specified for scrolling to work

    var bounds = this._hPA.getBoundingPoints();
    var clipRect = this._hPA.getClipRect();
    var boundedOffset;

    if (clipRect == null)
        return;

    if (sb.isHorizontal()) {
        // figure out the offset bounded by the plot area
        boundedOffset = this._getBoundedOffset(offset, bounds[0], bounds[2], clipRect.x, clipRect.x + clipRect.w);

        this._hPA.moveX(-boundedOffset);
        if (this._hY1L != null)
            this._hY1L.moveX(-boundedOffset);

        // TODO: also update the min axis coord ???
    }
    else {
        // figure out the offset bounded by the plot area
        boundedOffset = this._getBoundedOffset(offset, bounds[1], bounds[3], clipRect.y, clipRect.y + clipRect.h);

        this._hPA.moveY(-boundedOffset);
        if (this._hY1L != null)
            this._hY1L.moveY(-boundedOffset);

        // TODO: also update the min axis coord ???
        // chart.m_yPxMin -= boundedOffset
    }

}

/**
 * Scrolls the plotArea on the y2-axis by the specified number of pixels.
 *
 * @param offset  the number of pixels to scroll the plot area
 * @param sb      the scrollbar for this axis
 */
DvtHandleManager.prototype._scrollY2Axis = function(offset, sb) {

    if (this._hY2PA == null || this._hY2PA.getBoundingPoints() == null)
        return;     // bounds must be specified for scrolling to work

    var bounds = this._hY2PA.getBoundingPoints();
    var clipRect = this._hY2PA.getClipRect();
    var boundedOffset;

    if (clipRect == null)
        return;

    if (sb.isHorizontal()) {
        // figure out the offset bounded by the plot area
        boundedOffset = this._getBoundedOffset(offset, bounds[0], bounds[2], clipRect.x, clipRect.x + clipRect.w);

        this._hY2PA.moveX(-boundedOffset);
        if (this._hY2L != null)
            this._hY2L.moveX(-boundedOffset);
    }
    else {
        // figure out the offset bounded by the plot area
        boundedOffset = this._getBoundedOffset(offset, bounds[1], bounds[3], clipRect.y, clipRect.y + clipRect.h);

        this._hY2PA.moveY(-boundedOffset);
        if (this._hY2L != null)
            this._hY2L.moveY(-boundedOffset);
    }
}

/**
 * Returns the offset bounded by the frameStart and frameEnd coords given
 */
DvtHandleManager.prototype._getBoundedOffset = function(offset, handleStart, handleEnd, frameStart, frameEnd) {
    var boundedOffset = offset;
    if (handleStart - offset >= frameStart)
        boundedOffset = handleStart - frameStart;
    else if (frameEnd > handleEnd - offset)
        boundedOffset = handleEnd - frameEnd;

    return boundedOffset;
}


    
/**
* Scrolls the plot area by the given number of pixels on each axis.
* @param offsetPctO the offset to scroll in pixels 
* @param offsetPctX the offset to scroll in pixels
* @param offsetPctY the offset to scroll in pixels
* @param offsetPctY2 the offset to scroll in pixels
* @param clickPoint if triggered by a mouse event, this is the absolute coord for the click
* @param dispatchEvent true if a move event should be fired
* @param complete true if the scrolling operation is complete
*/ 
DvtHandleManager.prototype.scrollPlotAreaOffset = function(offsetO, offsetX, offsetY, offsetY2, clickPoint, dispatchEvent, complete) 
    {
      // Delta in percentage of axis
      var deltaO = 0;  
      var deltaX = 0;
      var deltaY = 0;
      var deltaY2 = 0;
      // Scroll on the O-Axis
      if(offsetO != 0 && this._o1rs != null) {
        if(this._o1rs.isHorizontal())
          deltaO = 100*offsetO/this._fullPlotWidth;
        else
          deltaO = 100*offsetO/this._fullPlotHeight;
      }
      
      // Scroll on the X-Axis
      if(offsetX != 0 && this._x1rs != null) {
        if(this._x1rs.isHorizontal())
          deltaX = 100*offsetX/this._fullPlotWidth;
        else
          deltaX = 100*offsetX/this._fullPlotHeight;
      }
      
      // Scroll on the Y-Axis
      if(offsetY != 0 && this._y1rs != null) {
      	var clipRect = this._hPA.getClipRect();
      	if (clickPoint != null && clipRect != null) {
      		// For split dual y, target correct handle only 
      		if (clickPoint.x < clipRect.x || clickPoint.x > (clipRect.x+clipRect.width) ||
      		    clickPoint.y < clipRect.y || clickPoint.y > (clipRect.y+clipRect.height))
      		    offsetY = 0;
      	}
      	
        if(this._y1rs.isHorizontal())
          deltaY = 100*offsetY/this._fullPlotWidth;
        else
          deltaY = 100*offsetY/this._fullPlotHeight;
      }
      
      // Scroll on the Y2-Axis
      if(offsetY2 != 0 && this._y2rs != null) {
      	var clipRectY2 = this._hY2PA.getClipRect();
      	if (clickPoint != null && clipRectY2 != null) {
      		// For split dual y, target correct handle only
      		if (clickPoint.x < clipRectY2.x || clickPoint.x > (clipRectY2.x+clipRectY2.width) ||
      		    clickPoint.y < clipRectY2.y || clickPoint.y > (clipRectY2.y+clipRectY2.height))
      		    offsetY2 = 0;
      	}
      	
        if(this._y2rs.isHorizontal())
          deltaY2 = 100*offsetY2/this._fullPlotWidthY2;
        else
          deltaY2 = 100*offsetY2/this._fullPlotHeightY2;
      }
      
      this.scrollPlotAreaPercentage(deltaO, deltaX, deltaY, deltaY2, dispatchEvent, complete);
    }

    /**
    * Scrolls the plot area by the given percentage on each axis.
    * @param offsetPctO the offset to scroll in percentage of the axis
    * @param offsetPctX the offset to scroll in percentage of the axis
    * @param offsetPctY the offset to scroll in percentage of the axis
    * @param offsetPctY2 the offset to scroll in percentage of the axis
    */ 
DvtHandleManager.prototype.scrollPlotAreaPercentage = function(offsetPctO, offsetPctX, offsetPctY, offsetPctY2, dispatchEvent, complete) 
    {
      var delta = null; // delta in coords
      // Scroll on the O-Axis
      if(offsetPctO != 0 && this._o1rs != null) {
        if(this._o1rs.isHorizontal())
          delta = (offsetPctO/100)*this._fullPlotWidth;
        else
          delta = (offsetPctO/100)*this._fullPlotHeight;
          
        // scroll the plot area
        this._scrollOAxis(delta, this._o1rs); 
        
        // Update the Range Slider
        this._o1rs.move(offsetPctO, dispatchEvent, complete);
      }
      
      // Scroll on the X-Axis
      if(offsetPctX != 0 && this._x1rs != null) {
        if(this._x1rs.isHorizontal())
          delta = (offsetPctX/100)*this._fullPlotWidth;
        else
          delta = (offsetPctX/100)*this._fullPlotHeight;
          
        // scroll the plot area
        this._scrollXAxis(delta, this._x1rs); 
        
        // Update the Range Slider
        this._x1rs.move(offsetPctX, dispatchEvent, complete);
      }
      
      // Scroll on the Y-Axis
      if(offsetPctY != 0 && this._y1rs != null) {
        if(this._y1rs.isHorizontal())
          delta = (offsetPctY/100)*this._fullPlotWidth;
        else
          delta = (offsetPctY/100)*this._fullPlotHeight;
          
        // scroll the plot area
        this._scrollYAxis(delta, this._y1rs); 
        
        // Update the Range Slider
        this._y1rs.move(offsetPctY, dispatchEvent, complete);
      }
      
      // Scroll on the Y2-Axis
      if(offsetPctY2 != 0 && this._y2rs != null) {
        if(this._y2rs.isHorizontal())
          delta = (offsetPctY2/100)*this._fullPlotWidthY2;
        else
          delta = (offsetPctY2/100)*this._fullPlotHeightY2;
          
        // scroll the plot area
        this._scrollY2Axis(delta, this._y2rs); 
        
        // Update the Range Slider
        this._y2rs.move(offsetPctY2, dispatchEvent, complete);
      }
      //updateBackgroundEraser();
    }
    
/**
 * Dispatches move events to JS. 
 * @param dispatchEvent true if a move event should be fired
 * @param complete true if the scrolling operation is complete
 */ 
DvtHandleManager.prototype.dispatchJSEvents = function() {
    if(this._o1rs != null)
        this._o1rs.move(0, true, true);
    if(this._x1rs != null)
        this._x1rs.move(0, true, true);
    if(this._y1rs != null)
        this._y1rs.move(0, true, true);
    if(this._y2rs != null)
        this._y2rs.move(0, true, true);
} 

/*-----------------------------------------------------------------------*/
/*     getHandle()       Returns a Handle object with the specified ID   */
/*                       Returns null if not found.                      */
/*-----------------------------------------------------------------------*/
DvtHandleManager.prototype.getHandle = function(handleId) {
    switch(handleId) {
        case DvtGraphHandle.ID_PA:
            return this._hPA;
        case DvtGraphHandle.ID_X1L:
            return this._hX1L;
        case DvtGraphHandle.ID_Y1PA:
            return this._hY1PA;
        case DvtGraphHandle.ID_Y2PA:
            return this._hY2PA;
        case DvtGraphHandle.ID_OL:
            return this._hOL;
        case DvtGraphHandle.ID_Y1L:
            return this._hY1L;
        case DvtGraphHandle.ID_Y2L:
            return this._hY2L;
        default:
            return null;
    }
    return null;
}
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
/*--------------------------------------------------------------------------*/
/*  Abstract base event handler for graph (traditional and AMX)  */
/*--------------------------------------------------------------------------*/
var  DvtGraphEventHandler = function()
{
};

DvtObj.createSubclass(DvtGraphEventHandler, DvtEventManager, "DvtGraphEventHandler");

DvtGraphEventHandler.prototype.Init = function(context, callback, callbackObj) {
   DvtGraphEventHandler.superclass.Init.call(this, context, callback, callbackObj);
   
   this.SeriesRolloverHandler = null;
   this.HideShowSeriesHandler = null;
   this.DataCursorHandler = null;
   this.GraphPanHandler = null;
   this.GraphKeyboardHandler = null;
   this.SeriesFocusHandler = null;
}

DvtGraphEventHandler.prototype.setSeriesRolloverHandler = function(handler) {
  this.SeriesRolloverHandler = handler;
}

DvtGraphEventHandler.prototype.setHideShowSeriesHandler = function(handler) {
  this.HideShowSeriesHandler = handler;
}

DvtGraphEventHandler.prototype.setDataCursorHandler = function(handler) {
  this.DataCursorHandler = handler;
}

DvtGraphEventHandler.prototype.setGraphPanHandler = function(handler) {
  this.GraphPanHandler = handler;
}

DvtGraphEventHandler.prototype.setSeriesFocusHandler = function(handler) {
  this.SeriesFocusHandler = handler;
}

/**
 * Sets the graph keyboard handler that this DvtMEH dispatches keyboard events to
 * 
 * @param {DvtGraphKeyboardHandler} handler
 */
DvtGraphEventHandler.prototype.setGraphKeyboardHandler = function(handler) {
  this.GraphKeyboardHandler = handler;
}

DvtGraphEventHandler.prototype.HandleTouchHoverStartInternal = function(event) {
    var touch = event.touch;
    var targetObj = event.target;
    var touchX = touch.pageX;
    var touchY = touch.pageY;
    var pos = this._context.getRelativePosition(touchX, touchY);
    var dlo = this.GetLogicalObject(targetObj);
    var touchId = touch.identifier;
    this.TouchManager.setTooltipEnabled(touchId, this.IsTooltipShowable(dlo, pos));

    this.HandleTouchHoverStartGraphInternal(event);

    if(this.DataCursorHandler)
      this.DataCursorHandler.processMove(touchX, touchY, targetObj, dlo);                         
    
    return false;
}

DvtGraphEventHandler.prototype.HandleTouchHoverStartGraphInternal = function(event) {
}

DvtGraphEventHandler.prototype.HandleTouchHoverMoveInternal = function(event) {
    var touch = event.touch;
    var targetObj = event.target;
    var touchX = touch.pageX;
    var touchY = touch.pageY
    var pos = this._context.getRelativePosition(touchX, touchY);
    var dlo = this.GetLogicalObject(targetObj);
    var touchId = touch.identifier;

    if (this.DataCursorHandler) {
        var cursorShown = this.DataCursorHandler.processMove(touchX, touchY, targetObj);                    
    }
    this.TouchManager.setTooltipEnabled(touchId, this.IsTooltipShowable(dlo, pos));

    this.HandleTouchHoverMoveGraphInternal(event);

    return false;
}

DvtGraphEventHandler.prototype.HandleTouchHoverMoveGraphInternal = function(event) {
}

DvtGraphEventHandler.prototype.HandleTouchHoverEndInternal = function(event) {
    var touch = event.touch;
    var targetObj = event.target;     
    var dlo = this.GetLogicalObject(targetObj);
    
    if (this.DataCursorHandler) {
         this.DataCursorHandler.processEnd();  
    }

    var hideShowPopupHandled = this._processHideShowPopup(dlo);
    if (hideShowPopupHandled) {
        var info = this.TouchManager.getTouchInfo(touch.identifier);
        if (info)
            info.fireClick = false;
    }
    
    if(this.SeriesRolloverHandler) 
      this.SeriesRolloverHandler.processMouseOut(dlo);    
    
    this.HandleTouchHoverEndGraphInternal(event);
} 

DvtGraphEventHandler.prototype.HandleTouchHoverEndGraphInternal = function(event) {
}

DvtGraphEventHandler.prototype.HandleTouchHoverOverInternal = function(event) {
    var targetObj = event.target;     
    var dlo = this.GetLogicalObject(targetObj);
    
    if(this.SeriesRolloverHandler) {
      this.SeriesRolloverHandler.processMouseOver(dlo);

      if (this.IsMagnifyLensEnabled()) {
          if (dlo) {
              var magLens = this.GetMagnifyLens();
              var categories = dlo.getCategories();
              if(categories[0] !== null) {
                magLens.setDirty(true);
              }
          }
      }
    }
    this.HandleTouchHoverOverGraphInternal(event);
}

DvtGraphEventHandler.prototype.HandleTouchHoverOverGraphInternal = function(event) {
}

DvtGraphEventHandler.prototype.HandleTouchHoverOutInternal = function(event) {
    var targetObj = event.target;     
    var dlo = this.GetLogicalObject(targetObj);
    
    if(this.SeriesRolloverHandler) 
      this.SeriesRolloverHandler.processMouseOut(dlo);

    this.HandleTouchHoverOutGraphInternal(event);
}

DvtGraphEventHandler.prototype.HandleTouchHoverOutGraphInternal = function(event) {
}

DvtGraphEventHandler.prototype.HandleTouchClickInternal = function(evt) {
    var touch = evt.touch;
    var pos = this._context.getRelativePosition(touch.pageX, touch.pageY);
    var targetObj = evt.target;
    var touchEvent = evt.touchEvent;

    var dlo = this.GetLogicalObject(targetObj);
    if (this.SeriesFocusHandler)
        this.SeriesFocusHandler.processSeriesFocus(pos, dlo);
    var done = this.HandleTouchClickGraphInternal(evt);
    return done;
}

DvtGraphEventHandler.prototype.HandleTouchClickGraphInternal = function(event) {
}

/** @private */
DvtGraphEventHandler.prototype._processHideShowPopup = function(dlo) {
    return false;
}

/*
 * Subclasses override
 */
DvtGraphEventHandler.prototype.IsMagnifyLensEnabled = function()
{
    return false;
}
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
/*--------------------------------------------------------------------------*/
/*  MasterEventHandler     Global First-Level Event Handler for all Charts  */
/*--------------------------------------------------------------------------*/
/**
  *  @class DvtMEH
  *  @extends DvtObj
  *  @constructor
  */
var  DvtMEH = function(chart)
{
    this.Init(chart);
};

DvtObj.createSubclass(DvtMEH, DvtGraphEventHandler, "DvtMEH");

DvtMEH.HIDE_SERIES_ACTION_TYPE = "hideSeriesActionType";
DvtMEH.SHOW_SERIES_ACTION_TYPE = "showSeriesActionType";

DvtMEH.prototype.Init = function(chart) {
   DvtMEH.superclass.Init.call(this, chart.getCtx(), chart.__dispatchEvent, chart);
   this.__arEVHList    = null ;                // list of Chart's Event Manager's
   this._Chart = chart;
   this._graphMarqueeHandler = null;
}

/**
 * @override
 */
DvtMEH.prototype.GetLogicalObjectAndDisplayable = function(target, ignoreParents) {
  var ret = DvtMEH.superclass.GetLogicalObjectAndDisplayable.call(this, target, ignoreParents);
  if (ret) {
    return ret;
  } else if (target) {
    ret = target._Dlo;
    var retObj = {};
    retObj.logicalObject = ret;
    retObj.displayable = target;
    return retObj;
  }
  return null;
}

/**
 * @override
 */
DvtMEH.prototype.GetTooltipColor = function(obj, x, y) {
  var ret = DvtMEH.superclass.GetTooltipColor.call(this, obj);
  if(ret)
    return ret;

  var sidx;
  if (this._Chart.isArea() && x != null && y != null) {
      var mth = this._Chart.getMarkerTooltipHelper();
      if (mth) {
          var match = mth.findMatch(x, y);
          // bug 12805696: there's no match for the scrollbar tooltip
          // but it's OK, to pass 'undefined' to Chart.getSeriesColor
          // - will default to 'black'
          if (match)
              sidx = match.sidx;
      }
      else
        sidx = obj.getSeriesIndex();
  }
  else 
     sidx = obj.getSeriesIndex();
    
  var tipColor =  this._Chart.getSeriesColor(sidx);
  
  if (obj._objId && obj._objId.indexOf("BR")==0){
      
        var color = obj._DetObj.getImpl().getFill();
        if (color instanceof DvtSolidFill){
            tipColor = color.getColor();
        }
        else if (color instanceof DvtGradientFill && DvtArrayUtils.getIndex(color.getColors(), tipColor)<0){
            tipColor = color.getColors()[0];
        }
  }
    
  return tipColor;
}

/*---------------------------------------------------------------------*/
/*   addEventHandler()                                                 */
/*---------------------------------------------------------------------*/
/**
 *    Adds a new event handler to the list of registered event managers.
 *    @param {DvtObj}  the The event handler to be added.
 */
DvtMEH.prototype.addEventHandler = function(eh)
{
   if (eh) {
     if (! this._arEVHList) {
       this._arEVHList = [] ;
     }
     this._arEVHList.push(eh) ;
   }
};


/*---------------------------------------------------------------------*/
/*    destroy()                                                        */
/*---------------------------------------------------------------------*/
/*
 *    Releases all resources owned to prevent memory leaks.
 */
DvtMEH.prototype.destroy = function()
{
  DvtMEH.superclass.destroy.call(this);
}


/*--------------------------------------------------------------------------*/
/*   OnMouseEventTemp()           First-Level Mouse Event Handler              */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  The current logic is as follows :                                       */
/*  2)  A list of objects under the mouse point is obtained.                */
/*                                                                          */
/*  3)  The event target is used to find the Chart pointer.  This is used   */
/*      to select a ChartEventManager object from the MasterEventHandler's  */
/*      list.  The event is then passed to the selected Chart event handler */
/*      to distribute to all its registered event handlers.                 */
/*                                                                          */
/*  4)  After the event has been distributed to the chart event handlers,   */
/*      for all objects under the mouse, a test is made to see if they      */
/*      implement any of the iMouseXXX interfaces. If they do, the relevent */
/*      event interface method in the interface is called for the object.   */
/*                                                                          */
/*      Before the IMouseEvent is dispatched to the interface implementer   */
/*      two additional tests are made:                                      */
/*         a)  Does the Chart permit events being sent to any member        */
/*         b)  Does the recipient object permit events to be sent to it     */
/*             even though it implements IMouseXXX                          */
/*                                                                          */
/*   Note:  One issue to be addressed is that the target field of an event  */
/*          is a read-only property.  Therefore when the event is dis-      */
/*          tributed to all implementers of IMouseEvents, the target field  */
/*          will point to the top object.  This is not currently an issue,  */
/*          but in the future we might need to clone the event so that we   */
/*          change the target field.                                        */
/*--------------------------------------------------------------------------*/
// TODO OnMouseEventTemp will be removed once the event handler refactor is complete.
DvtMEH.prototype.OnMouseEventTemp = function(evt)
{
  //-----------------------------------------------------//
  //  Distribute event to event handlers for this chart  //
  //-----------------------------------------------------//

  if (this._arEVHList) {
    var len = this._arEVHList.length ;
    var ar  = this._arEVHList;

    for (var i = 0; i < len; i++) {
      eh = ar[i] ;

      if (eh && (! eh.newMouseEvent(evt))) {  // return of false means
        break ;                               // terminate event distribution
      }
    }       // end for
  }     // end if (event list)
};

//*******************************************************************************//
//*********************** Begin Event Listeners *********************************//
//*******************************************************************************//

/**
 * @override
 */
DvtMEH.prototype.OnClickInternal = function(event) {
  
  var pos = this._context.getRelativePosition(event.pageX, event.pageY);
  var dlo = this.GetLogicalObject(event.target);
  var focusHandled = false;
  if (this.SeriesFocusHandler) {
      this.SeriesFocusHandler.processSeriesFocus(pos, dlo);
      focusHandled = this.SeriesFocusHandler.isSeriesFocusHandled();
  }
  if (!focusHandled) {
      // TODO Temporarily delegate to OnMouseEventTemp until this masterEventHandler is cleaned up
      this.OnMouseEventTemp(event);
  }

  // Shape Attributes Support
  DvtShapeAttr.processEvent(dlo, event.target, event.type);

}

/**
 * @override
 */
DvtMEH.prototype.OnClick = function(event) 
{
  DvtMEH.superclass.OnClick.call(this, event) ;  

  var logicalObject = this.GetLogicalObject(event.target);
  
  this._processPieExplode(logicalObject);
  
  var zb = this._Chart.getZoomButton();
  if (zb)
  zb.resetButton();
}

/**
 * @override
 * @param {DvtKeyboardEvent} event
 */
DvtMEH.prototype.OnKeyDown = function(event)
{ 
  // this logic ported over from DataZoom.as in Flash
  if(event.keyCode == DvtKeyboardEvent.CONTROL)
  {

    if(this._Chart.getMarqueeZoomMode() == DvtGraphMarqueeZoomHandlerBase.ZOOM_AUTO)
    {
      if(this._graphMarqueeHandler && !this._graphMarqueeHandler.isMarqueeHappening())
      {
        this._graphMarqueeHandler.enableMarquee(true);
      
        if(this.GraphPanHandler)
        {
          this.GraphPanHandler.enablePanning(false);
        }
        
        this._graphMarqueeHandler.updateCursor();
      }
    }   
  }
  else if(this._graphMarqueeHandler && this._graphMarqueeHandler.isMarqueeEnabled() &&
          event.keyCode == DvtKeyboardEvent.ESCAPE)
  {
    // Cancel zoom on ESC
    if(this._graphMarqueeHandler && this._graphMarqueeHandler.isMarqueeHappening())
      this._graphMarqueeHandler.cancelMarqueeZoom();
  }
  else if(this._Chart.getMarqueeZoomMode() != DvtGraphMarqueeZoomHandlerBase.ZOOM_OFF &&
          event.keyCode == DvtKeyboardEvent.SHIFT)
  {
    // toggle between ZoomIn vs ZoomOut on SHIFT
    if(this._graphMarqueeHandler)
    {
      this._graphMarqueeHandler.processShiftZoom(true);
    }         
  }
  else if( (this._graphMarqueeHandler ? !this._graphMarqueeHandler.isMarqueeHappening() : true) &&
           (this.GraphPanHandler ? !this.GraphPanHandler.isCurrentlyPanning() : true) &&
           (this._Chart.getPanMode != DvtGraphPanHandler.PAN_OFF) )
  {
    if(this.GraphKeyboardHandler)
    {
      // on keyDown for pageUp and pageDown, pan the graph
      this.GraphKeyboardHandler.processKeyDown(event);
    }    
  }


}

/**
 * @override
 * @param {DvtKeyboardEvent} event
 */
DvtMEH.prototype.OnKeyUp = function(event)
{
  // this logic ported over from DataZoom.as in Flash
  if(event.keyCode == DvtKeyboardEvent.CONTROL)
  {
    if(this._Chart.getMarqueeZoomMode() == DvtGraphMarqueeZoomHandlerBase.ZOOM_AUTO)
    {
      if(this._graphMarqueeHandler)
      {
        this._graphMarqueeHandler.enableMarquee(false);
        this._graphMarqueeHandler.removeCursor();
        
        // Treat this as a zoom cancel
        if(this._graphMarqueeHandler.isMarqueeHappening())
          this._graphMarqueeHandler.cancelMarqueeZoom();
      }
        
      if(this.GraphPanHandler)
        this.GraphPanHandler.enablePanning(true);
    }
  }
  else if(this._Chart.getMarqueeZoomMode() != DvtGraphMarqueeZoomHandlerBase.ZOOM_OFF &&
          event.keyCode == DvtKeyboardEvent.SHIFT)
  {
    if(this._graphMarqueeHandler)
    {
      this._graphMarqueeHandler.processShiftZoom(true);
    }    
  }

}


/**
 * @override
 */
DvtMEH.prototype.OnMouseMove = function(event) {
  // Shape Attributes Support
  var dlo = this.GetLogicalObject(event.target);
  var relPos = this._context.getRelativePosition(event.pageX, event.pageY);
 
  // TODO Temporarily delegate to OnMouseEventTemp until this masterEventHandler is cleaned up
  this.OnMouseEventTemp(event);
 
  // Delegate to the superclass.  Eventually, all base component event handling is to be 
  // handled in DvtEventManager.
  DvtMEH.superclass.OnMouseMove.call(this, event);

  if (this.DataCursorHandler) {
     var cursorShown = this.DataCursorHandler.processMove(event.pageX, event.pageY, event.target);
  }
  
  if (this._graphMarqueeHandler) {
      var handled = this._graphMarqueeHandler.processDragMove(relPos);
      if (handled)
        event.preventDefault();
  }
 
  DvtShapeAttr.processEvent(dlo, event.target, event.type);

  if (this.GraphPanHandler) {
      var handled = this.GraphPanHandler.processDragMove(relPos);
      if (handled)
        event.preventDefault();
  }

}

/**
 * @override
 */
DvtMEH.prototype.OnMouseOver = function(event) {
  var pos = this._context.getRelativePosition(event.pageX, event.pageY);
  
  // Delegate to the superclass.  Eventually, all base component event handling is to be 
  // handled in DvtEventManager.
  DvtMEH.superclass.OnMouseOver.call(this, event);

  // TODO Temporarily delegate to OnMouseEventTemp until this masterEventHandler is cleaned up
  this.OnMouseEventTemp(event);
  
  var dlo = this.GetLogicalObject(event.target);

  // Shape Attributes Support
  DvtShapeAttr.processEvent(dlo, event.target, event.type);
  
  // Series Rollover Behavior
  if(this.SeriesRolloverHandler) 
    this.SeriesRolloverHandler.processMouseOver(dlo);
      
  // Reference Object Hover Behavior Support
  DvtRefObjHandler.processMouseOver(dlo);
  
  // Set Focused
  this._setFocused(true);
}

/**
 * @override
 */
DvtMEH.prototype.OnMouseOut = function(event) {
  var pos = this._context.getRelativePosition(event.pageX, event.pageY);
  
  // Delegate to the superclass.  Eventually, all base component event handling is to be 
  // handled in DvtEventManager.
  DvtMEH.superclass.OnMouseOut.call(this, event);
 
  // TODO Temporarily delegate to OnMouseEventTemp until this masterEventHandler is cleaned up
  this.OnMouseEventTemp(event);
  
  var dlo = this.GetLogicalObject(event.target);

  if (this.DataCursorHandler)
    this.DataCursorHandler.processOut(pos);

  // Shape Attributes Support
  DvtShapeAttr.processEvent(dlo, event.target, event.type);
  
  // Series Rollover Behavior
  if(this.SeriesRolloverHandler) 
    this.SeriesRolloverHandler.processMouseOut(dlo);
  
  // Reference Object Hover Behavior Support
  DvtRefObjHandler.processMouseOut(dlo);
  
  var relatedObj = event.relatedTarget;
  // If left chart
  if(!relatedObj || (relatedObj._Dlo && relatedObj._Dlo.getChart() != this._Chart))
    this._setFocused(false);
}

/**
 * @override
 */
DvtMEH.prototype.OnMouseDown = function(event) {

  DvtMEH.superclass.OnMouseDown.call(this, event);
  
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
  var relPos = this._context.getRelativePosition(event.pageX, event.pageY);
  
  if (this._graphMarqueeHandler) {
    var handled = this._graphMarqueeHandler.processDragStart(relPos, obj);
    if (handled)
      event.preventDefault();
  }
  
  // TODO Temporarily delegate to OnMouseEventTemp until this masterEventHandler is cleaned up
  this.OnMouseEventTemp(event);
  
  // Shape Attributes Support
  var dlo = this.GetLogicalObject(event.target);
  DvtShapeAttr.processEvent(dlo, event.target, event.type);
  
  if (this.GraphPanHandler) {
      var handled = this.GraphPanHandler.processDragStart(relPos, null);
      if (handled)
        event.preventDefault();
  }
}

/**
 * @override
 */
DvtMEH.prototype.OnMouseUp = function(event) {
  DvtMEH.superclass.OnMouseUp.call(this, event);
  
  var relPos = this._context.getRelativePosition(event.pageX, event.pageY);
  
  if (this._graphMarqueeHandler)
    this._graphMarqueeHandler.processDragEnd(relPos);
  
  // TODO Temporarily delegate to OnMouseEventTemp until this masterEventHandler is cleaned up
  this.OnMouseEventTemp(event);
  
  // Shape Attributes Support
  var dlo = this.GetLogicalObject(event.target);
  DvtShapeAttr.processEvent(dlo, event.target, event.type);

  if (this.GraphPanHandler)
      this.GraphPanHandler.processDragEnd(relPos);

}

DvtMEH.prototype.OnContextMenuInternal = function(event) {
    var obj = this.GetLogicalObject(event.target);
    this._processHideShowPopup(obj);
}

DvtMEH.prototype.HandleTouchHoverStartGraphInternal = function(event) {
    var targetObj = event.target;     
    var dlo = this.GetLogicalObject(targetObj);
    // Reference Object Hover Behavior Support
    DvtRefObjHandler.processMouseOver(dlo);
}

DvtMEH.prototype.HandleTouchHoverEndGraphInternal = function(event) {
    var targetObj = event.target;     
    var dlo = this.GetLogicalObject(targetObj);
    // Reference Object Hover Behavior Support
    DvtRefObjHandler.processMouseOut(dlo);
} 

DvtMEH.prototype.HandleTouchHoverOverGraphInternal = function(event) {
    var targetObj = event.target;     
    var dlo = this.GetLogicalObject(targetObj);
    // Reference Object Hover Behavior Support
    DvtRefObjHandler.processMouseOver(dlo);
}

DvtMEH.prototype.HandleTouchHoverOutGraphInternal = function(event) {
    var targetObj = event.target;     
    var dlo = this.GetLogicalObject(targetObj);
    // Reference Object Hover Behavior Support
    DvtRefObjHandler.processMouseOut(dlo);
}

DvtMEH.prototype.IsTooltipShowableInternal = function(obj, pos) {
    var chart = this._Chart; 
    if (this.DataCursorHandler) {
        var plotRect = chart.getFrameRect();
        if (plotRect.containsPoint(pos.x, pos.y)) {
            if (obj) {
                var objId = obj.getObjId();
                if (objId == DvtGraphObjType.OTHER || 
                    objId == DvtGraphObjType.PARETO_LINE_RISER ||
                    objId == DvtGraphObjType.PARETO_LINE_MARKER ||
                    objId == DvtGraphObjType.TIME_SELECTOR) {
                    return true;
                }
            }
            return false;
        } else {
           // Don't show tooltips for objects extending out of the plot bounds
           if (DvtGraphEventUtils.isNonLegendData(obj)) {
                return false;
           }
        }
    } else if (this.IsMagnifyLensEnabled()) {
       var plotRect = chart.getFrameRect();
       if (!plotRect.containsPoint(pos.x, pos.y)) {
           // Don't show tooltips for objects extending out of the plot bounds
           if (DvtGraphEventUtils.isNonLegendData(obj)) {
                return false;
           }
       }
    }
    return true;
}

DvtMEH.prototype.GetTooltipsAllowedRegion = function(obj) {
    if (!obj)
        return null;
    var chart = this._Chart;
    // Only force the restriction of being within the handle bounds when events are received outside a clipping region
    if (this._context.getDocumentUtils().isEventAvailableOutsideClip()) {
        // Only for zoom and scroll since content is guaranteed to be within plot bounds
        var isScrolling = chart.getHandleManager().hasScrollbars();
        if (isScrolling) {
           // Don't show tooltips for objects extending out of the plot bounds
           if (DvtGraphEventUtils.isNonLegendData(obj)) {
                return chart.getFrameRect();
           }
        }
    }
    return null;
}

DvtMEH.prototype.HandleImmediateTouchStartInternal = function(event) {
    if (this.GraphPanHandler) {
        this.TouchManager.processAssociatedTouchAttempt(event, DvtGraphPanHandler.PAN_TOUCH_KEY, this.PlotPanStartTouch, this);
    }
}

DvtMEH.prototype.HandleImmediateTouchMoveInternal = function(event) {
    if (this.GraphPanHandler) {
        this.TouchManager.processAssociatedTouchMove(event, DvtGraphPanHandler.PAN_TOUCH_KEY);
    }
}

DvtMEH.prototype.HandleImmediateTouchEndInternal = function(event) {
    if (this.GraphPanHandler) {
        this.TouchManager.processAssociatedTouchEnd(event, DvtGraphPanHandler.PAN_TOUCH_KEY);
    }
}

DvtMEH.prototype.PlotPanStartTouch = function(event, touch) {
    if (this.GraphPanHandler) {
        var frame = this._Chart.getFrameRect();
        var pos = this._context.getRelativePosition(touch.pageX, touch.pageY);
        // If touches are not in the graph frame, don't save them for panning
        if (frame && !frame.containsPoint(pos.x, pos.y)) {
            return;
        }
        
        var touchIds = this.TouchManager.getTouchIdsForObj(DvtGraphPanHandler.PAN_TOUCH_KEY);
        if (touchIds.length <= 1) {
            this.TouchManager.saveProcessedTouch(touch.identifier, DvtGraphPanHandler.PAN_TOUCH_KEY, null, DvtGraphPanHandler.PAN_TOUCH_KEY, DvtGraphPanHandler.PAN_TOUCH_KEY, this.PlotPanMoveTouch, this.PlotPanEndTouch, this);
            this.TouchManager.setTooltipEnabled(touch.identifier, false);
            var plotTouchIds = this.TouchManager.getTouchIdsForObj(DvtGraphPanHandler.PAN_TOUCH_KEY);
            if (plotTouchIds.length == 2) {
                var point = this.TouchManager.calcAveragePosition(plotTouchIds);
                var relPos = this._context.getRelativePosition(point.x, point.y);
                this.GraphPanHandler.processDragStart(relPos, null);
            }
        }
    }
}

DvtMEH.prototype.PlotPanMoveTouch = function(event, touch) {
    if (this.GraphPanHandler) {
        var touchIds = this.TouchManager.getTouchIdsForObj(DvtGraphPanHandler.PAN_TOUCH_KEY);
        if (touchIds.length == 2) {
            var delta = this.TouchManager.getMultiTouchDelta(touchIds);
            if (delta) {
              this.GraphPanHandler.processDragMoveDelta(delta);
            }
            event.preventDefault();
        }
    }
}

DvtMEH.prototype.PlotPanEndTouch = function(event, touch) {
    if (this.GraphPanHandler) {
        var plotTouchIds = this.TouchManager.getTouchIdsForObj(DvtGraphPanHandler.PAN_TOUCH_KEY);
        var point = this.TouchManager.calcAveragePosition(plotTouchIds);
        var relPos = this._context.getRelativePosition(point.x, point.y);
        this.GraphPanHandler.processDragEnd(relPos);
    }
}

DvtMEH.prototype.HandleTouchClickGraphInternal = function(evt) {
    var chart = this._Chart;
    var touch = evt.touch;
    var targetObj = evt.target;
    var touchEvent = evt.touchEvent;

    var dlo = this.GetLogicalObject(targetObj);
    
    this._processPieExplode(dlo);
    
    var zb = chart.getZoomButton();
    if (zb) {
        zb.resetButton();
    }
    var visibilityToggled = false;
    var focusHandled = false;
    if (this.SeriesFocusHandler)
        focusHandled = this.SeriesFocusHandler.isSeriesFocusHandled();
    if (focusHandled) {
      if (chart.isMagnifyLensEnabled()) {
         var magnifyLens =  this.GetMagnifyLens();
         magnifyLens.setDirty(true);
      }   
    }
    if (!focusHandled) {
        if (this.HideShowSeriesHandler) {
            var toggled = this._processShowHide(dlo);
            if (toggled) {
                touchEvent.preventDefault();
            }
        }
    }
    DvtShapeAttr.processEvent(dlo, targetObj, DvtMouseEvent.CLICK);
    return (focusHandled || visibilityToggled);
}

DvtMEH.prototype._processPieExplode = function(logicalObject) {
  if (this._Chart.isPieSliceExplodeEnabled())
  {
    if(logicalObject instanceof DvtPieSlice)
      DvtPieSliceExplodeHandler.processClick(logicalObject);
  }
}

DvtMEH.prototype._processShowHide = function(dlo) {
    var toggled = this.HideShowSeriesHandler.handleShowHide(dlo);
    if (toggled && this.IsMagnifyLensEnabled()) {
        var magnifyLens =  this.GetMagnifyLens();
        magnifyLens.setDirty(true);
    }    
    return toggled;
}

DvtMEH.prototype.GetMagnifyContent = function(pos) {
    return this._Chart; 
}    

// Update visual feedback on element when action popup shown
DvtMEH.prototype._actionTooltipHideListener = function(evt) {
    var targetObj = evt.target; 
    var dlo = this.GetLogicalObject(targetObj);
    var toggled = this._processShowHide(dlo);
}

DvtMEH.prototype._actionTooltipShowListener = function(evt) {
    var targetObj = evt.target; 
    var dlo = this.GetLogicalObject(targetObj);
    var toggled = this._processShowHide(dlo);

}

DvtMEH.prototype.IsMagnifyLensEnabled = function()
{
    var chart = this._Chart;
    return (chart.isMagnifyLensEnabled());
}

DvtMEH.prototype.GetMagnifyLensRegion = function(pos, logicalObj)
{
    var chart = this._Chart;
    return DvtGraphEventUtils.getActionablePlotRect(chart, pos.x, pos.y, logicalObj);
}

DvtMEH.prototype.GetClearSelectionText = function() {
    var chart = this._Chart;
    return chart.getTextResource(DvtGraphCore.CLEAR_SELECTION_KEY);
}

/** @private */
DvtMEH.prototype._processHideShowPopup = function(dlo) {
    if (!dlo)
        return false;
    var chart = this._Chart; 
    if (chart.isSeriesFocusEnabled()) {
        if (this.HideShowSeriesHandler) {
            if (DvtGraphEventUtils.isLegendMarker(dlo)) {
                var sidx   = dlo.getSeriesIndex();
                var bVis = chart.isSeriesVisible(sidx);
                if (! bVis)
                    this.addActionTooltipMenuItem(DvtEventManager.SHOW_SERIES_ACTION_TYPE, chart.getTextResource(DvtGraphCore.SHOW_KEY), this._actionTooltipHideListener, this);
                else {
                    this.addActionTooltipMenuItem(DvtEventManager.HIDE_SERIES_ACTION_TYPE, chart.getTextResource(DvtGraphCore.HIDE_KEY), this._actionTooltipShowListener, this);
                }
                return true;
            }
        }
    }
    return false;
}

/*---------------------------------------------------------------------*/
/*   removeEventManager()                                              */
/*---------------------------------------------------------------------*/
/**
 *    Removes a chart event handler from the list of registered handlers.
 *    @param {?}  eh  The event handler.
 */
DvtMEH.prototype.removeEventManager = function(eh)
{
  if (this._arEVHList) {
     var  len = this._arEVHList.length ;

     for (var i =0; i < len; i++)   {
        if (this.m_arEVHList[i] === ceh)   {
          this._arEVHList[i] = null ;    // null out. slot is reusable.
          break ;
        } 
     }
  }  
};

/**
 * Returns the event handler with the specified type.
 * @param {string} type
 */
DvtMEH.prototype.getEventHandler = function(type) {
  if(!this._arEVHList)
    return null;
    
  for(var i=0; i<this._arEVHList.length; i++) {
    if(type === this._arEVHList[i].getType())
      return this._arEVHList[i];
  }
  
  // No match found, return null
  return null;
}

DvtMEH.prototype._setFocused = function(b) {
  var zb = this._Chart.getZoomButton();
  if (zb) {
    if (b) {
      zb.show();
    } else {
      zb.hide();
    }
  }
}

/**
 * Sets the graph marquee handler to use with this event manager.
 * @param {DvtGraphMarqueeHandler} handler The marquee handler to use.
 */
DvtMEH.prototype.setGraphMarqueeHandler = function(handler) {
  this._graphMarqueeHandler = handler;
}

/**
 * Sets the tooltip style
 * @param {DvtFill} tooltipFill
 * @param {DvtFont} tooltipFont
 */
DvtMEH.prototype.setTooltipStyle = function(tooltipFill, tooltipFont) {
  // Regular tooltip/datatip
  var ttm = this._context.getTooltipManager();
  if (ttm && ttm.setFont)
    ttm.setFont(tooltipFont);
  
  // Data cursor
  var dc = this._Chart.getDataCursor();
  if (dc)
    dc.setFont(tooltipFont);
  
  // Custom tooltip
  if (this.CustomTooltipManager) {
      var actionTooltip = this.CustomTooltipManager.getActionTooltip();
      if (actionTooltip) {
        if (tooltipFill)
            actionTooltip.setTooltipFill(tooltipFill);
        if (tooltipFont) {
            actionTooltip.setFont(tooltipFont);
            actionTooltip.setMenuFontSize(tooltipFont.size);    
        }
      }
  }
}

/**
 * @override
 */
DvtMEH.prototype.hideTooltip = function() {
  // Don't hide the tooltip if data cursor is shown on a touch device
  if(!(this.DataCursorHandler && this.DataCursorHandler.isDataCursorShown() && DvtAgent.isTouchDevice()))
    DvtMEH.superclass.hideTooltip.call(this);

  // Hide the data cursor. This is necessary to hide the data cursor line when the user mouses over the tooltip div in 
  // IE9, which does not support pointer-events.
  if(this.DataCursorHandler && DvtAgent.isPlatformIE()) 
    return this.DataCursorHandler.processEnd();
}
/**
  *  Graph handle class used for initializing logical graph objects.
  *  @extends DvtHandle
  *  @class DvtGraphHandle 
  *  @constructor  
  */
var   DvtGraphHandle = function(context, sId, boundRect, clipRect, clipId) {
    this.Init(context, sId, boundRect, clipRect, clipId);
}

DvtObj.createSubclass(DvtGraphHandle, DvtHandle, "DvtGraphHandle");

// Constants
DvtGraphHandle.ID_NONE = 0; // type unspecified
DvtGraphHandle.ID_X1L = 1;  // X1 labels
DvtGraphHandle.ID_PA = 2;   // plot area
DvtGraphHandle.ID_Y1PA = 3; // Y1 plot area
DvtGraphHandle.ID_Y2PA = 4; // Y2 plot area
DvtGraphHandle.ID_OL = 5;   // ordinal labels
DvtGraphHandle.ID_Y1L = 6;  // Y1 labels
DvtGraphHandle.ID_Y2L = 7;  // Y2 labels

/**
 * @protected
 */
DvtGraphHandle.prototype.Init = function(context, sId, boundRect, clipRect, clipId) {
    DvtGraphHandle.superclass.Init.call(this, context, sId, boundRect, clipRect, clipId);
    this.Id = DvtGraphHandle.createHandleId(sId);
    this._objList = [];
}

DvtGraphHandle.prototype.init = function () {
  var objList = this.getChildren();

  for (var i = 0;i < objList.length;i++) {
    var obj = objList[i];

    obj._Dlo.setChart(this._Dlo.getChart());

    this.addChild(obj);
    obj._Dlo.init();
  }
}

/**
 * Returns the list of children of this handle.
 * @return {array} The list of children.
 */
DvtGraphHandle.prototype.getChildren = function () {
  return this._objList;
}

/**
 * Adds an child object to this handle.
 * @param {object} obj The child object to add.
 */
DvtGraphHandle.prototype.addObject = function (obj) {
  if (obj != null)
    this._objList.push(obj);
}

/**
 * Creates a handle id.
 * @param {string} sId The string id of a handle.
 * @return {number} representing a handle id.
 */
DvtGraphHandle.createHandleId = function (sId) {
  switch (sId) {
    case "X1L":
      return DvtGraphHandle.ID_X1L;
    case "PA":
      return DvtGraphHandle.ID_PA;
    case "Y1PA":
      return DvtGraphHandle.ID_Y1PA;
    case "Y2PA":
      return DvtGraphHandle.ID_Y2PA;
    case "OL":
      return DvtGraphHandle.ID_OL;
    case "Y1L":
      return DvtGraphHandle.ID_Y1L;
    case "Y2L":
      return DvtGraphHandle.ID_Y2L;
    default :
      return DvtGraphHandle.ID_NONE;
  }
}
// Copyright (c) 2008, 2012, Oracle and/or its affiliates. 
// All rights reserved. 
/*---------------------------------------------------------------------*/
/*    DvtGraphAxis      An axis of a chart and its associated components    */
/*---------------------------------------------------------------------*/
/**
  *  Represents an axis of a chart and its associated components.
  *  @class DvtGraphAxis is a platform independent class representing a chart
  *                 axis line and its associated components.
  *  @constructor  
  *  @param {DvtGraphCore} chart     the owning chart.
  *  @param {DvtLine}  axis      the axis line object.
  *  @param {Array}    gridLines an array of gridlines.
  *  @param {Array}    labels    an array of text labels.
  */
var  DvtGraphAxis = function(chart, axis, aGridLines, aLabels)
{
   this.Init(chart, axis, aGridLines, aLabels) ;
};

DvtObj.createSubclass(DvtGraphAxis, DvtObj, "DvtGraphAxis");

//  Axis types

/**  The X-axis.  @final @type number  */
DvtGraphAxis.X  = 1 ;
/**  The Y1-axis. @final @type number  */
DvtGraphAxis.Y1 = 2 ;
/**  The Y2-axis. @final @type number  */
DvtGraphAxis.Y2 = 3 ;
/**  Unknown axis type. @final @type number  */
DvtGraphAxis.UNKNOWN = 0 ;


/*---------------------------------------------------------------------*/
/*  Init()                                                             */
/*---------------------------------------------------------------------*/
/**
  *  Object initializer.
  *  @protected
  */
DvtGraphAxis.prototype.Init = function(chart, axis, aGridLines, aLabels)
{
   this._axis    = axis ;
   this._aGrids  = aGridLines ;
   this._aLabels = aLabels ;
   this._type    = DvtGraphAxis.UNKNOWN ;
   this._Chart   = chart ;

   if (axis) {
     var id = axis._Dlo.getObjId() ;

     if (id === DvtGraphObjType.X1_AXIS_LINE ||
         id === DvtGraphObjType.O1_AXIS_LINE) {
       this._type = DvtGraphAxis.X ;
     }
     else if (id === DvtGraphObjType.Y1_AXIS_LINE) {
       this._type = DvtGraphAxis.Y1 ;
     }
     else if (id === DvtGraphObjType.Y2_AXIS_LINE) {
       this._type = DvtGraphAxis.Y2 ;
     }
   }
};

/**
  *   Returns the axis line object.
  *   @returns {DvtLine}  the axis line object.
  */
DvtGraphAxis.prototype.getAxisLine = function()
{
   return this._axis ;
};

/**
  *  Returns an array of {@link DvtLine} objects representing
  *                    the associated grid lines.
  *  @returns {Array}  an array of {@link DvtLine} line objects representing
  *                    the associated grid lines.
  */
DvtGraphAxis.prototype.getGridLines = function()
{
   return this._aGrids ;
};

/**
  *  Returns an array of {@link DvtText} objects representing
  *  the associated text labels.
  *  @returns {Array} an array of {@link DvtText} line objects representing
  *  the associated text labels.
  */
DvtGraphAxis.prototype.getLabels = function()
{
   return this._aLabels ;
};


/**
  *  Returns the type of the axis (such as {@link DvtGraphAxis#X}.
  *  @returns {number} the type of the axis (such as {@link DvtGraphAxis#X}.
  *  the associated text labels.
  */
DvtGraphAxis.prototype.getType = function()
{
   return this._type ;
};
// Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*   DvtGraphCore                                                      */
/*---------------------------------------------------------------------*/
/**
  *  Internal class that represents a chart.  Do not create directly - 
  *  use {@link DvtGraph}.
  *  @class   DvtGraphCore
  *  @extends DvtContainer
  *  @constructor
  */
var  DvtGraphCore = function(context, id, callback, callbackObj)
{
   this._Init(context, id, callback, callbackObj) ;
};

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

/**
  *  No series rollover effect
  *  @final
  *  @type number
  */
DvtGraphCore.RB_NONE    = 0 ;
/**
  *  Series rollover effect is to dim the objects of a different series
  *  @final
  *  @type number
  */
DvtGraphCore.RB_DIM     = 1;
/**
  * Series rollover effect is to highlight the objects of the same series
  * @final
  * @type number 
  */
DvtGraphCore.RB_HILITE  = 2;


/**
  *  No Selection support.
  *  @final
  *  @type number
  */
DvtGraphCore.SEL_NONE   = 0 ; 
/**
  *  Single selection mode
  *  @final
  *  @type number
  */
DvtGraphCore.SEL_SINGLE = 1 ;
/**
  *  Multiple selection mode
  *  @final
  *  @type number
  */
DvtGraphCore.SEL_MULTI  = 2 ;


/**
  *  No zoom direction.
  *  @final
  *  @type number
  */
DvtGraphCore.DATA_ZOOM_NONE    = 0 ;
/**
  *  Horizontal zoom direction.
  *  @final
  *  @type number
  */
DvtGraphCore.DATA_ZOOM_HORIZ   = 1 ;
/**
  *  Vertical zoom direction.
  *  @final
  *  @type number
  */
DvtGraphCore.DATA_ZOOM_VERT    = 2 ;
/**
  *  Horizontal and vertical zoom direction.
  *  @final
  *  @type number
  */
DvtGraphCore.DATA_ZOOM_BOTH    = 3 ;


DvtGraphCore.ZS_LIVE_SCROLL    = "ZS_LIVE_SCROLL" ;
DvtGraphCore.ZS_DELAYED_SCROLL = "ZS_DELAYED_SCROLL" ;



/*---------------------------------------------------------------------*/
/*  addClipPath()                                                      */
/*---------------------------------------------------------------------*/
/**
  *  Add a {@link DvtGraphClipPath} to the chart's list of clipping paths.
  *  @param {DvtGraphClipPath} clip The clip path to be added.
  */
DvtGraphCore.prototype.addClipPath = function(clip)
{
   if (! this._aClipRects) {
     this._aClipRects = [] ;
   }
   this._aClipRects.push(clip) ;
} ;


/*---------------------------------------------------------------------*/
/*  _addEventHandler                                                   */
/*---------------------------------------------------------------------*/
/**
  *   @private
  */
DvtGraphCore.prototype._addEventHandler = function(eh)
{
   this._initEventHandler();

   this._MEH.addEventHandler(eh) ;
};

/*---------------------------------------------------------------------*/
/*  _initEventHandler                                                  */
/*---------------------------------------------------------------------*/
/**
  *   @private
  */
DvtGraphCore.prototype._initEventHandler = function()
{
   if (! this._MEH) {
    
    var handler = new DvtMEH(this) ;
    this._MEH = handler;
    
    handler.setContextMenuHandler(this.__getContextMenuHandler());
    
    // Add the actual event listeners
    handler.addListeners(this);

    if (DvtAgent.isTouchDevice()) {
     handler.setTouchRegionBounds(new DvtRectangle(0,0,parseInt(this.getWidth()),parseInt(this.getHeight())));
    }
    
    if (this.isDataCursorEnabled())
     handler.setDataCursorHandler(new DvtGraphDCEH(this));
    
    if (this.isHSSeriesSupported())
     handler.setHideShowSeriesHandler(new DvtHSSEH(this));

    if (this.isSeriesFocusEnabled())
        handler.setSeriesFocusHandler(new DvtGraphSeriesFocusHandler(this));


    if (this.getSROEffect() !== DvtGraphCore.RB_NONE)
     handler.setSeriesRolloverHandler(new DvtGraphSeriesRolloverHandler(this));

   }
};


/*--------------------------------------------------------------------*/
/*   isStockObjId()    Returns true if supplied object Id represents  */
/*                     a stock graph type riser object.               */
/*--------------------------------------------------------------------*/
/**
 * @param {String} id
 * @return {Boolean}
 */
DvtGraphCore.isStockObjId = function(id)
{
    return  ((id == DvtGraphObjType.STOCK_CLOSE_TICK)    ||
             (id == DvtGraphObjType.STOCK_FALLING_RISER) ||
             (id == DvtGraphObjType.STOCK_HIGH_LINE)     ||
             (id == DvtGraphObjType.STOCK_LOW_LINE)      ||
             (id == DvtGraphObjType.STOCK_OPEN_TICK)     ||
             (id == DvtGraphObjType.STOCK_RISER)         ||
             (id == DvtGraphObjType.STOCK_RISING_RISER)  ||
             (id == DvtGraphObjType.STOCK_VOLUME_RISER)) ;
};

/*---------------------------------------------------------------------*/
/*   addFont()                                                         */
/*---------------------------------------------------------------------*/
/**
 *   Adds a {@link DvtFont} object to the Chart's list of fonts.
 *   @param {DvtFont} font  The font to be added.
 */
DvtGraphCore.prototype.addFont = function(font)
{
   var i = 0 ;

   if (this._aFontTable === null) {
     this._aFontTable = [] ;
   }

   this._aFontTable[font._Id] = font ;
} ;



/*---------------------------------------------------------------------*/
/*   activateLink()                                                    */
/*---------------------------------------------------------------------*/
/**  
  */
DvtGraphCore.prototype.activateLink = function(location, target)
{
  window.open(location, target);
};


/*---------------------------------------------------------------------*/
/*   addUpdateIndicators()                                             */
/*---------------------------------------------------------------------*/
/**  
  *   Adds new update indicator objects in the supplied array to this
  *   charts internal list of indicator objects.
  *   @param {Array} arIndicators  the array of update indicator objects to 
  *                  be added to this charts internal array of such objects.
  */
DvtGraphCore.prototype.addUpdateIndicators = function(indicator)
{
   if (indicator) {
     if (! this._indicators) {
       this._indicators = [] ;
     }

     if (DvtArrayUtils.isArray(indicator)) {
       var len = indicator.length ;
       for (var i = 0; i < len; i++) {
         this._indicators.push(indicator[i]) ;
       }
     }
     else {
       this._indicators.push(indicator) ;
     }
   }
};


/*---------------------------------------------------------------------*/
/*   callExternal()                                                    */
/*---------------------------------------------------------------------*/
/**  
  *  Executes the supplied Javascript.
  */
DvtGraphCore.prototype.callJs = function(js)
{
   this._external.callJs(js) ;          // defer to implementation.
};



/*---------------------------------------------------------------------*/
/*   getClipPath()                                                     */
/*---------------------------------------------------------------------*/
/**  
  *  Returns the ClipPath object for the specified id.
  *  @type DvtGraphClipPath
  */
DvtGraphCore.prototype.getClipPath = function(id)
{
   var  retClip;

   if (this._aClipRects !== null)  {
      var  len = this._aClipRects.length ;

      for (var i = 0; i < len; i++) {
         var clip = this._aClipRects[i] ;
         if (clip.getId().indexOf(id)!=-1) {
           retClip = clip ;
           break ;
         }
      }
   }

   return retClip ;
};


/*--------------------------------------------------------------------*/
/* .getCtx()                                                      */
/*--------------------------------------------------------------------*/
/**
  *   @returns {DvtContext}  The platform implementation context.
  */
DvtGraphCore.prototype.getCtx = function()
{
    return this._ctx ;
};


/*--------------------------------------------------------------------*/
/*   getObjList()                                                     */
/*--------------------------------------------------------------------*/

DvtGraphCore.prototype.getObjList = function()
{
   return this._objList ;       // the initial object list after parse.
                                // and before first render.
};


/*--------------------------------------------------------------------*/
/*  getPlotRect()    Returns the first available of the following     */
/*                   rectangles:                                      */
/*                   1) the plot area                                 */
/*                   2) the frame                                     */
/*                   3) the chart background                          */
/*                   4) the engine's rectangle                        */
/*--------------------------------------------------------------------*/
/**
  *   @type DvtRectangle
  */
DvtGraphCore.prototype.getPlotRect = function()
{
   var x;
   var y;
   var w;
   var h;

   if (!this._Plot) {
      if (this._rPlotW >= 0) {
        x = this._rPlotX ;
        y = this._rPlotY ;
        w = this._rPlotW ;
        h = this._rPlotH ;
      }
      else if (this._rFrameW >= 0) {
        x = this._rFrameX ;
        y = this._rFrameY ;
        w = this._rFrameW ;
        h = this._rFrameH ;
      }
      else  if (this._rCBW >= 0) {
        x = this._rCBX ;
        y = this._rCBY ;
        w = this._rCBW ;
        h = this._rCBH ;
      }
      else  {
        x = 0 ;
        y = 0 ;
        w = this._width ;    // from <graph/gaugeset> tag
        h = this._height ;
      }

      this._Plot = new DvtRectangle(x, y, w, h) ;
   }

   return this._Plot ;
};

DvtGraphCore.prototype.getId = function() {
    if (this._id) 
      return this._id;
    else 
      return DvtGraphCore.superclass.getId.call(this);
};


/**
  *   @private
  */
DvtGraphCore.prototype._Init = function(context, id, callback, callbackObj)
{
   if (! this.getImpl()) {
     this.setImpl(context.getImplFactory().newContainer()) ;
   }
   DvtGraphCore.superclass.Init.call(this, context) ;
   
   this._id             = id;
   this._ctx            = context ;
   this._callback       = callback;
   this._callbackObj    = callbackObj;
   this._type           = '' ;       // graph/gauge type see DvtGraphType
   this._width          = 0 ;
   this._height         = 0 ;
   this._3D             = false ;
   this._objList        = null ;
   this._bObjInit       = false ;    // true = all det obj's init'ed

   this._MEH            = null ;     // master event handler for this chart.
   this._Automation     = new DvtGraphAutomation(this) ;     
   this._aFontTable     = null ;

   //  Tooltip effects

   this._bTooltips      = true ;     // tooltips enabled/disabled
   this._MarkerTooltipHelper = null ;// LineMarker support

   this._SRO            = DvtGraphCore.RB_NONE ;  // series rollover effect

   this._arHSSState     = null ;              // hide show series state
   this._HSSShowCount   = 0 ;                 // count of visible series
   this._arHSLegendMiscId = [];
   this._HiddenSeries = [];
   this._HiddenUnderlayStates = [];

   this._selMode        = DvtGraphCore.SEL_NONE;
   this._selListener    = false ;
   this._selectionHandler = null;
   this._selectedIds  = null;

   this._dragSource     = new DvtGraphDragSource(this, context);
   this._dropTarget     = new DvtGraphDropTarget(this);
   
   this._Animation      = null ;

   this._aClipRects     = null ;

   this._seriesEffect   = DvtFill.NONE ;
   this._bVisEffect     = false ;

   this._XAxis          = null ;        // DvtGraphAxis obj.
   this._Y1Axis         = null ;        // DvtGraphAxis obj.
   this._Y2Axis         = null ;        // DvtGraphAxis obj.
   this._DataSummary    = null ;        // DvtDataSummary obj.
   this._Data           = null ;        // DvtGraphData obj.

   this._aDLOs          = new Array();

   this._arSeriesMarkerSize = null ;  // array of linemarker size indexed by series index
   this._aRisers         = null ;     // array of riser objects arrays in series index order
   this._firstRiserIdx   = -1 ;
   this._aHMarkers       = null ; // array of hidden marker arrays in series index order
   this._aLegends        = null ; // array of legend object arrays in series index order
   this._aMiscLegends    = null ; // miscellaneous legends and text

   //  Dimensions of the plot area -  passed from parser

   this._Plot    = null ;
   this._rPlotX  = -1;
   this._rPlotY  = -1;
   this._rPlotW  = -1;
   this._rPlotH  = -1;

   //  Dimensions of the O="CB" (chart bkgnd) rect -  passed from parser

   this._rCBX  = -1;
   this._rCBY  = -1;
   this._rCBW  = -1;
   this._rCBH  = -1;

   //  Dimensions of the O="F" (frame) rect (or the clipping rect on a
   //  plot handle)-  passed from parser.

   this._rFrameX  = -1;
   this._rFrameY  = -1;
   this._rFrameW  = -1;
   this._rFrameH  = -1;

   // Support for changing z-order for selection
   this._numFrontObjs = 0;
   this._numSelectedObjsInFront = 0;

   // Pie Slice Explode All

   this._bPieExplodeAll = false;
   //this._pieExplodeAllResource:String = "Explode All";
   //this._pieUniteAllResource:String   = "Unite All";
   //this._explodeAllItem:ContextMenuItem;
   //this._uniteAllItem:ContextMenuItem;

   this._zoomInRectEventJs   = null ;
   this._zoomOutRectEventJs  = null ;
   this._zoomInClickEventJs  = null ;
   this._zoomOutClickEventJs = null ;
   this._zoomScrollBehavior  = null ;
   this._zoomDir             = null ;
   this._zoomButton          = null ;
   
   this._external = context.getJsExt()      //  gateway to ADF Js

   this._legendHandle = null;
   this._scrollableLegend = null;
   this._pieChart = null;

   this._dataCursorDisplay = "AUTO";
   this._magnifyLensDisplay = "OFF";

   this._bBiDi = false ;                    // true if BiDi

   this._handleManager = null;              // DvtHandleManager instance

   this._MarqueeZoomMode = DvtGraphMarqueeZoomHandlerBase.ZOOM_OFF;
   this._PanMode = DvtGraphPanHandler.PAN_OFF;

   // Filters unsupported, so initialize alternative selection effects
   if (!context.getDocumentUtils().isFilterSupported()) {
        DvtSelectionEffectUtils.DEFAULT_SEL_TYPE        = DvtSelectionEffectUtils.SEL_TYPE_STROKE_NO_FILTERS;
   }

   // DnD variables

   // drop site feedback rect
   this._dropSiteFeedback = null;
   // drag feedback colors
   this._dragFeedbackBorderColor = null;
   this._dragFeedbackFillColor = null;
   this._dragFeedbackFillAlpha = 0.5;
   
   this._attMode = false;

} ;


/*---------------------------------------------------------------------*/
/*   destroy()                                                         */
/*---------------------------------------------------------------------*/
/*
 *    Releases all resources owned to prevent memory leaks.
 */
DvtGraphCore.prototype.destroy = function()
{
  DvtGraphCore.superclass.destroy.call(this);

  if (this._MEH) 
  {
    this._MEH.removeListeners(this);
    this._MEH.destroy();
  }
  
}


/**
 * Dispatches the event to the callback function.
 * @param {object} event The event to be dispatched.
 */
DvtGraphCore.prototype.__dispatchEvent = function(event) {
  DvtEventDispatcher.dispatchEvent(this._callback, this._callbackObj, this, event);
};

/**
 * Registers the specified DLO with this chart.
 * @param {DvtDetObj} dlo
 */
DvtGraphCore.prototype.addDLO = function(dlo) {
  if(dlo) 
    this._aDLOs.push(dlo);
}

/**
 * Returns an array containing all DLOs registered with this chart.
 */
DvtGraphCore.prototype.getDLOs = function() {
  return this._aDLOs;
}

/**
 * Specifies the pie chart subcomponent of this graph.
 * @param {DvtPieChart} pie
 */
DvtGraphCore.prototype.__setPieChart = function(pie) {
  this._pieChart = pie;
}

/**
 * Returns the pie chart subcomponent of this graph, if it exists.
 * @return {DvtPieChart}
 */
DvtGraphCore.prototype.__getPieChart = function() {
  return this._pieChart;
}

/*---------------------------------------------------------------------*/
/*   enableTooltips()                                                  */
/*---------------------------------------------------------------------*/
/**
 *  Enables or disables tooltips display.
 *  @param {Boolean} bEnable 
 */
DvtGraphCore.prototype.enableTooltips = function(bEnable)
{
   this._bTooltips  = bEnable ;    // tooltips enabled/disabled
   this._MEH.setTooltipsEnabled(bEnable);  // communicate it to the MEH
};

/*---------------------------------------------------------------------*/
/*  get/set3D()                                                        */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart is 3D
  *  @type Boolean 
  */
DvtGraphCore.prototype.is3D = function()
{
   return this._3D ;
};
/**
  *  Sets whether the chart is 3D
  *  @param {Boolean} b3D  true if the chart is 3D. 
  */
DvtGraphCore.prototype.set3D = function(b3D)
{
   this._3D = b3D ;
};


/*---------------------------------------------------------------------*/
/*    getAnimation()                                                   */
/*---------------------------------------------------------------------*/
/**
  *    Returns the animation object {@link DvtGraphAnimation}.
  *    @type  DvtGraphAnimation
  */
DvtGraphCore.prototype.getAnimation = function()
{
    return this._Animation ;
};


/*---------------------------------------------------------------------*/
/*   getDataSummary()                                                  */
/*---------------------------------------------------------------------*/
/**
  *   Gets the parsed data summary for this chart.
  *   @returns  {DvtDataSummary}  The data summary object.
  */
DvtGraphCore.prototype.getDataSummary = function()
{
   return this._DataSummary ;
} ;


/*---------------------------------------------------------------------*/
/*    getLayers()                                                      */
/*---------------------------------------------------------------------*/
/**
  *  Returns the layers array.
  *  @returns {Array} the layers array.  The array is indexed by miscId.
  *  Each entry is an array of series for that layer.
  */ 
DvtGraphCore.prototype.getLayers = function()
{
    return this._Data.getLayers() ;
};


/*---------------------------------------------------------------------*/
/*   get/setAutomation()                                               */
/*---------------------------------------------------------------------*/
/**
  *    Returns {DvtGraphAutomation} the automation object DvtGraphAutomation.
  */
DvtGraphCore.prototype.getAutomation = function()
{
    return this._Automation ;
};

/**
  *    Sets the animation object {@link DvtGraphAutomation}.
  *    @param {DvtGraphAutomation} oAuto  The animation object.
  */
DvtGraphCore.prototype.setAutomation = function(oAuto)
{
    this._Automation = oAuto;
};




/*---------------------------------------------------------------------*/
/*   getFont()     Return a font spec for the supplied font ID.        */
/*---------------------------------------------------------------------*/
/**
  * Returns a {@link DvtFont} object for the specified font Id. 
  * @type DvtFont
  */
DvtGraphCore.prototype.getFont = function(fid)
{
   var font = null ;

   var ar = this._aFontTable ;
   if (ar && (fid < ar.length)) {
     font = ar[fid] ;
     font = font ? font : null ;
   }

   return font ;
} ;



/*---------------------------------------------------------------------*/
/*  get/setDatatipFill()                                               */
/*---------------------------------------------------------------------*/

DvtGraphCore.prototype.getDatatipFill = function()
{
  return this._datatipFill;
};

DvtGraphCore.prototype.setDatatipFill = function(fill)
{
  this._datatipFill = fill;
};


/*---------------------------------------------------------------------*/
/*   get/setFrameRect()                                                */
/*---------------------------------------------------------------------*/
/**
  *  Get the Frame area dimensions.
  *  @param {DvtRectangle} rect  Optional DvtRectangle in which the frame
  *  dimenions will be returned.
  *  @type  DvtRectangle
  */
DvtGraphCore.prototype.getFrameRect = function(rect)
{
    if (! rect) {
      rect = new DvtRectangle() ;
    }
    rect.x = this._rFrameX ;
    rect.y = this._rFrameY ;
    rect.w = this._rFrameW ;
    rect.h = this._rFrameH ;

    return rect;
};
/**
  *     Notes the Frame area dimensions.
  */
DvtGraphCore.prototype.setFrameRect = function(x, y, w, h)
{
    this._rFrameX  = x ;
    this._rFrameY  = y ;
    this._rFrameW  = w ;
    this._rFrameH  = h ;
};


/*---------------------------------------------------------------------*/
/*   get/setGaugeData()                                                */
/*---------------------------------------------------------------------*/
/**
  *   Gets the DvtGaugeData (or subclass) object.
  *   @type DvtGaugeData
  */
DvtGraphCore.prototype.getGaugeData = function()
{
    return this._gaugeData ;
};

/**
  *   Sets the DvtGaugeData (or subclass) object.
  *   @param {DvtGaugeData}  gd  The gauge data.
  */
DvtGraphCore.prototype.setGaugeData = function(gd)
{
    this._gaugeData = gd ;
};

/*---------------------------------------------------------------------*/
/*  getHeight()                                                        */
/*---------------------------------------------------------------------*/
/**
  *   Returns the height of the chart.
  *   @type number
  */
DvtGraphCore.prototype.getHeight = function()
{
   return this._height ;
} ;

/*---------------------------------------------------------------------*/
/*   HSSeriesState related                                             */
/*---------------------------------------------------------------------*/
/**
  *   Returns the count of the visible series.
  *   @returns {number}  The number of series visible.
  */
DvtGraphCore.prototype.getSeriesVisCount = function()
{
   return this._HSSShowCount ;
};

/**
  *   Returns true if Hide and Show Series is supported by this chart.
  *   @returns {boolean} true if Hide and Show Series is supported by this chart.
  */
DvtGraphCore.prototype.isHSSeriesSupported = function()
{
   return (this._arHSSState !== null) ;
};


/**
  *   Returns the visible state of the series for a given series index.
  *   @param  {number}   the series index.
  *   @returns {boolean} true if the series is visible. False is returned
  *                      if the series is not visible, or the series index is invalid.
  */
DvtGraphCore.prototype.isSeriesVisible = function(sidx)
{
   var bRet = false ;
   if (sidx  <  this._arHSSState.length) {
     bRet =  this._arHSSState[sidx] ;
   }

   return bRet ;
};


/**
  *   Internal method to return the "hide and show series" state in the
  *   format supplied in the xml and as used in hide-show-series events.
  *   @returns {String}  the hide and show series state, where each
  *   character in the string is "t" if the series is hidden, and "f"
  *   if it is visible.
  */
DvtGraphCore.prototype.getHSSState = function()
{
   var ar   = this._arHSSState ;
   var sRet = '' ;

   if (ar) {
     for (var i = 0; i < ar.length; i++) {
        sRet += (ar[i]? 'f' : 't') ;
     }
   }

  return sRet ;
};

/**
  *   Internal method to return the "hide and show layers" state in the
  *   format supplied in the xml and as used in hide-show-series events.
  *   @returns {String}  the hide and show layers state
  */
DvtGraphCore.prototype.getHSLayersState = function()
{
   var sRet = '' ;
   //Instead of miscId which can change if the dataset changes, save the underlay color, shape, or split series instead
  if (this._arHSLegendMiscId.length > 0) {
    var layerList = this.getLayers();
    for (var i = 0; i < this._arHSLegendMiscId.length; i++) {
      if (i != 0)
        sRet += ';';
      sRet += layerList[this._arHSLegendMiscId[i]].join(',');
    }
  }

  return sRet ;
};

/**
  *   Internal method to set the "hide and show series" state (called
  *   only by the xml parser).
  *   @param {String} arState   The hide and show series state.
  *   @param {number} nShowing  The number of series visible.
  */
DvtGraphCore.prototype.setHSSState = function(arState, nShowing)
{
   this._arHSSState   = arState ;
   this._HSSShowCount = nShowing ;
};

/**
  *   Internal method to set the "hide and show series" state (called
  *   only by the xml parser).
  *   @param {String} arState   The array of legend marker miscIds to hide.
  */
DvtGraphCore.prototype.setHSLegendMiscId = function(arState)
{
  this._arHSLegendMiscId = arState;
};

/**
  *   Sets the visibility for the unerlays of markers with the given underlay state.
  *   @param {boolean} bHide True if underlay should be hidden
  *   @param {number}  state  The underlay shape or color
  */
DvtGraphCore.prototype.updateHSSeriesUnderlayState = function(bHide, state, bSave) {
  if (!bHide) 
    this._removeHiddenState(state, false);

  var chartObjs = this.getDLOs();
  for (var i = 0;i < chartObjs.length;i++) {
    var obj = chartObjs[i];
    if (obj.getUnderlayType() === state || obj.getUnderlayFC() === state) {
      var displayable = obj.getDetObj();
      if (this._HSNB == "single" && displayable.getUnderlay){ // new hide show behavior only hide the clicked underlay bug 13852246 
        var underlay = displayable.getUnderlay();
        displayable.setUnderlayVisible(false);
        if (bHide){
            if (obj.getUnderlayType() === state )
                displayable.setUnderlay(DvtMarker.DEFAULT_UNDERLAY_TYPE, underlay.getFill().getColor(), underlay.getFill().getAlpha());
            if (obj.getUnderlayFC() === state )
                displayable.setUnderlay(underlay.getType(), DvtMarker.DEFAULT_UNDERLAY_COLOR);
        }
        else if (!bHide){
            if (obj.getUnderlayType() === state )
                displayable.setUnderlay(obj.getUnderlayType(), underlay.getFill().getColor(), underlay.getFill().getAlpha());
            if (obj.getUnderlayFC() === state )
                displayable.setUnderlay(underlay.getType(), obj.getUnderlayFC());
        }
        if (this._isHiddenSeries(displayable._Dlo)) //if the series is hidden.. we want to be able to change the underlay if needed, but still keep it hidden.
            displayable.setUnderlayVisible(false);
      }
      else if ((this._HSNB == "all" || this._HSNB=="true") && displayable.setUnderlayVisible) { // new hide show behavior does not hide markers when underlays are hidden
        var keepHidden = this._isHiddenSeries(displayable._Dlo) || this._isHiddenUnderlay(displayable._Dlo);
        if (bHide && !keepHidden) {
          displayable.setUnderlayVisible(false);
        }
        else if (!bHide && !keepHidden) {
          displayable.setUnderlayVisible(true);
        }
      } 
      else if (this._HSNB=="off" || this._HSNB=="false") { // old hide show behavior hides markers when underlays are hidden
        this._setMarkerUnderlayVisibility(displayable, bHide);
      }
    }
  }
  
  if (bHide)
    this._addHiddenState(state, false);
  
  // Update the server's hide show series and underlay state.
  if (bSave) {
    var newState = this.getHSSState();
    this._external.sendPPRProperty(this.getId(), DvtJsExt.PPR_ATTRIB_HSS, newState);
    
    var newLayersState = this.getHSLayersState();
    this._external.sendPPRProperty(this.getId(), DvtJsExt.PPR_ATTRIB_HS_LAYERS, newLayersState);
  }
}
/**
  *   Sets the series visibility for the supplied series index, updates
  *   the chart's internal HSS state, and sends a PPR property change call.
  *   @param {boolean} bHide True if series should be hidden
  *   @param {number}  sidx   The series index
  */
DvtGraphCore.prototype.updateHSSeriesState = function(bHide, sidx, bSave) {
  if (!bHide)
    this._removeHiddenState(sidx, true);

  if (this._HSR){
      return; 
  }
  var aDLOs = this.getDLOs();
  var len = aDLOs.length;
  for (var i = 0;i < len;i++) {
    var dlo = aDLOs[i];
    if (!DvtGraphObjType.isLegendItem(dlo.getObjId())) {
      if (sidx == dlo.getSeriesIndex()) {
        var displayable = dlo.getDetObj();
          var keepHidden = this._isHiddenSeries(displayable._Dlo);
        if (this._HSNB== "all"  || this._HSNB=="true") {  // new hide show behavior does not hide markers when underlays are hidden
          if (bHide && !keepHidden) {
            displayable.setVisible(false);
            if (displayable.setUnderlayVisible)
              displayable.setUnderlayVisible(false);
          }
          else if (!bHide && !keepHidden) {
            displayable.setVisible(true);
            if (displayable.setUnderlayVisible && !this._isHiddenUnderlay(displayable._Dlo))
              displayable.setUnderlayVisible(true);
          }
        } else if(this._HSNB== "single"){
          if (bHide && !keepHidden) {
            displayable.setVisible(false);
            if (displayable.setUnderlayVisible)
              displayable.setUnderlayVisible(false);
          }
          else if (!bHide && !keepHidden) {
            displayable.setVisible(true);
            if (displayable.setUnderlayVisible)
              displayable.setUnderlayVisible(true);
          }
        } else { // old hide show behavior hides markers when underlays are hidden
          this._setMarkerUnderlayVisibility(displayable, bHide);
        }
      }
    }
  }
  if (bHide)
    this._addHiddenState(sidx, true);

  // Update visible series counter
  var bVis = !bHide;
  var b = this._arHSSState[sidx];
  if (b !== bVis) {
    this._arHSSState[sidx] = bVis;
    if (bVis) {
      this._HSSShowCount++
    }
    else  {
      this._HSSShowCount--
    }
  }

  // Notify the chart about the event, allowing it to pass it on to subcomponents like the pie.
  var eventType = bVis ? DvtCategoryHideShowEvent.TYPE_SHOW : DvtCategoryHideShowEvent.TYPE_HIDE;
  var category = this.getDataObj().getSeriesFromIndex(sidx);
  var event = new DvtCategoryHideShowEvent(eventType, category);
  this.processEvent(event, this);

  // Update the server's hide show series and underlay state.
  if (bSave) {
    var newState = this.getHSSState();
    this._external.sendPPRProperty(this.getId(), DvtJsExt.PPR_ATTRIB_HSS, newState);
    
    var newLayersState = this.getHSLayersState();
    this._external.sendPPRProperty(this.getId(), DvtJsExt.PPR_ATTRIB_HS_LAYERS, newLayersState);
  }
};

DvtGraphCore.prototype._setMarkerUnderlayVisibility = function(displayable, bHide) {
  var keepHidden = this._isHiddenSeries(displayable._Dlo) || this._isHiddenUnderlay(displayable._Dlo);
  if (bHide && !keepHidden) {
    if (displayable.setUnderlayVisible)
      displayable.setUnderlayVisible(false);
    displayable.setVisible(false);
    
  }
  else if (!bHide && !keepHidden) {
    if (displayable.setUnderlayVisible)
      displayable.setUnderlayVisible(true);
    displayable.setVisible(true);
  }
}

/**
  *   Sets the hollow legend state for the legend marker represented by the sidx.
  *   @param {number}  sidx   The series index
  */
DvtGraphCore.prototype.updateHSLegendState = function(sidx, bSave) {
  if (this._aLegends && this._aLegends[sidx]) {
    var aLeg  = this._aLegends[sidx] ;   
    aLeg[0].setHollow(this.getSeriesColor(sidx)); //legend marker
    var bHidden = aLeg[0].isHollow();
    return bHidden
  }
}

/**
  *   Sets the hollow legend state for the legend marker represented by the sidx.
  *   @param {number}  midx   The misc. index used for split series and layer legends
  */
DvtGraphCore.prototype.updateHSMiscLegendState = function(midx, bSave) {
    var aLeg  = this._aMiscLegends ;
    var bHidden;
    if (aLeg) {
        for (i = 0; i < aLeg.length; i++) {
            var obj = aLeg[i];
            var objMidx = obj._Dlo.getMiscId() ;
            if (midx === objMidx && !(obj instanceof DvtText)) {
                obj.setHollow() ;
                bHidden = obj.isHollow();
            }
        }
    }
    
  if (bSave) {
    if (bHidden) {
      this._arHSLegendMiscId.push(midx);
    } else {
      for (var i=0; i<this._arHSLegendMiscId.length; i++) {
        if (this._arHSLegendMiscId[i] == midx)
          this._arHSLegendMiscId.splice(i,1);      
      }
    }
  }
    
    return bHidden;
}

/**
  *   Checks to see if object should remain hidden based on what legend markers are hidden
  *   @param {DvtDetObj}  obj   Object to be checked
  *   @private
  */
DvtGraphCore.prototype._isHiddenSeries = function (obj) {
    for (var i=0; i<this._HiddenSeries.length; i++) {
        if (obj.getSeriesIndex() == this._HiddenSeries[i] )
            return true;
    }
    return false;
}

/**
  *   Checks to see if underlay should remain hidden based on what legend markers are hidden
  *   @param {DvtDetObj}  obj   Object to be checked
  *   @private
  */
DvtGraphCore.prototype._isHiddenUnderlay = function (obj) {
    for (i=0; i<this._HiddenUnderlayStates.length; i++){
        var hiddenState = this._HiddenUnderlayStates[i];
        if (obj.getUnderlayType() == hiddenState || obj.getUnderlayFC() == hiddenState)
            return true;
    }
    return false;
}

/**
  *   Adds a series or underlay to list of currently hidden series or underlays respectively
  *   @param {string}  state   state to be added
  *   @param {boolean}  isSeries   True if state is a series index
  *   @private
  */
DvtGraphCore.prototype._addHiddenState = function(state, isSeries) {
    if (isSeries)
        this._HiddenSeries.push(state);
    else 
        this._HiddenUnderlayStates.push(state);
}

/**
  *   Removes a series or underlay to list of currently hidden series or underlays respectively
  *   @param {string} state   state to be removed
  *   @param {boolean} isSeries   True if state is a series index
  *   @private
  */
DvtGraphCore.prototype._removeHiddenState = function(state, isSeries) {
    // There can be duplicate states in this array if data is split series
    // so just remove first instance
    if (isSeries)
        for (var i=0; i<this._HiddenSeries.length; i++) {
            if (this._HiddenSeries[i] == state) {
                this._HiddenSeries.splice(i, 1);
                break;
        }
    }
    else {
        for (i=0; i<this._HiddenUnderlayStates.length; i++) {
            if (this._HiddenUnderlayStates[i] === state) {
                this._HiddenUnderlayStates.splice(i, 1);
                break;
            }
        }
    }
}


/*---------------------------------------------------------------------*/
/*  get/setHiddenMarkers()                                             */
/*---------------------------------------------------------------------*/
/**
  *   Returns an array of hidden marker objects. The array is organized in 
  *   series index order.
  *   @type Array
  */
DvtGraphCore.prototype.getHiddenMarkers = function()
{
   return this._aHMarkers ;

};

/**
  *   Sets the hidden markers objects array.
  *   @type Array
  */
DvtGraphCore.prototype.setHiddenMarkers = function(ar)
{
   this._aHMarkers = ar ;
};



/*---------------------------------------------------------------------*/
/*   get/setLegends()                                                  */
/*---------------------------------------------------------------------*/
/**
  *   Returns an array of legend objects. The array is organized in 
  *   series index order.
  *   @type Array
  */
DvtGraphCore.prototype.getLegends = function()
{
   return this._aLegends ;
};
/**
  *   Sets the legend objects array.
  *   @type Array
  */
DvtGraphCore.prototype.setLegends = function(legends)
{
   this._aLegends = legends;
};

DvtGraphCore.prototype.isMagnifyLensEnabled = function()
{
   if (this._magnifyLensDisplay == "OFF")
    return false;
   else if (this._magnifyLensDisplay == "AUTO") {
    // return false;
    if (DvtAgent.isTouchDevice() && (this.isBubbleChart() || this.isScatterChart())) {
        return true;
    }
   }
   return false;
};

DvtGraphCore.prototype.setMagnifyLensDisplay = function(display)
{
   this._magnifyLensDisplay = display;
};

/*---------------------------------------------------------------------*/
/*   get/setMiscLegends()                                              */
/*---------------------------------------------------------------------*/
/**
  *   Returns an array of legend objects that have no series index.
  *   @type Array
  */
DvtGraphCore.prototype.getMiscLegends = function()
{
   return this._aMiscLegends ;
};
/**
  *   Sets the legend objects array (of objects with no series index).
  *   @type Array
  */
DvtGraphCore.prototype.setMiscLegends = function(legends)
{
   this._aMiscLegends = legends;
};



/*---------------------------------------------------------------------*/
/*   DataCursor related                                                */
/*---------------------------------------------------------------------*/

DvtGraphCore.prototype.getDataCursor = function()
{
   return this._dataCursor ;
};

DvtGraphCore.prototype.setDataCursor = function(dataCursor)
{
   this._dataCursor = dataCursor;
};

// Determines if this graph should render a data cursor or not
DvtGraphCore.prototype.isDataCursorEnabled = function() {
   var chartType = this.getType();
   if ( this.isPieChart()                    ||
        this.isRingChart()                   || 
        this.isThreeDChart()                 ||
        chartType == DvtGraphType.POLAR      || 
        chartType == DvtGraphType.FUNNEL     || 
        chartType == DvtGraphType.RADAR_LINE)
        return false;
   if (this._dataCursorDisplay == "ON")
    return true;
   else if (this._dataCursorDisplay == "AUTO") {
    if (DvtAgent.isTouchDevice() && (this.isLineChart() || this.isArea())) {
        // BUG #12913318: Don't enable the data cursor if selection is enabled in the auto case
        if (this._selMode == DvtGraphCore.SEL_NONE) {
            return true;
        }
    }
   }
   return false;
};

DvtGraphCore.prototype.setDataCursorDisplay = function(display) {
    this._dataCursorDisplay = display;
}

/*---------------------------------------------------------------------*/
/*   Legend Selector related                                           */
/*---------------------------------------------------------------------*/

DvtGraphCore.prototype.getLegendSelector = function()
{
   return this._legendSelector ;
};

DvtGraphCore.prototype.setLegendSelector = function(legendSelector)
{
   this._legendSelector = legendSelector;
};

DvtGraphCore.prototype.setSeriesFocusEnabled = function(enabled)
{
   this._seriesFocus = enabled;
};

DvtGraphCore.prototype.isSeriesFocusEnabled = function() {
    return this._seriesFocus;
}

DvtGraphCore.HIDE_KEY = "hide";
DvtGraphCore.SHOW_KEY = "show";
DvtGraphCore.CLEAR_SELECTION_KEY = "clearselection";

// Text resources
DvtGraphCore.prototype.setTextResources = function(resources)
{
   this._textResources = resources;
};

DvtGraphCore.prototype.getTextResources = function()
{
   return this._textResources;
}

DvtGraphCore.prototype.getTextResource = function(key)
{
    if (this._textResources)
        return this._textResources[key];
    return null;
};

DvtGraphCore.prototype.getLegendRect = function()
{
   return this._legendRect ;
};

DvtGraphCore.prototype.setLegendRect = function(legendRect)
{
   this._legendRect = legendRect;
};


/*---------------------------------------------------------------------*/
/*   Scrollable legend related                                         */
/*---------------------------------------------------------------------*/

DvtGraphCore.prototype.getScrollableLegend = function()
{
   return this._scrollableLegend ;
};

DvtGraphCore.prototype.setScrollableLegend = function(sl)
{
   this._scrollableLegend = sl;
};

/*---------------------------------------------------------------------*/
/*  getMarkerTooltipHelper()                                           */
/*---------------------------------------------------------------------*/
/**
  *  Returns the {@link DvtTooltipHelper} for this chart. The DvtTooltipHelper
  *  is used to keep track of line markers, so that their tooltips can be
  *  shown without creating DetList entries.
  *  @param {Boolean} bCreateNew}  true if a new DvtTooltipHelper should be
  *                                created if one does not already exist.
  *  @type DvtTooltipHelper
  */
DvtGraphCore.prototype.getMarkerTooltipHelper = function(bCreateNew)
{
   if ((! this._MarkerTooltipHelper) && bCreateNew) {
     this._MarkerTooltipHelper = new DvtTooltipHelper(this);
   }

   return this._MarkerTooltipHelper;
} ;


/*---------------------------------------------------------------------*/
/*  get/setSelListener()                                               */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the MT has a slection listener.
  *  @type Boolean
  */
DvtGraphCore.prototype.getSelListener = function()
{
   return this._selListener ;
};
/**
  *  Sets if the MT has a selection listener.
  *  @param {Boolean}  true if listener, else false.
  */
DvtGraphCore.prototype.setSelListener = function(bListener)
{
   this._selListener = bListener;
};


/*---------------------------------------------------------------------*/
/*  get/setMenuIds()                                                   */
/*---------------------------------------------------------------------*/

DvtGraphCore.prototype.getMenuId = function(seriesIndex, groupIndex)
{
   var index = (seriesIndex * this._Data.getGroupCount()) + groupIndex;
   if(!this._aMenuIds || this._aMenuIds.length < index)
     return -1;
   else
     return this._aMenuIds[index];
};


DvtGraphCore.prototype.setMenuIds = function(ar)
{
   this._aMenuIds = ar ;
};


/*---------------------------------------------------------------------*/
/*  get/setTooltipFill()                                               */
/*---------------------------------------------------------------------*/

DvtGraphCore.prototype.getTooltipFill = function()
{
  return this._tooltipFill;
};

DvtGraphCore.prototype.setTooltipFill = function(fill)
{
  this._tooltipFill = fill;
};


/*---------------------------------------------------------------------*/
/*  get/setTooltipFontId()                                             */
/*---------------------------------------------------------------------*/

DvtGraphCore.prototype.setTooltipFontId = function(fontId)
{
  this._tooltipFontId = fontId;
};

DvtGraphCore.prototype.getTooltipFontId = function()
{
  return this._tooltipFontId;
};


/**
 * Returns a boolean indicating whether or not pie slice explode is enabled
 * 
 * @return {boolean}
 */
DvtGraphCore.prototype.isPieSliceExplodeEnabled = function()
{
  var pie = this.__getPieChart();
  if(pie)
  {
    return pie.getPieInfo().isSliceInteractive();
  }
  else
  {
    return false;
  }
}

/*---------------------------------------------------------------------*/
/*     setPieExplodeAllResource                                        */
/*---------------------------------------------------------------------*/
DvtGraphCore.prototype.setPieExplodeAllResource = function()
{
    // TDO
};
/*---------------------------------------------------------------------*/
/*    setPieUniteAllResource                                           */
/*---------------------------------------------------------------------*/
DvtGraphCore.prototype.setPieUniteAllResource = function()
{
   //TDO
};


/*----------------------------------------------------------------------*/
/*  isPieAllExploded()                                                  */
/*----------------------------------------------------------------------*/
///**  Returns true if the pie chart has all its slices exploded.
//  *  @returns {boolean} true if the pie chart has all its slices exploded.
//  */  
//DvtGraphCore.prototype.isPieAllExploded() = function
//{
//   var  bRet = false ;
//
//   if (this._pie) {
//     bRet =  this.pie.isAllExploded() ;
//   }
//   return bRet ;
//};


/*----------------------------------------------------------------------*/
/*  isPieAllUnited()                                                    */
/*----------------------------------------------------------------------*/
/**  Returns true if the pie chart has all its slices united (imploded, not exploded)
  *  @returns {boolean} true if the pie chart has all its slices united (imploded, not exploded)
  */
//DvtGraphCore.prototype.isPieAllUnited = function()
//{
//   var  bRet = false ;
//
//   if (this._pie) {
//     bRet = this._pie.isAllUnited() ;
//   }
//   return bRet ;
//};


/*----------------------------------------------------------------------*/
/*  isPieSliceClickExplosionSupported()                                 */
/*----------------------------------------------------------------------*/
/**  Returns true if the pie chart supports single-click automatic pie
 *   slice explosion.
/*  @returns {boolean} true if the pie chart supports single-click automatic pie
 *  slice explosion.
 */
//DvtGraphCore.prototype.isPieSliceClickExplosionSupported = function()
//{
//   return  pieChart.m_bAutoExplode ;
//};


/*---------------------------------------------------------------------*/
/*   get/setRisers()    getSeriesRisers()                              */
/*---------------------------------------------------------------------*/
/**
  *   Returns an array of riser objects arrays. The array is organized in 
  *   series index order. (See also {@link DvtGraphCore#getSeriesRisers}.)
  *   @type Array
  */
DvtGraphCore.prototype.getRisers = function()
{
   return this._aRisers ;
};
/**
  *   Sets the riser objects array.
  *   @type Array
  */
DvtGraphCore.prototype.setRisers = function(risers)
{
   this._aRisers = risers;
};

/**
  *   Returns an array of riser objects for a specific series.  (See also {@link DvtGraphCore#getRisers}.)
  *   @type Array
  */
DvtGraphCore.prototype.getSeriesRisers = function(sidx)
{
   return this._aRisers[sidx] ;
};


/*---------------------------------------------------------------------*/
/*  get/setSelectionMode()                                             */
/*---------------------------------------------------------------------*/
/**
  *  Returns the selection mode. Values are DvtGraphCore.SEL_NONE,
  *  DvtGraphCore.SEL_SINGLE, and DvtGraphCore.SEL_MULTI.
  *  @type number
  */
DvtGraphCore.prototype.getSelectionMode = function()
{
   return this._selMode ;
};
/**
  *  Sets the selecton mode.
  *  @param {number}  The selection mode. Values are DvtGraphCore.SEL_NONE,
  *  DvtGraphCore.SEL_SINGLE, and DvtGraphCore.SEL_MULTI.
  */
DvtGraphCore.prototype.setSelectionMode = function(mode)
{
   this._selMode = mode;
};



/**
  *  Determine if selection is supported on this chart.
  *  Returns true if selection is supported, otherwise
  *  returns false.
  *  
  *  @type boolean
  */
DvtGraphCore.prototype.isSelectionSupported = function()
{
   return (this.getSelectionMode() != DvtGraphCore.SEL_NONE);
};


/**
 *  Set initial selectedIds
 *  @param {Array} the selectedIds array
 */
DvtGraphCore.prototype.setSelectedIds = function(selIds)
{
    if (selIds) 
    {
      this._selectedIds = selIds;        
    }
};

/*---------------------------------------------------------------------*/
/*   getSeriesColor()                                                  */
/*---------------------------------------------------------------------*/
/**
  *   Returns the series color for the specified series index.
  *   @param {number}  seriesIndex  The series index.
  *   @returns {String}  The string color.
  */
DvtGraphCore.prototype.getSeriesColor = function(seriesIndex)
{
   return  this._Data.getSeriesColor(seriesIndex) ;
} ;


/*---------------------------------------------------------------------*/
/*  getSeriesCount()                                                   */
/*---------------------------------------------------------------------*/
/**
  *   Returns the series count for this chart.
  *   @type number
  */
DvtGraphCore.prototype.getSeriesCount = function()
{
   return this._Data.getSeriesCount() ;
} ;


/*---------------------------------------------------------------------*/              
/*   getFormattedData()                                                */
/*---------------------------------------------------------------------*/              
/**
  *  Returns a formatted value for specified type (x, or y, or z), for the
  *  specified series and group indices.
  *  @param {number}  vtype  The data type (e.g. {@link DvtGraphData#DV_X})
  *  @param {number}  sidx   The series index.
  *  @param {number}  gidx   The group index.
  *  @type String
  */
DvtGraphCore.prototype.getFormattedData = function(vtype, sidx, gidx)
{
    return this._Data.getFormattedValue(vtype, sidx, gidx);
}

/*---------------------------------------------------------------------*/              
/*   getData()                                                         */
/*---------------------------------------------------------------------*/              
/**
  *  Returns a value for specified type (x, or y, or z), for the
  *  specified series and group indices.
  *  @param {number}  vtype  The data type (e.g. {@link DvtGraphData#DV_X})
  *  @param {number}  sidx   The series index.
  *  @param {number}  gidx   The group index.
  *  @type number
  */
DvtGraphCore.prototype.getData = function(vtype, sidx, gidx)
{
  return this._Data.getValue(vtype, sidx, gidx) ;
};


/*---------------------------------------------------------------------*/              
/*   getDataObj()                                                      */
/*---------------------------------------------------------------------*/              
/**
  *  Returns the DvtGraphData object for this chart.
  *  @returns {DvtGraphData} the DvtGraphData object for this chart.
  */
DvtGraphCore.prototype.getDataObj = function()
{
  return this._Data ;
};


/*---------------------------------------------------------------------*/
/*   get/setSeriesEffect()                                             */
/*---------------------------------------------------------------------*/
/**
  *  Returns the series effect (e.g. {@link DvtFill#COLOR)).
  *  @type number
  */
DvtGraphCore.prototype.getSeriesEffect = function()
{
   return  this._seriesEffect ;
};


/**
  *   Sets the series effect (e.g. {@link DvtFill#COLOR)).
  *   @param {number} se  The series effect (e.g. {@link DvtFill#COLOR)).
  */
DvtGraphCore.prototype.setSeriesEffect = function(se)
{
   this._seriesEffect = se ;
};


/**
 * Returns the array of showPopupBehaviors for this chart.
 * @return {array} The array of showPopupBehaviors for this chart.
 */
DvtGraphCore.prototype.__getShowPopupBehaviors = function() {
  if(!this._showPopupBehaviors)
    this._showPopupBehaviors = new Array();

  return this._showPopupBehaviors;
}

/**
 * Specifies the array of showPopupBehaviors for this chart.
 * @param {array} behaviors The array of showPopupBehaviors for this chart.
 */
DvtGraphCore.prototype.__setShowPopupBehaviors = function(behaviors) {
  this._showPopupBehaviors = behaviors;  
}


/*---------------------------------------------------------------------*/
/*  get/setSROEffect()                                                 */
/*---------------------------------------------------------------------*/
/**
  *  Returns the series rollover effect value. Values are DvtGraphCore.RB_NONE,
  *  DvtGraphCore.RB_DIM, and DvtGraphCore.RB__HILITE.
  *  @type number
  */
DvtGraphCore.prototype.getSROEffect = function()
{
   return this._SRO;
};
/**
  *  Sets the series rollover effect value.
  *  @param {number}  The series rollover effect value. Values are DvtGraphCore.RB_NONE,
  *  DvtGraphCore.RB_DIM, and DvtGraphCore.RB_HILITE.
  */
DvtGraphCore.prototype.setSROEffect = function(effect)
{
   this._SRO = effect;
};


/*---------------------------------------------------------------------*/
/*   get/setVisualEffect()                                             */
/*---------------------------------------------------------------------*/
/**
  *  Gets the visual effect value (true or false).
  *  @type Boolean
  */
DvtGraphCore.prototype.getVisualEffect = function()
{
   return this._bVisEffect;
};

/**
  *  Sets the visual effect value (true or false).
  *  @param {Boolean} bVE
  */
DvtGraphCore.prototype.setVisualEffect = function(bVE)
{
   this._bVisEffect = bVE ;
};


/*---------------------------------------------------------------------*/
/*  getType()                                                          */
/*---------------------------------------------------------------------*/
/**
  *   Returns the chart type (see {@link DvtGraphType}).
  *   @type String
  */
DvtGraphCore.prototype.getType = function()
{
   return this._type ;
} ;


/*---------------------------------------------------------------------*/
/*  getWidth()                                                         */
/*---------------------------------------------------------------------*/
/**
  *   Returns the width of the chart.
  *   @type number
  */
DvtGraphCore.prototype.getWidth = function()
{
   return this._width ;
} ;


/*---------------------------------------------------------------------*/
/*     isArea()                                                        */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart is an area graph.
  *  @type Boolean
  */
DvtGraphCore.prototype.isArea = function()
{
   return this.getType().indexOf("AREA") != -1;
};


/*---------------------------------------------------------------------*/
/*   isBiDi()                                                          */
/*---------------------------------------------------------------------*/
/**
  *   Returns true if this is a BiDi chart or gauge.
  *   @type Boolean
  */
DvtGraphCore.prototype.isBiDi = function()
{
   return this._bBiDi ;
} ;



/*---------------------------------------------------------------------*/
/*  isDataAnimation()   Returns true if ADS/PPR data change animation  */
/*                      is enabled.                                    */
/*---------------------------------------------------------------------*/
/**  Returns true if ADS/PPR data change animation is enabled.
  *   @type Boolean
  */
DvtGraphCore.prototype.isDataAnimation = function()
{
   var anim = this._Animation ;

   return (anim && (anim.animOnDataChange !== DvtGraphAnimation.NONE)) ;
} ;

/*---------------------------------------------------------------------*/
/*     isBubbleChart()                                                 */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart is a bubble chart.
  *  @type Boolean
  */
DvtGraphCore.prototype.isBubbleChart = function()
{
   return (this._type.indexOf(DvtGraphType.BUBBLE) >= 0) ;
};

/*---------------------------------------------------------------------*/
/*     isLineChart()                                                   */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart is a line chart.
  *  @type Boolean
  */
DvtGraphCore.prototype.isLineChart = function()
{
   return (this._type.indexOf("LINE") >= 0) ;
};

/*---------------------------------------------------------------------*/
/*     isStockChart()                                                   */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart is a stock chart.
  *  @type Boolean
  */
DvtGraphCore.prototype.isStockChart = function()
{
   return (this._type.indexOf("STOCK") >= 0) ;
};

/*---------------------------------------------------------------------*/
/*     isPieChart()                                                    */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart is a pie chart.
  *  @type Boolean
  */
DvtGraphCore.prototype.isPieChart = function()
{
   return (this._type.indexOf("PIE") >= 0) ;
};

/*---------------------------------------------------------------------*/
/*     isRingChart()                                                   */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart is a ring chart.
  *  @type Boolean
  */
DvtGraphCore.prototype.isRingChart = function()
{
   return (this._type.indexOf("RING") >= 0) ;
};

/*---------------------------------------------------------------------*/
/*     isThreeDChart()                                                 */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart is a three D chart type.
  *  @type Boolean
  */
DvtGraphCore.prototype.isThreeDChart = function()
{
   return (this._type.indexOf("THREED") >= 0) ;
};

/*---------------------------------------------------------------------*/
/*     isDialGauge()                                                   */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart is of dial gauge type.
  *  @type Boolean
  */
DvtGraphCore.prototype.isDialGauge = function()
{
   return (this._type === DvtGraphType.DIAL) ;
};


/*---------------------------------------------------------------------*/
/*     isDualYAxis()                                                   */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart has dual Y-axes.
  *  @type Boolean
  */
DvtGraphCore.prototype.isDualYAxis = function()
{
   return (this._type.indexOf('2Y') >= 0) ;
};


/*---------------------------------------------------------------------*/
/*   isGauge()                                                         */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart is a gauge type.
  *  @type Boolean
  */
DvtGraphCore.prototype.isGauge = function()
{
   return  ( this._type === DvtGraphType.DIAL || 
             (this._type.indexOf(DvtGraphType.STATUSMETER) >= 0) ||
             this._type === DvtGraphType.LED ) ;
};


/*---------------------------------------------------------------------*/
/*     isGraph()      Returns true if the chart is a graph type        */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart is a graph, false if it is a gauge/statusmeter.
  *  @type Boolean
  */
DvtGraphCore.prototype.isGraph = function()
{
   return  (! this.isGauge()) ;
};


/*---------------------------------------------------------------------*/
/*     isHorizontal()                                                  */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart is horizontal in orientation
  *  @type Boolean
  */
DvtGraphCore.prototype.isHorizontal = function()
{
   return (this._type.indexOf('HORIZ') >= 0) ;
};



/*---------------------------------------------------------------------*/
/*  isInitFx()    Returns true if initial draw effects are enabled     */
/*---------------------------------------------------------------------*/
/**  Returns true if initial draw effects (excluding blackbox animation)
  *  are enabled.
  *  @type Boolean
  */
DvtGraphCore.prototype.isInitFx = function()
{
   var anim = this._Animation ;

   return (anim && (anim.animOnDisplay === DvtGraphAnimation.AUTO)) ;
} ;


/*---------------------------------------------------------------------*/
/*     isPie()      Returns true if the chart is a pie chart           */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart is a pie chart.
  *  @type Boolean
  */
DvtGraphCore.prototype.isPie = function()
{
   return  (this._type === DvtGraphType.PIE) ;
};


/*---------------------------------------------------------------------*/
/*     isPolarChart()      Returns true if the chart is a polar chart  */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart is a polar chart.
  *  @type Boolean
  */
DvtGraphCore.prototype.isPolarChart = function()
{
   return  (this._type === DvtGraphType.POLAR) ;
};


/*---------------------------------------------------------------------*/
/*   isScatterChart()                                                  */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart is a scatter chart.
  *  @type Boolean
  */
DvtGraphCore.prototype.isScatterChart = function()
{
   return  (this._type.indexOf(DvtGraphType.SCATTER) >= 0) ;
};


/*---------------------------------------------------------------------*/
/*     isSplit()                                                       */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart is split.
  *  @type Boolean
  */
DvtGraphCore.prototype.isSplit = function()
{
   return (this._type.indexOf('SPLIT') >= 0) ;
};


/*---------------------------------------------------------------------*/
/*     isSplit2Y()                                                     */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if the chart has a split 2Y axis.
  *  @type Boolean
  */
DvtGraphCore.prototype.isSplit2Y = function()
{
   return (this._type.indexOf('SPLIT2Y') >= 0) ;
};


/*---------------------------------------------------------------------*/
/*   isTooltipsEnabled()                                               */
/*---------------------------------------------------------------------*/
/**
 *  Returns true if tooltip display is enabled.  See also {@link DvtGraphCore#enableTooltips}.
 *  @type Boolean
 */
DvtGraphCore.prototype.isTooltipsEnabled = function()
{
   return this._bTooltips ;
};

/*---------------------------------------------------------------------*/
/*   isVertical()                                                      */
/*---------------------------------------------------------------------*/
/**
 *  Returns true if the chart is vertically orientated.
 *  @type Boolean
 */
DvtGraphCore.prototype.isVertical = function()
{
   if (this.isGauge())
       return this._type.indexOf('VERT') >= 0 ;
    else
       return this._type.indexOf('HORIZ') < 0;
};



/*----------------------------------------------------------------------*/
/*   log()                                                              */
/*----------------------------------------------------------------------*/
/**
  *   Forwards a message to the ADF logger.
  *   @param  {String} msg the message to be logged.
  *   @param  {number} level the log level of the messsage.  If omitted
  *                          a value of {DvtExternal#LOG_INFO} is assumed.
  */
DvtGraphCore.prototype.log = function(msg, level) {

  this.external.log(msg, level) ;
};



/*----------------------------------------------------------------------*/
/*   makeTooltip()    Return a tooltip created from either the chart's  */
/*                    tooltip template, or a specified tooltip, or null.*/
/*----------------------------------------------------------------------*/
/**
  *   Returns a tooltip created from either the chart's tooltip template,
  *   or a specified tooltip string, or null.
  *   @param {DvtObj}  The det object whose tooltip is to be generated.
  *   @type String
  */
DvtGraphCore.prototype.makeTooltip = function(obj)
{
  var s ;
  if (obj) {
    if (obj.hasTooltipText()) {     // is there a specifically defined tooltip
      s = obj.getTooltipText() ;
    }
    else  {
      // See if we can generate one from the chart's tt= template (<md> tag)

      try {
            s = this._Data.makeTooltipFromTemplate(obj.getSeriesIndex(), obj.getGroupIndex());
      }
      catch (err) {
//        alert("DVTChart.makeTooltip() -> (" + err.fileName + " line " + err.lineNumber + ")  " + err.name + "  " + err.message) ;
      }
    }
  }

  return s ;
} ;



/*---------------------------------------------------------------------*/
/*   makeTooltipFromSeriesGroup()                                      */
/*---------------------------------------------------------------------*/
/**
  *   Returns a tooltip created from the chart's tooltip template using
  *   the supplied series/group indices.
  *   @param {number} sidx   the series index.
  *   @param {number} gidx   the group index.
  *   @returns {String}  a tooltip created from the tooltip template and
  *   the supplied series/group indices.
  */
DvtGraphCore.prototype.makeTooltipFromSeriesGroup = function(sidx, gidx)
{
  var s ;

  // See if we can generate one from the chart's tt= template (<md> tag)

  try {
        s = this._Data.makeTooltipFromTemplate(sidx, gidx);
  }
  catch (err) {
    alert("DvtGraphCore.makeTooltipFromSeriesGroup() -> (" + err.fileName + " line " + err.lineNumber + ")  " + err.name + "  " + err.message) ;
    s = null ;
  }

  return s ;
} ;



/**
 * Processes the specified event.
 * @param {object} event
 * @param {object} source The object that fired the event.
 */
DvtGraphCore.prototype.processEvent = function(event, source) {
  // TODO  differentiate by type and handle
  // TODO  seriesRolloverEvent: internally process + distribute to other children like legend
  var type = event.getType();
  if(type == DvtCategoryHideShowEvent.TYPE_HIDE || type == DvtCategoryHideShowEvent.TYPE_SHOW ||
     type == DvtCategoryRolloverEvent.TYPE_OVER || type == DvtCategoryRolloverEvent.TYPE_OUT) {
    this._distributeToChildren(event);
    return;
  }
  
  // All other events should be dispatched out
  this.__dispatchEvent(event);
};

/**
 * Distributes the specified event to this chart's children.
 * @param {object} event
 * @param {object} source The object that fired the event.
 */
DvtGraphCore.prototype._distributeToChildren = function(event) {
  // Currently pie is the only child.  More may be added later.
  if(this._pieChart)
    this._pieChart.processEvent(event);
}

/*----------------------------------------------------------------------*/
/*   render()                                                           */
/*----------------------------------------------------------------------*/
/**
 *    Render of chart's det objects.
 */
DvtGraphCore.prototype.render = function()
{
   // Final prep of chart

   if (! this._bObjInit) {
     if (this.isBubbleChart()) {
       this._sortMarkers() ;
     }
     
     if (this.isDataCursorEnabled()) {
        // Initialize data cursor based on graph settings
        var dc = this.getDataCursor();
        var font = this.getFont(this.getTooltipFontId()) ;
        dc.setFont(font);
        dc.setTooltipFill(this.getDatatipFill());    

        if (dc.getBehavior() == DvtDataCursor.BEHAVIOR_AUTO) {
            if (this.isLineChart() || this.isArea()) {
                dc.setBehavior(DvtDataCursor.BEHAVIOR_SMOOTH);
            } else {
                dc.setBehavior(DvtDataCursor.BEHAVIOR_SNAP);
            }
        }
        // Series focus always enabled when a data cursor is present only if a legend is available
        if (this.getLegendRect()) {
            this.setSeriesFocusEnabled(true);
        }
     }

     // move all scrollbars to the very end of the object list
     // workaround for a webkit bug clip-path doesn't apply to mouse events
     this._bringScrollbarsToFront();

   }

   var arDetList = this._objList ;
   var len       = arDetList.length ;
   var i;
   var obj;
   for (i = 0; i < len; i++) {
     obj = arDetList[i] ;
     if (! this._bObjInit) {
       this.addChild(obj) ;
       obj._Dlo.init() ;
     }
   }
   
  // Set hide/show state
  if (this._arHSSState) {
    for (var i=0; i<this._arHSSState.length; i++) {
      if (!this._arHSSState[i]) 
        this._initHideShowState(i, -1);
    }
  }
  for (var i=0; i<this._arHSLegendMiscId.length; i++)
    this._initHideShowState(-1, this._arHSLegendMiscId[i]);
   
   //BUG FIX #12427741: apply shadows after the objects have been added to
   //the display list, but before they are rendered (because when they're
   //rendered and there are initial animation effects the sizes may be 
   //changed)
   this._applyShadows();
   this._applyBevels();
   
   for (i = 0; i < len; i++) {
     obj = arDetList[i] ;
     
     obj._Dlo.render() ;
   }

   this._renderAdditionalControls();

   //  Add event handlers if required.  The order added is the order
   //  that event handlers are passed events.

   if (! this._bObjInit) {
     this._bObjInit = true ;

     this._initEventHandler();

     var tooltipFill = this.getDatatipFill();
     var tooltipFont = this.getFont(this.getTooltipFontId());
     this._MEH.setTooltipStyle(tooltipFill, tooltipFont);
     
     if (this._selMode !== DvtGraphCore.SEL_NONE) {    // selection
      var mode = (this._selMode === DvtGraphCore.SEL_SINGLE ? DvtSelectionHandler.TYPE_SINGLE :
                                                          DvtSelectionHandler.TYPE_MULTIPLE);
      if (this._selectionHandler == null) this._selectionHandler = new DvtSelectionHandler(mode);
      this._MEH.setSelectionHandler(this._selectionHandler);
      
      if (this._selectedIds != null) 
      {
          // Call DvtSelectionHandler to process initial selections
          if (this.isPieChart() && this._pieChart) {
            this._selectionHandler.processInitialSelections(this._selectedIds, this._pieChart.__getSlices());
          } else {
            this._selectionHandler.processInitialSelections(this._selectedIds, this._aDLOs);  
          }
      }
    }

    this._MEH.setDragSource(this.getDragSource());

     if (this.isHSSeriesSupported()) {             // hide/show series
       this._addEventHandler(new DvtHSSEH(this)) ;
     }

     if (this._zoomDir != DvtGraphCore.DATA_ZOOM_NONE)
     {
        this._initZoomScrollHandlers();
     }

     var keyboardHandler = new DvtGraphKeyboardHandler(this); 
     this._MEH.setGraphKeyboardHandler(keyboardHandler);
   }   // end if objInit
   
   // Pie graph initial hide/show support.  This must be done after the shapes are rendered.
   if (!this._HSR && this._arHSSState && this._pieChart) {
     for (var i=0; i<this._arHSSState.length; i++) {
       if (!this._arHSSState[i]) {
         this._pieChart.hideSlice(i);
       }
     }
   }
} ;

DvtGraphCore.prototype._initHideShowState = function(sidx, miscId) {
  //Case 1: Legend marker is split series
  //Case 2: Legend marker is undleray color
  //Case 3: Legend marker is underlay shape
  //Case 4: Legend marker is series
  var bHidden;
  if (sidx === -1) {
    var layerList = this.getLayers();
    var layerSeries = layerList[miscId];
    var len = layerSeries.length;
    
    if (len > 0) {
      // Take first element and determine case
      var val = layerSeries[0];
      sidx = parseInt(val, 10);
      if (!isNaN(sidx)) { // Case 1
        bHidden = this.updateHSMiscLegendState(miscId, false);
        for (var i = 0; i< layerSeries.length; i++)
          this.updateHSSeriesState(bHidden, layerSeries[i], false) ;
      }
      else if (val.charAt(0) === '#') { // Case 2
        bHidden = this.updateHSMiscLegendState(miscId, false);
        this.updateHSSeriesUnderlayState(bHidden, val, false);
      } else { // Case 3
        type = (val ===  'c' ? DvtMarker.CIRCLE :
               (val === 's' ? DvtMarker.SQUARE :
               (val === 't' ? DvtMarker.TRIANGLE_UP :
               (val === 'td' ? DvtMarker.TRIANGLE_DOWN : DvtMarker.DIAMOND))));
        bHidden = this.updateHSMiscLegendState(miscId, false);
        this.updateHSSeriesUnderlayState(bHidden, type, false);
      }
    }
  } else { // Case 4
    bHidden = this.updateHSLegendState(sidx, false);
    this.updateHSSeriesState(bHidden, sidx, false) ;
  }
}

/**
  *  Render additional graph controls that were not added to the flat list while parsing
  *  @private
  */
DvtGraphCore.prototype._renderAdditionalControls = function() 
{
  if (this.getZoomButton()) {
      this.addChild(this.getZoomButton());
  }
  if (this.isDataCursorEnabled() || this.isMagnifyLensEnabled()) {
       var plotRect = this.getFrameRect();
       var dataCursorRect = plotRect;
       var xOffset = 0;
       var yOffset = 0;
       if (this.is3D()) {
            var frameSide = this.getFrameSide();
            if (frameSide) {
                var frameSideDim = frameSide.getDimensions();
                xOffset = frameSideDim.w;
                // TODO: plus 1 should be border width of plot frame
                yOffset = frameSideDim.h - plotRect.h + 1;
            }
       }
       dataCursorRect = new DvtRectangle(plotRect.x, plotRect.y, plotRect.w, plotRect.h);
       dataCursorRect.x += -xOffset;
       dataCursorRect.w += xOffset;
       dataCursorRect.h += yOffset;
       
       this._threeDHorizontalOffset = xOffset;
       this._threeDVerticalOffset = yOffset;
       
        var topPlotRect = dataCursorRect;
        var bottomPlotRect = dataCursorRect;

        var separator = this.getFrameSeparator();
        if (separator) {
            var separatorDim = separator.getDimensions();
            if (this.isHorizontal()) {
                if (this.is3D()) {
                   separatorDim = new DvtRectangle(separatorDim.x, separatorDim.y+yOffset, plotRect.w, 1);
                }
                topPlotRect = new DvtRectangle(dataCursorRect.x, dataCursorRect.y, separatorDim.x - dataCursorRect.x, dataCursorRect.h);        
                bottomPlotRect = new DvtRectangle(separatorDim.x, dataCursorRect.y, dataCursorRect.x + dataCursorRect.w - separatorDim.x, dataCursorRect.h);        
            } else {
                if (this.is3D()) {
                   separatorDim = new DvtRectangle(separatorDim.x, separatorDim.y+yOffset, 1, plotRect.h);
                }
                topPlotRect = new DvtRectangle(dataCursorRect.x, dataCursorRect.y, dataCursorRect.w, separatorDim.y - dataCursorRect.y);        
                bottomPlotRect = new DvtRectangle(dataCursorRect.x, separatorDim.y, dataCursorRect.w, dataCursorRect.y + dataCursorRect.h - separatorDim.y);        
            }
        }
        this._topPlotRect = topPlotRect;
        this._bottomPlotRect = bottomPlotRect;
        
  }
  if (this.isDataCursorEnabled()) {
      var dcContainer = new DvtContainer(this.getCtx());
      this._dcContainer = dcContainer;
      var dataCursor = this.getDataCursor();
      dcContainer.addChild(dataCursor);
      this.addChild(this._dcContainer);
  }
  if (this.isMagnifyLensEnabled()) {         
      var magnifyLens = this.getCtx().getCustomTooltipManager().getMagnifyLens();    
      var bgFill = this.getDatatipFill();
      if (bgFill)
          magnifyLens.setBackgroundFill(bgFill);
      
      magnifyLens.initialize();
      
  }

};


/*---------------------------------------------------------------------*/
/*   setAnimation()                                                    */
/*---------------------------------------------------------------------*/
/**
  *   Set the {link DvtAnimation} object for this chart.
  *   @param {DvtGraphAnimation} anim The chart animation object.
  */
DvtGraphCore.prototype.setAnimation = function(anim)
{
   this._Animation = anim ;
} ;


/*---------------------------------------------------------------------*/
/*   setCBGRect()                                                      */
/*---------------------------------------------------------------------*/
/**
  *     Notes the chart background area dimensions.
  */
DvtGraphCore.prototype.setBGRect = function(x, y, w, h)
{
   this._rCBGX  = x ;
   this._rCBGY  = y ;
   this._rCBGW  = w ;
   this._rCBGH  = h ;
};


/*---------------------------------------------------------------------*/
/*   setBiDi()                                                         */
/*---------------------------------------------------------------------*/
/**
  *   Notes whether the graph has BiDi elements.  See also {@link DvtGraphCore#isBiDi}.
  *   @param {Boolean}  bBiDi  True if the chart is BiDi.
  */
DvtGraphCore.prototype.setBiDi = function(bBiDi)
{
   this._bBiDi = bBiDi ;
} ;


/*---------------------------------------------------------------------*/
/*   setData()            Sets the data block for this chart           */
/*---------------------------------------------------------------------*/
/**
  *   Sets the parsed data block from the graph in this chart.
  *   @param {DvtGraphData}  data  The data object.
  */
DvtGraphCore.prototype.setData = function(data)
{
   this._Data = data ;
} ;


/*---------------------------------------------------------------------*/
/*   setDataSummary()                                                  */
/*---------------------------------------------------------------------*/
/**
  *   Sets the parsed data summary for this chart.
  *   @param {DvtDataSummary}  ds  The data summary object.
  */
DvtGraphCore.prototype.setDataSummary = function(ds)
{
   this._DataSummary = ds ;
} ;



/*---------------------------------------------------------------------*/
/*  setSeriesMarkerSize()    Sets the marker size for the supplied     */
/*                           series index.                             */
/*---------------------------------------------------------------------*/

DvtGraphCore.prototype.setSeriesMarkerSize = function(iSeriesIndex, size)
{
  if (this._arSeriesMarkerSize === null) {
     this._arSeriesMarkerSize = [] ;
  }

  var  seriesCount  = this.getSeriesCount() ;

  if (iSeriesIndex >= 0 && iSeriesIndex < seriesCount) {
     this._arSeriesMarkerSize[iSeriesIndex] = size ;
  }
} ;


/*---------------------------------------------------------------------*/
/*   setXML()       Parse the XML into an object list                  */
/*---------------------------------------------------------------------*/
/**
  *  Parses the supplied graph/gauge XML.
  *  @param {String} sXML  the graph XML.
  *  @param {DvtGraphAnimation} anim  the animation object.  This only has
  *  the animation type (from the prepended tag) at this point.  If no
  *  prepended animation string, this argument is null.
  */
DvtGraphCore.prototype.setXML = function(sXML, anim)
{
//   try {

     this._Animation = anim ;

     var  parser     = new DvtGraphParser(this._ctx, this, anim) ;
     this._objList   = parser.parse(sXML) ;
//   }
//   catch (err) {
//      alert("DvtGraphCore.setXML() -> (" + err.fileName + " line " + err.lineNumber + ")  " + err.name + "  " + err.message) ;
//   } 

     if (this._bBiDi && this._ctx.fixBrowserTextRTL) {
       //  Bug 12566921.  Chrome (as at version 12.0.742.91 6/12/11) honors
       //  the dir="rtl" on the top <html> for the page and causes text
       //  alignment rendering issues with the current MT generated XML (because
       //  the (x,y) now become associated with the right hand text side) ;  IE9,
       //  Firefox and Safari currently do not. This fix overrides this
       //  locally for the svg element, and isolates the fix to the Dvt
       //  toolkit for BiDi only to avoid any undesirable external consequences
       //  in Adf.
     
       this._ctx.fixBrowserTextRTL() ;
     }

} ;

/**
 * Returns the graph context object for the given target of an event.
 */
DvtGraphCore.prototype.getGraphContext = function (obj) {
    return new DvtGraphContext(obj);
} ;

/**
 * Returns an Array of graph context objects for the current selection.
 */
DvtGraphCore.prototype.getSelectionContext = function () {
    var selection = this.getSelectionHandler().getSelection();
    if (selection && selection.length > 0) {
        var selectionContext = [] ;
        for (var i = 0; i < selection.length; i++) {
            var graphContext = this.getGraphContext(selection[i]);
            selectionContext.push(graphContext);
        }
    }
    return selectionContext;
} ;

/*---------------------------------------------------------------------*/
/*   paint()               Redraw the chart.                           */
/*---------------------------------------------------------------------*/

/**
  *    //TDO
  */
/*
DvtGraphCore.prototype.paint = function()
{

   this._ctx.clearRect(0, 0, this._width, this._height) ;

   try {
          this.drawAll(true) ;
   }
   catch (err) {
      alert("DvtGraphCore.paint() -> (" + err.fileName + " line " + err.lineNumber + ")  " + err.name + "  " + err.message) ;

   } 
} ;
*/


/*---------------------------------------------------------------------*/
/*  setPlotRect()                                                      */
/*---------------------------------------------------------------------*/
/**
  *     Notes the plot area dimensions from the parser.
  */
DvtGraphCore.prototype.setPlotRect = function(x, y, w, h)
{
    this._rPlotX  = x ;
    this._rPlotY  = y ;
    this._rPlotW  = w ;
    this._rPlotH  = h ;
};

/*---------------------------------------------------------------------*/
/*   get/setXAxis(), get/setY1Axis(), get/setY2Axis()                  */
/*---------------------------------------------------------------------*/
/**
  *   Returns the X-axis object.
  *   @returns {DvtGraphAxis} the X-axis object.
  */
DvtGraphCore.prototype.getXAxis = function()
{
   return this._XAXis ;
};
/**
  *   Sets the X-axis object for the chart.
  *   @param {DvtGraphAxis} axis  the X-axis object.
  */
DvtGraphCore.prototype.setXAxis = function(axis)
{
   this._XAXis = axis ;
};
/**
  *   Returns the Y1-axis object.
  *   @returns {DvtGraphAxis} the Y1-axis object.
  */
DvtGraphCore.prototype.getY1Axis = function()
{
   return this._Y1AXis ;
};
/**
  *   Sets the Y1-axis object for the chart.
  *   @param {DvtGraphAxis} axis  the Y1-axis object.
  */
DvtGraphCore.prototype.setY1Axis = function(axis)
{
   this._Y1AXis = axis ;
};
/**
  *   Returns the Y2-axis object.
  *   @returns {DvtGraphAxis} the Y2-axis object.
  */
DvtGraphCore.prototype.getY2Axis = function()
{
   return this._Y2AXis ;
};
/**
  *   Sets the Y2-axis object for the chart.
  *   @param {DvtGraphAxis} axis  the Y2-axis object.
  */
DvtGraphCore.prototype.setY2Axis = function(axis)
{
   this._Y2AXis = axis ;
};

DvtGraphCore.prototype.getMarqueeZoomMode = function()
{
   return this._MarqueeZoomMode ;
};

DvtGraphCore.prototype.setMarqueeZoomMode = function(mode)
{
   this._MarqueeZoomMode = mode ;
};

DvtGraphCore.prototype.getPanMode = function()
{
   return this._PanMode ;
};

DvtGraphCore.prototype.setPanMode = function(mode)
{
   this._PanMode = mode ;
};

/*---------------------------------------------------------------------*/
/*   get/setZoomScrollBehavior()                                       */
/*---------------------------------------------------------------------*/

DvtGraphCore.prototype.getZoomScrollBehavior = function()
{
   return this._zoomScrollBehavior ;
};

DvtGraphCore.prototype.setZoomScrollBehavior = function(zaS)
{
   this._zoomScrollBehavior = zaS ;
};


/*---------------------------------------------------------------------*/
/*   get/setZoomDir()                                                  */
/*---------------------------------------------------------------------*/

DvtGraphCore.prototype.setZoomDir = function(zd)
{
   this._zoomDir = zd ;
} ;

DvtGraphCore.prototype.getZoomDir = function()
{
   return this._zoomDir;
} 

/*---------------------------------------------------------------------*/
/*   get/setZoomInOutJs()                                              */
/*---------------------------------------------------------------------*/

DvtGraphCore.prototype.setZoomInOutJs = function(zirJs, zorJs, zicJs, zocJs)
{
   this._zoomInRectEventJs   = zirJs ;
   this._zoomOutRectEventJs  = zorJs ;
   this._zoomInClickEventJs  = zicJs ;
   this._zoomOutClickEventJs = zocJs ;
};

DvtGraphCore.prototype.setZoomMode = function(mode)
{
   this._MarqueeZoomMode = mode;
   this._initZoomScrollHandlers();
}

DvtGraphCore.prototype._initZoomScrollHandlers = function()
{
    var isZooming = this._MarqueeZoomMode == DvtGraphMarqueeZoomHandlerBase.ZOOM_ON;
    var plotRect = this.getFrameRect();

    // Graph panning
    var graphPanHandler = null;
    if (this._PanMode != DvtGraphPanHandler.PAN_OFF && !isZooming) {
        graphPanHandler = new DvtGraphPanHandler( this.getCtx(), this, plotRect, this._PanMode);
    }
    this._MEH.setGraphPanHandler(graphPanHandler);

    // Marquee zoom
    var marqueeZoomHandler = null;
    if (this._MarqueeZoomMode != DvtGraphMarqueeZoomHandlerBase.ZOOM_OFF) {
        marqueeZoomHandler = new DvtGraphMarqueeZoomHandler(this.getCtx(), this, 
                                        plotRect,
                                        this._MarqueeZoomMode, this._zoomDir);
    }
    this._MEH.setGraphMarqueeHandler(marqueeZoomHandler);
}

/*---------------------------------------------------------------------*/
/*   get/setZoomButton()                                               */
/*---------------------------------------------------------------------*/

DvtGraphCore.prototype.getZoomButton = function()
{
   return this._zoomButton ;
} ;

DvtGraphCore.prototype.setZoomButton = function(zb)
{
   this._zoomButton = zb ;
} ;

/**
 * Returns the context menu handler for this chart.
 * @return {DvtContextMenuHandler} The context menu handler for this chart.
 */
DvtGraphCore.prototype.__getContextMenuHandler = function() {
  // Make sure the context menu handler is initialized
  if(!this._contextMenuHandler)
    this._contextMenuHandler = new DvtContextMenuHandler(this.getCtx());
  
  return this._contextMenuHandler;
};

/*----------------------------------------------------------------------*/
/*    sortMarkers()          Sort markers in descending size            */
/*----------------------------------------------------------------------*/
/**
  *   Sort markers in the det list in descending order by size. 
  *   @private
  */
DvtGraphCore.prototype._sortMarkers = function()
{
   var o;
   var dlo ;
   var i,j,k ;

   var arTemp = [] ;
   
   var ar  = this._aRisers ;
   if (! ar) {
     return ;
   }
   var len = ar.length ;

   for (i = 0; i < len; i++) {
      var ar2  = ar[i] ;
      if (! ar2) {
        continue ;
      }
      var len2 = ar2.length ;
      for (j = 0; j < len2; j++) {
         o = ar2[j] ;
         dlo = o._Dlo ;
         if (dlo._objId === DvtGraphObjType.LINE_MARKER)  {
           var type = o.getType() ;
           if (type === DvtMarker.CIRCLE || type === DvtMarker.HUMAN) {
             var obj    = {};
//             obj.series = dlo._seriesIdx ;
//             obj.group  = dlo._groupIdx ;
             obj.size   = o.getSize() ;
             obj.marker = o ;

             // temp  TDO
             var idx = -1 ;
             for (var z = 0, dlen = this._objList.length; z < dlen; z++) {
                if (this._objList[z] === o) {
                  idx = z;
                  break;
                }
             }
             if (idx >= 0) {
               obj.index  = idx ; 
             }
             arTemp.push(obj) ; 
           }
         }
      }
   }
   
   var tmplen = arTemp.length ;
   if (tmplen > 0)  {
      var arSort = DvtArrayUtils.copy(arTemp) ;
      arSort.sort(_fnSort) ;
 
      var len = arSort.length ;
      for (i = 0; i < len; i++)  {
         var idx = arTemp[i].index ;
         if (idx >= 0) {
           this._objList[idx] = arSort[i].marker ;
         }
      }
   }    // end if
};


function _fnSort(a, b)
{
   return (b.size - a.size) ;
};

//---------------------------------------------------------------------//
// Ordering Support: ZOrderManager impl                                //
//---------------------------------------------------------------------//

/**
 * @override
 */
DvtGraphCore.prototype.bringToFrontOfSelection = function(displayable)
{
  var par = displayable.getParent();
  if (par)
  {
    var parentChildCount = par.getNumChildren();
    if (par == this) {
        // The data cursor should always be above the current selected item, if rendered on the chart
        if (this._dcContainer) {
          var childIndex = this.getChildIndex(this._dcContainer);
          if (childIndex != -1) {
            this._numFrontObjs = parentChildCount - childIndex;   
          } else {
            this._numFrontObjs = 0;  
          }
        }
    }

    if (parentChildCount - this._numFrontObjs > 1)
    {
      par.removeChild(displayable);
      var newIndex = parentChildCount - this._numFrontObjs - 1;
      par.addChildAt(displayable, newIndex);
    }
  }
  
  //if the object is not both selected and selecting then
  //increment the counter (otherwise the object is already
  //represented in the counter)
  if (!displayable.isSelected() || !displayable.isHoverEffectShown())
  {
    this._numSelectedObjsInFront++;
  }
};

/**
 * @override
 */
DvtGraphCore.prototype.pushToBackOfSelection = function(displayable)
{
  //decrement the counter
  if (this._numSelectedObjsInFront > 0)
  {
    this._numSelectedObjsInFront--;
  }
  
  //move the object to the first z-index before the selected objects
  var par = displayable.getParent();
  if (par)
  {
    var parentChildCount = par.getNumChildren();
    var newIndex = parentChildCount - this._numFrontObjs - 1 - this._numSelectedObjsInFront;
    if (newIndex >= 0)
    {
      par.removeChild(displayable);
      par.addChildAt(displayable, newIndex);
    }
  }
};

/**
 * @override
 */
DvtGraphCore.prototype.setNumFrontObjs = function(num)
{
  this._numFrontObjs = num;
};

//BUG FIX #12427741: apply shadows after the objects have been added to
//the display list
/**
 * @private
 */
DvtGraphCore.prototype._applyShadows = function()
{
  //the parser should have set the shadow map on the chart if there was one
  if (this._shadowMap)
  {
    var shadowMapLength = this._shadowMap.length;
    var obj;
    var shadow;
    for (var i = 0; i < shadowMapLength; i+=2)
    {
      obj = this._shadowMap[i];
      shadow = this._shadowMap[i+1];
      obj.addDrawEffect(shadow);
    }
  }
  this._shadowMap = null;
};

//BUG FIX #12427741: apply bevels after the objects have been added to
//the display list
/**
 * @private
 */
DvtGraphCore.prototype._applyBevels = function()
{
  //the parser should have set the bevel map on the chart if there was one
  if (this._bevelMap)
  {
    var bevelMapLength = this._bevelMap.length;
    var obj;
    var bevel;
    for (var i = 0; i < bevelMapLength; i+=2)
    {
      obj = this._bevelMap[i];
      bevel = this._bevelMap[i+1];
      obj.addDrawEffect(bevel);
    }
  }
  this._bevelMap = null;
};


/**
 * Returns the only DvtHandleManager instance
 */
DvtGraphCore.prototype.getHandleManager = function() {
    if (this._handleManager == null) {
        this._handleManager = new DvtHandleManager(this);
    }
    return this._handleManager;
}


DvtGraphCore.prototype.getCurrentSeries = function() {
    return this._currentSeries;
}

DvtGraphCore.prototype.setCurrentSeries = function(series) {
    this._currentSeries = series;
}

DvtGraphCore.prototype.getFrameSeparator = function() {
    return this._frameSeparator;
}

DvtGraphCore.prototype.setFrameSeparator = function(separator) {
    this._frameSeparator = separator;
}

DvtGraphCore.prototype.getFrameSide = function() {
    return this._frameSide;
}

DvtGraphCore.prototype.setFrameSide = function(side) {
    this._frameSide = side;
}

/**
 * Creates a sub ID using the chart's ID  
 * 
 * @param {string} id The ID to create the sub ID with
 */
DvtGraphCore.prototype.createSubId = function(id)
{
  return id + "_" + this.getId();
};


/**
 * Moves scrollbars to the very end of the chart's object list.
 * This is a workaround for a webkit bug - mouse events ignore clippath.
 * Need to ensure that the clipped plot area background isn't above scrollbars.
 */
DvtGraphCore.prototype._bringScrollbarsToFront = function ()
{
     var n = this._objList.length;
     var j;
     var arSb = [];

     // remove scrollbars from the objList array
     for (j = 0; j < n; j++) {
         if (this._objList[j] instanceof DvtScrollbar) {
             arSb.push(this._objList[j]);
             this._objList.splice(j, 1);
             j--;n--;
         }
     }
     // and re-append them to the and
     for (j = 0; j < arSb.length; j++) {
         this._objList.push(arSb[j]);
     }
}


/**
 * Returns the current selection state as string.
 */
DvtGraphCore.prototype.getSelectionState = function() {

    var selection = this.getSelectionHandler().getSelection();
    var selString = "";

    for (var i = 0; i < selection.length; i++) {
        if (this.isPieChart()) {
          selString += selection[i].getId() + "~:";  
        } else {
          selString += selection[i].getSeriesId() + "~:" + selection[i].getGroupId() + "~;";
        }
    }

    // strip the trailing separator
    selString = selString.slice(0, -2);

    return selString;
}


/**
 * @export
 * Returns the drag source for this component.
 * @return {DvtDragSource}
 */
DvtGraphCore.prototype.getDragSource = function() {
  return this._dragSource;
}

/**
 * Returns the drop target for this component.
 * @return {DvtDropTarget}
 */
DvtGraphCore.prototype.getDropTarget = function() {
  return this._dropTarget;
}

/**
 * @export
 * Rreturns the dragTransferable object.
 */
DvtGraphCore.prototype.getDragTransferable = function(mouseX, mouseY) {
    // Selection must be enabled for drag and drop to be supported
    if(!this.getSelectionHandler())
      return null;

    // add the current drag object to selection, if not there yet
    var dragObj = this.getDragSource().getDragObject();

    if (!dragObj.isSelected()) {
        this.getSelectionHandler().processClick(dragObj._Dlo, false);
        this._MEH.fireSelectionEvent();
    }

    var selection = this.getSelectionHandler().getSelection();

    var selString = this.getSelectionState();

    var dragParams = new Object;
    dragParams.selection = selString;

    // if this is not a multi selection, send individual object info
    if (selection.length <= 1 && dragObj._Dlo) {
        dragParams['seriesId'] = dragObj._Dlo.getSeriesId();
        dragParams['groupId'] = dragObj._Dlo.getGroupId();
    }
    return dragParams;
}


/**
 * Returns an Object containing the drag context info.
 */
DvtGraphCore.prototype.getDragContext = function(mouseX, mouseY) {

    var dragObj = this.getDragSource().getDragObject();

    // curently, only markers can be dragged
    if (!dragObj || !(dragObj instanceof DvtMarker))
        return null;

    var obj = dragObj._Dlo;
    var ret = new Object;
    var selections = this.getSelectionHandler().getSelection();

    // for multi selection, give the x/y values of the mouse position
    if (selections.length > 1) {
        ret.xValue = this.getXValue(mouseX);
        ret.yValue = this.getYValue(mouseY);
    }
    else {
        // single selection case
        ret.xValue = this.getData(DvtGraphData.DV_X, obj.getSeriesIndex(), obj.getGroupIndex());
        ret.yValue = this.getData(DvtGraphData.DV_Y, obj.getSeriesIndex(), obj.getGroupIndex());
    }
    return ret;
};


/**
 * @export
 * Returns the feedback for the drag operation.
 */
DvtGraphCore.prototype.getDragOverFeedback = function(mouseX, mouseY) {

    // for graph, the drag is based on current selection
    var selection = this.getSelectionHandler().getSelection();

    if (selection.length <= 0)
        return null;

    var objects = [];
    for (var i = 0; i < selection.length; i++) {
        objects.push(selection[i].getDetObj());
    }

    return objects;
}

/**
 * If a drop is possible at these mouse coordinates, returns the client id
 * of the drop component. Returns null if drop is not possible.
 */
DvtGraphCore.prototype.acceptDrag = function(mouseX, mouseY, clientIds) {

    var rect = this.getPlotRect();

    if (rect != null
            && mouseX >= rect.x && mouseX <= rect.x + rect.w
            && mouseY >= rect.y && mouseY <= rect.y + rect.h) {
        return clientIds[0];
    }
    else {
        this.dragExit();
    }
    return null;
}


/**
 * Paints drop site feedback as a drag enters the drop site.
 */
DvtGraphCore.prototype.dragEnter = function() {
    var rect = this.getPlotRect();
    if (rect) {
        this._dropSiteFeedback = new DvtRect(this.getCtx(), rect.x, rect.y, rect.w, rect.h, "dropSiteFeedback");
        this._dropSiteFeedback.setSolidStroke(this._dragFeedbackBorderColor, null, 1);
        this._dropSiteFeedback.setSolidFill(this._dragFeedbackFillColor, this._dragFeedbackFillAlpha);
        this._dropSiteFeedback.setPixelHinting(true);
        // TODO: use a better mechanism to find the z-order position for dropSiteFeedback rect
        this.addChildAt(this._dropSiteFeedback, 2);
    }
}

/**
 * Cleans up drop site feedback as a drag exits the drop site.
 */
DvtGraphCore.prototype.dragExit = function() {
    if (this._dropSiteFeedback != null) {
        this.removeChild(this._dropSiteFeedback);
        this._dropSiteFeedback = null;
    }
}


/**
 * Returns the object representing the drop site. This method is called when a valid
 * drop is performed.
 */
DvtGraphCore.prototype.getDropSite = function(mouseX, mouseY) {

    var dropSite = new Object;

    // TODO: for graphs that are not bubble or scatter type, return empty object

    dropSite.x = this.getXValue(mouseX);
    dropSite.y = this.getYValue(mouseY);

    // currently drag and drop works for markers only in graph

    // special handling for marker dragging within the same graph
    var dragObj = this.getDragSource().getDragObject();

    if (dragObj != null) {
        var marker = dragObj;
        if (marker instanceof DvtGraphDetMarker) {

            if (this.getSelectionMode() != DvtGraphCore.SEL_MULTI) {
                // for single and multiple selection within the same graph,
                // we need to support the legacy oldX and oldY (? comment in Flash code)
                var obj = marker._Dlo;
                dropSite.oldX = this.getData(DvtGraphData.DV_X, obj.getSeriesIndex(), obj.getGroupIndex());
                dropSite.oldY = this.getData(DvtGraphData.DV_Y, obj.getSeriesIndex(), obj.getGroupIndex());
            }

            // move markers to new locations
            var selection = this.getSelectionHandler().getSelection();
            var dragCoords = this.getDragSource().getDragCoords();
            
            var offsetX = mouseX - dragCoords.x;
            var offsetY = mouseY - dragCoords.y;

            // move selected markers one by one
            if (selection.length > 1) {
                for (var i = 0; i < selection.length; i++) {
                    var detObj = selection[i].getDetObj();
                    if (detObj && (detObj instanceof DvtGraphDetMarker)) {
                        detObj.setTranslateX(detObj.getTranslateX() + offsetX);
                        detObj.setTranslateY(detObj.getTranslateY() + offsetY);
                    } 
                }
            }
            else {
                // for single selection, adjust for the drag pointer offset
                var dragPointerOffset = this.getDragSource().__getPointerOffset();
                if (dragPointerOffset) {
                    offsetX += dragPointerOffset.x;
                    offsetY += dragPointerOffset.y;
                }
                
                marker.setTranslateX(marker.getTranslateX() + offsetX);
                marker.setTranslateY(marker.getTranslateY() + offsetY);
            }
        }
    }
    return dropSite;
}


/**
 * For drag and drop occuring with the same graph, returns the drag data. This
 * functin is used to maintain backward compatibility until ADF Faces supports
 * custom marshalling. (JC: is this still needed?)
 */
DvtGraphCore.prototype.getDragFeedback = function() {
    if (this.getDragSource().getDragObject() && this.getSelectionMode() != DvtGraphCore.SEL_MULTI) {
        return this.getDragTransferable();
    }
    return null;
}


/**
 * Returns the drag feedback bounds in container's coordinates.
 */
DvtGraphCore.prototype.getDragFeedbackBounds = function() {

    // for graph, the drag is based on current selection
    var selection = this.getSelectionHandler().getSelection();

    if (selection.length <= 0)
        return null;

    var objects = [];

    for (var i = 0; i < selection.length; i++) {
        objects.push(selection[i].getDetObj());
    }
    return DvtDnDUtils.getDragFeedbackBounds(objects);
}

/**
 * Sets the drop feedback border color.
 */
DvtGraphCore.prototype.setDragFeedbackBorderColor = function(color) {
    this._dragFeedbackBorderColor = color;
}

/**
 * Sets the drop feedback fill color.
 */
DvtGraphCore.prototype.setDragFeedbackFillColor = function(color) {
    this._dragFeedbackFillColor = color;
}

/**
 * Sets the drop feedback fill alpha.
 */
DvtGraphCore.prototype.setDragFeedbackFillAlpha = function(alpha) {
    this._dragFeedbackFillAlpha = alpha;
}



/**
 * Returns the x value for the given mouse coord.
 */
DvtGraphCore.prototype.getXValue = function(xCoord) {

    var ds = this.getDataSummary();

    if (isNaN(ds.x1MinVal) || isNaN(ds.xPxMin) || isNaN(ds.xPxRatio) || (ds.xPxRatio == 0))
        return NaN;

    // Bug 13365887: also needs to work for inverted X axis
    if (ds.xPxMin <= ds.xPxMax)
        return ds.x1MinVal + (xCoord - ds.xPxMin) / ds.xPxRatio;
    else
        return ds.x1MinVal + (ds.xPxMin - xCoord) / ds.xPxRatio;
}

/**
 * Returns the y value for the given mouse coord.
 */
DvtGraphCore.prototype.getYValue = function(yCoord) {

    // TODO: note this only works for the vertical Y1 axis (like in Flash)

    var ds = this.getDataSummary();

    if (isNaN(ds.y1MinVal) || isNaN(ds.yPxMin) || isNaN(ds.yPxRatio) || (ds.yPxRatio == 0))
        return NaN;

    return ds.y1MinVal + (ds.yPxMin - yCoord) / ds.yPxRatio;
}


/**
 * Returns the SelectionHandler instance.
 */
DvtGraphCore.prototype.getSelectionHandler = function () {
    return this._selectionHandler;
}

/**
 * Returns the coordinate offset of any scrolls that happened since the last time xml was recieved.
 */
DvtGraphCore.prototype.getScrollOffset = function(handleID) {
  var handle = this.getHandleManager().getHandle(handleID);
  if(handle != null) {
    return new DvtPoint(handle.getX(), handle.getY());
  }
  // None found, no offset needed
  return new DvtPoint(0, 0);
}

/**
 * Returns the DvtEventManager for this component.
 * @return {DvtEventManager}
 */
DvtGraphCore.prototype.__getEventManager = function() {
  // Initialize MEH if not already there
  this._initEventHandler();

  return this._MEH;
}

/**
 * Returns the logical object corresponding to the physical target
 * @param {Object} target
 * @return {Object}
 */
 DvtGraphCore.prototype.getLogicalObject = function(target)
 {
    // Initialize MEH if not already there
    this._initEventHandler();

    return this._MEH.GetLogicalObject(target);   
 }
 
 /**
  *  Returns true if new hide show behavior should be used.
  *  @type boolean
  */
DvtGraphCore.prototype.useHSNewBehavior = function()
{
   return this._HSNB;
};

/**
  *  Sets whether the new hide show behavior should be used
  *  @param String
  */
DvtGraphCore.prototype.setHSNewBehaviorUsage = function(hsBehavior)
{
   this._HSNB = hsBehavior;
};

/**
  *  Sets whether the graph uses hs with or without rescale
  *  @param boolean
  */  
DvtGraphCore.prototype.setHSRescale = function(hsBehavior)
{
   this._HSR= hsBehavior;
};

 /**
  *  Returns true if new hide show behavior should be used.
  *  @type boolean
  */
DvtGraphCore.prototype.isAttachmentMode = function()
{
   return this._attMode;
};

/**
  *  Sets whether the new hide show behavior should be used
  *  @param {boolean}
  */
DvtGraphCore.prototype.setAttachmentMode = function(bMode)
{
   this._attMode = bMode;
};
// Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*   DvtGraphData                                                      */
/*---------------------------------------------------------------------*/
/**
  *   @class DvtGraphData
  *   @constructor
  */
var  DvtGraphData = function()
{
   this._Init() ;
} ;

DvtObj.createSubclass(DvtGraphData, DvtObj, "DvtGraphData");

//  Data types for getData()

DvtGraphData.DV_X   = 2 ;   //  X data   ) these also (and MUST) match
DvtGraphData.DV_Y   = 3 ;   //  Y data   } the corresponding macro
DvtGraphData.DV_Z   = 4 ;   //  Z data   ) ID's in DVTGraphParser.
DvtGraphData.DV_HV  = 11 ;  //  HV data
DvtGraphData.DV_LV  = 12 ;  //  LV data
DvtGraphData.DV_OV  = 13 ;  //  OV data
DvtGraphData.DV_CV  = 14 ;  //  CV data
DvtGraphData.DV_VV  = 15 ;  //  VV data
DvtGraphData.DV_AV  = 17 ;  //  AV data
DvtGraphData.DV_TV  = 18 ;  //  TV data
DvtGraphData.DV_AUV = 27;   //  AUV data
DvtGraphData.DV_TUV = 28;   //  TUV data




/** @private */
DvtGraphData.prototype._Init = function()
{
   //  Metadata   (the following arrays are all indexed by
   //              _iSeriesIndex & _iGroupIndex in objects

   this._arSeries             = null ;    // series ID's
   this._arLayers             = null ;    // layer ID's
   this._arGroups             = null ;    // group ID's
   this._arSeriesLabels       = null ;    // series labels
   this._arGroupLabels        = null ;    // group labels
   this._arO1GroupLabels      = null ;    // O1 group labels
   this._arSeriesColors       = null ;    // default series colors (String array)
   this._arXLValues           = null ;    // alternative [X] formatted macro strings
   this._arYLValues           = null ;    // alternative [Y] formatted macro strings
   this._arZLValues           = null ;    // alternative [Z] formatted macro strings

   this._iSeriesCount         = 0 ;       // # of data series
   this._iGroupCount          = 0 ;       // # of data groups

   //  Tooltip template related 

   this._arTooltipTemplate    = null ;
   this. _iMacroCount         = 0 ;       // count of macros in the template
   this._arMacroIDs           = null ;    // array of macro indexes in order
                                          // of appearance in the template
   this._arValueFormats       = null ;    // value format rules (in c_arMacro order)
   this._iMacroSupportedCount = 0 ;       // count of all macros supported
   this._arData               = null ;    // Array of arrays. The entries are in
                                          // macro ID order. Each entry is a sub-
                                          // array and is a list of formatted data
                                          // values. The data array for each value
                                          // type is s1g1, s1g2, s1g3, . . . s2g1,
                                          //  ... However, currently getMacroValues()
                                          // should be used since this format will
                                          // change!
   this._arDataTypes          = null ;    // array of booleans. true means the
                                          // corresponding data array are floats,
                                          // false means they are formatted strings.
   this._arMacroVals          = null ;    // returned by getValues(). Array of
                                          // macro values for subtitution into
                                          // the tooltip template
   this._arGroupSum           = null ;    // Group value sums (indexed by value type DV_X, DV_Y, DV_Z)

   this._XAxisValueFormat ;               // range slider value format
   this._YAxisValueFormat ;               // range slider value format
   this._Y2AxisValueFormat ;              // range slider value format

} ;


/**
  *  Returns an array of group strings.
  *  @returns {Array}  an array of group strings.
 */
DvtGraphData.prototype.getGroups  = function()
{
   return this._arGroups ;
} ;

DvtGraphData.prototype.getGroupCount = function()
{
   return  (this._arGroups !== null ? this._arGroups.length : 0) ;
} ;



/*---------------------------------------------------------------------*/
/*   getGroupFromIndex()    Returns the group string for the specified */
/*                          group index.                               */
/*---------------------------------------------------------------------*/
/**
  *  Returns the group string for the specified group index.
  *  @type String
  */
DvtGraphData.prototype.getGroupFromIndex = function(iGroupIndex)
{
   var  sRet = null ;
   
   if (this._arGroups !== null && iGroupIndex >= 0)  {
      if (iGroupIndex < this._arGroups.length) {
         sRet = this._arGroups[iGroupIndex] ;
      }
   }

   return sRet ;
} ;

/**
  *  Returns an array of group labels, or null if none defined.
  *  @returns {Array}  an array of group labels, or null if none defined.
  */
DvtGraphData.prototype.getGroupLabels  = function()
{
   return this._arGroupLabels ;
} ;

/**
  *  Returns an array of O1 group labels, or null if none defined.
  *  @returns {Array}  an array of O1 group labels, or null if none defined.
  */
DvtGraphData.prototype.getO1GroupLabels  = function()
{
   return this._arO1GroupLabels ;
} ;


/*---------------------------------------------------------------------*/
/*   getSeries()                 Get series array                      */
/*---------------------------------------------------------------------*/
/**
  *  Returns an array of series strings in series index order.  Do NOT modify.
  *  @return {Array} the series Id's in series index order.
  *  @type Array
  */
DvtGraphData.prototype.getSeries = function()
{
   return this._arSeries ;
} ;



/*---------------------------------------------------------------------*/
/*    getSeriesColor()           Get the series solid color            */
/*                                                                     */
/*                               (see also isSeriesColor()             */
/*---------------------------------------------------------------------*/
/**
  *  Returns the series color for a specified series index.
  *  @returns {String} the series color for the specified series index.
  */
DvtGraphData.prototype.getSeriesColor = function(iSeriesIndex)
{
  var  seriesColor = null ;

  if (iSeriesIndex !== undefined && iSeriesIndex !== null    &&
         this._arSeriesColors !== null && iSeriesIndex >= 0  &&
         iSeriesIndex < this._arSeriesColors.length) {
    seriesColor = this._arSeriesColors[iSeriesIndex] ;
  }
  else  {
    seriesColor = '#000000' ;  // default is black  (should this just return null?)
  }

  return seriesColor ;
} ;



/*----------------------------------------------------------------------*/
/*   getIndexFromGroup()      Returns the position (relative to zero)   */
/*                            of the specified group in the groups list */
/*                           (for ADS and polylines only).              */
/*----------------------------------------------------------------------*/
/*   Returns -1 if not found, or a zero based index.                    */
/*----------------------------------------------------------------------*/
/**
  *  Returns the group index (relative to zero) for the specifed group
  *  string, or -1 if not found. The index is the position of the specified
  *  group in the groups list (for ADS and polylines only).
  *  @type number
  */
DvtGraphData.prototype.getIndexFromGroup = function(sGroup)
{
   var  iRet = -1 ;

   if (this._arGroups !== null) {
      var  len = this._arGroups.length ;

      for (var i = 0; i < len; i++) {
         if (this._arGroups[i] === sGroup) {
           iRet = i ;
           break ;
         }
      }
   }

   return iRet ;
} ;



/*----------------------------------------------------------------------*/
/*   getIndexFromSeries()                                               */
/*----------------------------------------------------------------------*/
/**
  *  Returns the series index (relative to zero) for the specifed series
  *  string, or -1 if not found.
  *  @type number
  */
DvtGraphData.prototype.getIndexFromSeries = function(series)
{
   var  iRet = -1 ;

   if (this._arSeries !== null)
   {
      var  len = this._arSeries.length ;

      for (var i = 0; i < len; i++) {
         if (this._arSeries[i] === series)
         {
           iRet = i ;
           break ;
         }
      }
   }

   return iRet ;
} ;



/*---------------------------------------------------------------------*/
/*   getMacroValues()                                                  */
/*---------------------------------------------------------------------*/
/**
  *   Returns an array of tooltip macro replacement value strings for the
  *   specified object. The array is ordered in the sequence of the macros
  *   found in the tooltip template.
  *   @type Array
  */
DvtGraphData.prototype.getMacroValues = function(iSeriesIndex, iGroupIndex)
{
   //  Strategy: populate the this._arMacroVals array with values that are
   //  required to replace the macros in the order they were encountered
   //  in parsing the tooltip template.

   var  i ;

   if (this._arData !== null) {
      var  iMacroID ;               // ID of macro (index into this._arData)
      var  ar ;
      var  sValue ;
      var  nValue ;

      var sSeriesLabel = '' ;
      var sGroupLabel  = '' ;

      for (i = 0; i < this._iMacroCount;i++) {
         iMacroID = this._arMacroIDs[i] ;

         if (iMacroID === 0) {                 // [S]
           if (sSeriesLabel.length === 0) {    // do we already have it?
                                               // no
              //  Series label take precedence over series value.

              if (iSeriesIndex >= 0)  {
                if (this._arSeriesLabels !== null) {
                  sSeriesLabel = this._arSeriesLabels[iSeriesIndex] ;
                }
                else if (this._arSeries !== null) {
                  sSeriesLabel = this._arSeries[iSeriesIndex] ;
                }
              }
           }
           sValue = sSeriesLabel ;
         }
         else if (iMacroID == 1) {             // [G]

            if (sGroupLabel.length  === 0) {   // do we already have it?
                                               // no
              //  Group label take precedence over group value.

              if (iGroupIndex >= 0) {
                if (this._arGroupLabels !== null) {
                  sGroupLabel = this._arGroupLabels[iGroupIndex] ;
                }
                else if (this._arGroups !== null) {
                  sGroupLabel = this._arGroups[iGroupIndex] ;
                }
              }
            }
            sValue = sGroupLabel ;
         }
         else  {
           //  Get a data value.  The data array for each value type is
           //
           //  S1G1, S1G2, S1G3, S1G4 ... 

           var  arAltValue = null ;
           
           sValue = '' ;

           ar = this._arData[iMacroID] ;   // access the appropriate X=, Y=, Z= data array

           // Use this index to find data in the arrays

           var index     = (iSeriesIndex * this._iGroupCount) + iGroupIndex ; 
           if (ar) { //Must check for both undefined and null
             //console.log("iSeriesIndex="  + iSeriesIndex    + " iSeriesCount=" +
             //            this._iSeriesCount + " iGroupIndex=" + iGroupIndex +
             //            " iGroupCount=" + this._arGroups.length + " iMacroID=" +
             //            iMacroID + " ar=" + ar) ;

             //  If data type is a float, format according to the ValueFormat.
             //  If it is a string, it has already been formatted by the middle-tier.

             if (this._arDataTypes[iMacroID]) {           // is it a X= Y= Z= float?  
                                                          // yes.
               var vf = this._arValueFormats[iMacroID] ;

               if (vf.isDefault()) {                      // was a corresponding VF specified?
                                                          // no - special case - will use the
                 if (iMacroID === DvtGraphData.DV_X) {    // alternate XL=, YL=, or ZL= values.

                   if (this._arXLValues !== null) { 
                     sValue = this._arXLValues[index] ;
                   }
                 }
                 else if (iMacroID === DvtGraphData.DV_Y)  {
                   if (this._arYLValues !== null) {
                     sValue = this._arYLValues[index] ;
                   }
                 }
                 else if (iMacroID === DvtGraphData.DV_Z) {
                   if (this._arZLValues !== null) {
                     sValue = this._arZLValues[index] ;
                   }
                 }
                 else  {                                 // use real data value and
                   nValue = ar[index] ;                  // format it with default format.
                   sValue = vf.getFormattedValue(nValue) ;
                 }
               }
               else {                                    // VF was specified, format
                 nValue = ar[index] ;                    // the real data value.
                 sValue = vf.getFormattedValue(nValue) ;
               }
             }
             else {   // not a float, its a middle-tier formatted string
               sValue = ar[(iSeriesIndex * this._iGroupCount) + iGroupIndex] ;
             }
           }
           else  {
             if (iMacroID == DvtGraphData.DV_X) {        // alternate XL=, YL=, or ZL= values.
             
               if (this._arXLValues !== null) { 
                 sValue = this._arXLValues[index] ;
               }
             }
             else if (iMacroID == DvtGraphData.DV_Y) {
               if (this._arYLValues !== null) {
                 sValue = this._arYLValues[index] ;
              }
             }
             else if (iMacroID == DvtGraphData.DV_Z) {
               if (this._arZLValues !== null) {
                 sValue = this._arZLValues[index] ;
               }
             }
           }

         }
         if (sValue === null) {
           sValue = '' ;
         }
         this._arMacroVals[i] = sValue ;
      }

   }           // end if this._arData
   else  {
      //  Return an empty array

      for (i = 0; i < this._iMacroCount;i++) {
         this._arMacroVals[i] = '' ;
      }
   }

   //dbg("getMacroValues() = " + m_arMacroVals) ;

   return this._arMacroVals ;
} ;



/*---------------------------------------------------------------------*/
/*    getSeriesCount()                                                 */ 
/*---------------------------------------------------------------------*/
/**
  *  Returns the number of data series.
  *  @type number
  */
DvtGraphData.prototype.getSeriesCount = function()
{
   return  (this._arSeries !== null ? this._arSeries.length : 0) ;
}  ;



/*---------------------------------------------------------------------*/
/*   getSeriesFromIndex()                                              */
/*---------------------------------------------------------------------*/
/**
  *  Returns the series string for the specified series index.
  *  @type String
  */
DvtGraphData.prototype.getSeriesFromIndex = function(iSeriesIndex)
{
   var  sRet = null ;
   
   if (this._arSeries !== null && iSeriesIndex >= 0) {
      if (iSeriesIndex < this._arSeries.length)  {
         sRet = this._arSeries[iSeriesIndex] ;
      }
   }

   return sRet ;
} ;


/*---------------------------------------------------------------------*/              
/*   getFormattedValue()                                               */
/*---------------------------------------------------------------------*/              
/**
  *  Returns a formatted value for specified type (x, or y, or z), for the
  *  specified series and group indices.
  *  @param {number}  type The data type (e.g. )
  *  @param {number}  sidx  The series index.
  *  @param {number}  gidx  The group index.
  *  @type String
  */
DvtGraphData.prototype.getFormattedValue = function(type, sidx, gidx) 
{
    var  value  = this.getValue(type, sidx, gidx);
    var vf = this._arValueFormats[type];
    return vf.getFormattedValue(value)
}


/*---------------------------------------------------------------------*/              
/*   getValue()                                                        */
/*---------------------------------------------------------------------*/              
/**
  *  Returns a value for specified type (x, or y, or z), for the
  *  specified series and group indices.
  *  @param {number}  type The data type (e.g. )
  *  @param {number}  sidx  The series index.
  *  @param {number}  gidx  The group index.
  *  @type number
  */
DvtGraphData.prototype.getValue = function(type, sidx, gidx)
{
  var n  ;

  if ((type >= DvtGraphData.DV_X  &&  type <= DvtGraphData.DV_Z) ||
      (type >= DvtGraphData.DV_HV &&  type <= DvtGraphData.DV_TV)||
      (type >= DvtGraphData.DV_AUV &&  type <= DvtGraphData.DV_TUV))
  {
     var  arData = this._arData[type] ;    // access the appropriate data array

     if (arData) {
       //  Ensure that we have real data and not middle-tier formatted data strings

       if (this._arDataTypes[type]) {
         n = arData[(sidx * this._iGroupCount) + gidx] ;
       }
       else  {
         var s = arData[(sidx * this._iGroupCount) + gidx] ;
         n = parseFloat(s) ;
       }
     }
  }

  return n ;

};


/*---------------------------------------------------------------------*/
/*  hasTooltipTemplate()                                               */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if a tooltip template is available with at least one entry.
  *  @type Boolean
  */
DvtGraphData.prototype.hasTooltipTemplate  = function()
{
   return ( (this._arTooltipTemplate !== null) && (this._arTooltipTemplate.length > 0) );
} ;


/*---------------------------------------------------------------------*/
/*    isSeriesColor()                                                  */
/*---------------------------------------------------------------------*/
/**
  *  Returns true if there is a series color available for the specified series index.
  *  See also {@link DvtGraphData#getSeriesColor}.
  */
DvtGraphData.prototype.isSeriesColor = function(iSeriesIndex)
{
  return  ((this._arSeriesColors !== null) && (iSeriesIndex >= 0) && 
           (iSeriesIndex < this._arSeriesColors.length)) ;
} ;



/*---------------------------------------------------------------------*/
/*      makeTooltipFromTemplate()                                      */
/*---------------------------------------------------------------------*/
/**
 * Returns a tooltip created from the chart's tooltip template.  Renderable
 * objects should call DvtGraphCore.makeTooltip() instead, as that accounts for
 * non-template tooltips as well.
 * @type string
 */ 
DvtGraphData.prototype.makeTooltipFromTemplate = function(sidx, gidx)
{
   var  arMacroValues  = this.getMacroValues(sidx, gidx);
   var  len            = this._arTooltipTemplate.length ;

   var s = '' ;

   if (len > 0)
   {
     //console.log("Data 1 len=" + len + "  this._arTooltipTemplate= " + this._arTooltipTemplate +
     //            "  arMacroVals=" + arMacroValues) ;

     //  Macro replacement
  
     var  i ;
     var  iDVLen ;
  
     if (arMacroValues !== null) {
       iDVLen = arMacroValues.length ; 
     }
  
     var temp ;

     for (i = 0; i < len; i++) {
        s += this._arTooltipTemplate[i] ;
  
        if (i < iDVLen) {
          temp = arMacroValues[i] ;
          if (temp.length > 0) {       // ignore empty macro vals.
            s += temp ;
          }
        }
     }
   }     // end if there is a tooltip template
  
   //  With the latest templates now being received in which constants
   //  can now appear as part of a macro data value, it is now possible for
   //  empty lines to be generated, so these must be removed.  
  
   s = s.replace(DvtGraphParser.NEW_LINE + DvtGraphParser.NEW_LINE, DvtGraphParser.NEW_LINE); // replace 2 or more newlines with 1 newline
   if(s.indexOf(DvtGraphParser.NEW_LINE) == 0)      // finally if there is a a newline
     s = s.substring(2) ;                           // at the very start, remove it.


     
//alert("Data.makeTooltipFromTemplate() returned " + s)

   return s;
} ;


/*---------------------------------------------------------------------*/
/*   setAltLValues()                                                   */
/*---------------------------------------------------------------------*/
/**
  *  Sets the alternate tooltip template macro [X], [Y], and [Z] replacement values.
  */
DvtGraphData.prototype.setAltLValues = function(macroID, ar)
{
   if (macroID === DvtGraphData.DV_X) {      // same as DVTGraphParser._iXIndex
      this._arXLValues = ar ;
   }
   else if (macroID === DvtGraphData.DV_Y) { // same as DVTGraphParser._iYIndex
      this._arYLValues = ar ;
   }
   else if (macroID === DvtGraphData.DV_Z) { // same as DVTGraphParser._iZIndex
      this._arZLValues = ar ;
   }
} ;



/*---------------------------------------------------------------------*/
/*   setDataValues()                                                   */
/*---------------------------------------------------------------------*/
/**
  *   Setter method for the formatted data values for a macro index.
  */
DvtGraphData.prototype.setDataValues = function(idx, ar, bNumbers)
{
    if(this._arData==null)
      this._arData=[];
    if(this._arDataTypes==null)
      this._arDataTypes=[];
    this._arData[idx]       = ar ;
    this._arDataTypes[idx]  = bNumbers ;
} ;



/*---------------------------------------------------------------------*/
/*   setGroups()                                                       */
/*---------------------------------------------------------------------*/
/**
  *   Setter method for the group values.
  */
DvtGraphData.prototype.setGroups = function(ar)
{
    this._arGroups = ar ;
    if (ar !== null) {
      this._iGroupCount = ar.length ;
    }
} ;


/*---------------------------------------------------------------------*/
/*  setGroupLabels()                                                   */
/*---------------------------------------------------------------------*/
/**
  *   Setter function for the group labels.
  */
DvtGraphData.prototype.setGroupLabels = function(ar)
{
  this._arGroupLabels = ar ;
} ;


/*---------------------------------------------------------------------*/
/*  setO1GroupLabels()                                                   */
/*---------------------------------------------------------------------*/
/**
  *   Setter function for the O1 group labels.
  */
DvtGraphData.prototype.setO1GroupLabels = function(ar)
{
  this._arO1GroupLabels = ar ;
} ;


/*---------------------------------------------------------------------*/
/*   get/setLayers()                                                   */
/*---------------------------------------------------------------------*/
/**
  *  Returns the layers array.
  *  @returns {Array} the layers array.  The array is indexed by miscId.
  *  Each entry is an array of series for that layer.
  */ 
DvtGraphData.prototype.getLayers = function()
{
   return  this._arLayers ;
};


DvtGraphData.prototype.setLayers = function(ar)
{
   if (ar) {
     var arL = [] ;
     var len = ar
     var i ;
     for (var i = 0; i < ar.length; i++)  {
        arL.push(ar[i].split(',')) ;
     }
     this._arLayers = arL ;
   }
};


/*---------------------------------------------------------------------*/
/*  setMacrosSupported()                                               */
/*---------------------------------------------------------------------*/
/**
  *  If a tooltip template has not been set, this is an alternative way
  *  to set the number of  macros supported.
  */
DvtGraphData.prototype.setMacrosSupported = function(iMacrosSupported)
{
   this._iMacroSupportedCount = iMacrosSupported ;  // count of all macros supported
   this._arData               = [] ;
   this._arDataTypes          = [] ;
} ;


/*---------------------------------------------------------------------*/
/*   setSeries()                                                       */
/*---------------------------------------------------------------------*/
/**
  *  Setter function for the series values (not data values).
  */
DvtGraphData.prototype.setSeries = function(ar)
{
   this._arSeries = ar ;
   if (ar !== null) {
      this._iSeriesCount = ar.length ;
   }
} ;


/*---------------------------------------------------------------------*/
/*  setSeriesColors()                                                  */
/*---------------------------------------------------------------------*/
/**
  *  Sets the series color array.
  *  @param {Array}  ar  an array of String objects in series index order.
  */

DvtGraphData.prototype.setSeriesColors = function(ar)
{
  this._arSeriesColors = ar ;
} ;


/*---------------------------------------------------------------------*/
/*  get/setSeriesLabels()                                              */
/*---------------------------------------------------------------------*/
/**
 *    Returns the series labels. Do NOT modify the returned Array.
 *    @returns {Array}  the series labels in series index order.
 */
DvtGraphData.prototype.getSeriesLabels = function()
{
  return this._arSeriesLabels ;
} ;


/**
 *    Sets the series labels.
 */
DvtGraphData.prototype.setSeriesLabels = function(ar)
{
  this._arSeriesLabels = ar ;
} ;


/*---------------------------------------------------------------------*/
/*  setTooltipTemplate()                                               */
/*---------------------------------------------------------------------*/
/**
  *  Sets the tooltip template data and supporting macro info.
  */
DvtGraphData.prototype.setTooltipTemplate = function(arTemplate, arMacroIDs, iMacrosSupported)
{
   //console.log("Data.setTooltipTemplate   " + arTemplate + "    " + arMacroIDs + "    " + iMacrosSupported) ;

   this._arTooltipTemplate    = arTemplate ;        // components of the template
   this._arMacroIDs           = arMacroIDs ;        // array of macro indexes in order
                                                    // of appearance in the template
   this._iMacroSupportedCount = iMacrosSupported ;  // count of all macros supported

   if (arMacroIDs !== null) {
     this._iMacroCount = arMacroIDs.length ;   // # of macros in the template
   }

   //  Create an array with the same number of entries as the number of         //[[[[[[ revisit
   //  macros currently supported. Unused entries will be null.

   if (iMacrosSupported > 0)  {
     this._arData       = [] ;
     this._arDataTypes  = [] ;

     if (this._iMacroCount > 0) {
       this._arMacroVals = [] ;
     }
   }

   //dbg("new Data: m_iMacroCount = " + m_iMacroCount +
   //    "  m_iSeriesCount=" + m_iSeriesCount +
   //    "  m_arMacroIDs=" + m_arMacroIDs) ;


} ;


/*---------------------------------------------------------------------*/
/*   setValueFormat()                                                  */ 
/*---------------------------------------------------------------------*/
/**
  *   Setter for a ValueFormat rule block.
  */
DvtGraphData.prototype.setValueFormat = function(idx, vf)
{
   if (this._arValueFormats === null) {
     this._arValueFormats = [] ;
   }

   this._arValueFormats[idx] = vf ;
} ;



/*---------------------------------------------------------------------*/
/*   get/setXAVF(), get/setYAVF(), get/setY2AVF().                     */
/*---------------------------------------------------------------------*/

/**
 *   Gets the x-axis (scrollbar) ValueFormat.
 */
DvtGraphData.prototype.getXAVF = function()
{
   return this._XAxisValueFormat ;
} ;

/**
 *   Sets the x-axis (scrollbar) ValueFormat.
 */
DvtGraphData.prototype.setXAVF = function(vf)
{
   this._XAxisValueFormat  = vf ;
} ;   

/**
 *   Gets the y-axis (scrollbar) ValueFormat.
 */
DvtGraphData.prototype.getYAVF = function()
{
   return this._YAxisValueFormat ;
} ;

/**
  *   Sets the y-axis (scrollbar) ValueFormat.
  */
DvtGraphData.prototype.setYAVF = function(vf)
{
   this._YAxisValueFormat = vf ;
} ;

/**
 *   Gets the y2-axis (scrollbar) ValueFormat.
 */
DvtGraphData.prototype.getY2AVF = function()
{
   return this._Y2AxisValueFormat ;
} ;

/**
  *   Sets the y2-axis (scrollbar) ValueFormat.
  */
DvtGraphData.prototype.setY2AVF = function(vf)
{
   this._Y2AxisValueFormat = vf ;
} ;
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. 
// All rights reserved. 
/*--------------------------------------------------------------------------*/
/*   DvtGraphObjType              Graph/Gauge object Id's                   */
/*--------------------------------------------------------------------------*/
/*  History:                                                                */
/*  --------                                                                */
/*--------------------------------------------------------------------------*/
/**
  *  Defines graph/gauge object types.
  *  @class DvtGraphObjType
  *  @constructor
  */
var  DvtGraphObjType = function(){};

DvtObj.createSubclass(DvtGraphObjType, DvtObj, "DvtGraphObjType");

// NOTE: Changes to this class require sync'd changes to middle tier ExportXML
//       classes and test framework (since objIDs are addressed directly).

/**   @final @type String  */
DvtGraphObjType.AREA_RISER               = "AR" ;
/**   @final @type String  */
DvtGraphObjType.AREA_RISER_BOTTOM        = "ARB" ;
/**   @final @type String  */
DvtGraphObjType.AREA_RISER_TOP           = "ART" ;
/**   @final @type String  */
DvtGraphObjType.BAR_RISER                = "BR" ;
/**   @final @type String  */
DvtGraphObjType.BAR3D_RISER              = "BR3D" ;
/**   @final @type String  */
DvtGraphObjType.BAR_RISER_SIDE           = "BRS" ;
/**   @final @type String  */
DvtGraphObjType.BAR_RISER_TOP            = "BRB" ;
/**   @final @type String  */
DvtGraphObjType.BOTTOM_LABEL             = "BottomLabel" ;
/**   @final @type String  */
DvtGraphObjType.BOX_BAR                  = "BoxBar" ;
/**   @final @type String  */
DvtGraphObjType.BOX_LINE                 = "BoxLine" ;
/**   @final @type String  */
DvtGraphObjType.BOX_MARK                 = "BoxMark" ;
/**   @final @type String  */
DvtGraphObjType.CHARTBKGND               = "CB" ;
/**   @final @type String  */
DvtGraphObjType.CUBE_RISER_FACE          = "CRF" ;
/**   @final @type String  */
DvtGraphObjType.DATA_TEXT                = "DataText" ;
/**   @final @type String  */
DvtGraphObjType.FRAME                    = "F" ;
/**   @final @type String  */
DvtGraphObjType.FRAME_SIDE               = "FrameSide" ;
/**   @final @type String  */
DvtGraphObjType.FRAME_BOTTOM             = "FrameBottom" ;
/**   @final @type String  */
DvtGraphObjType.FRAME_SEPARATOR          = "FrameSeparator" ;
/**   @final @type String  */
DvtGraphObjType.FUNNEL_SLICE             = "FunnelSlice" ;
/**   @final @type String  */
DvtGraphObjType.GAUGE_BACKGROUND         = "GB" ;
/**   @final @type String  */
DvtGraphObjType.GAUGE_FRAME              = "GaugeFrame" ;
/**   @final @type String  */
DvtGraphObjType.GAUGESET_BKGND           = "GSB" ;
/**   @final @type String  */
DvtGraphObjType.INDICATOR                = "Indicator" ;
/**   @final @type String  */
DvtGraphObjType.INDICATOR_BASE           = "IndicatorBase" ;
/**   @final @type String  */
DvtGraphObjType.LEGEND_AREA              = "LA" ;
/**   @final @type String  */
DvtGraphObjType.LEGEND_MARKER            = "LeM" ;
/**   @final @type String  */
DvtGraphObjType.LEGEND_TEXT              = "LT" ;
/**   @final @type String  */
DvtGraphObjType.LEGEND_TITLE              = "LegendTitle" ;
/**   @final @type String  */
DvtGraphObjType.LINE_MARKER              = "LM" ;
/**   @final @type String  */
DvtGraphObjType.LINE_RISER               = "LR" ;
/**   @final @type String  */
DvtGraphObjType.LINE_RISER_SIDE          = "LRS" ;
/**   @final @type String  */
DvtGraphObjType.LINE_RISER_TOP           = "LRT" ;
/**   @final @type String  */
DvtGraphObjType.METRIC_LABEL             = "ML" ;
/**   @final @type String  */
DvtGraphObjType.O1_AXIS_LINE             = "O1AxisLine" ;
/**   @final @type String  */
DvtGraphObjType.O1_LABEL                 = "O1L" ;
/**   @final @type String  */
DvtGraphObjType.O1_MAJOR_GRID            = "O1MajorGrid" ;
/**   @final @type String  */
DvtGraphObjType.O1_MINOR_GRID            = "O1MinorGrid" ;
/**   @final @type String  */
DvtGraphObjType.O1_RANGE_SLIDER          = "O1RS" ;
/**   @final @type String  */
DvtGraphObjType.O1_TITLE                 = "O1Title" ;
/**   @final @type String  */
DvtGraphObjType.OTHER                    = "Other" ;
/**   @final @type String  */
DvtGraphObjType.PARETO_LINE_MARKER       = "ParetoLineMarker" ;
/**   @final @type String  */
DvtGraphObjType.PARETO_LINE_RISER        = "ParetoLineRiser" ;
/**   @final @type String  */
DvtGraphObjType.PIE_BAR_RISER            = "PieBarRiser" ;
/**   @final @type String  */
DvtGraphObjType.PIE_BAR_RISER_SIDE       = "PieBarRiserSide" ;
/**   @final @type String  */
DvtGraphObjType.PIE_BAR_RISER_TOP        = "PieBarRiserTop" ;
/**   @final @type String  */
DvtGraphObjType.PIE_BAR_SLICE            = "PieBarSlice" ;
/**   @final @type String  */
DvtGraphObjType.PIE_BAR_SLICE_CRUST      = "PieBarSliecCrust" ;   // <<< MT currently sends this!!!
/**   @final @type String  */
DvtGraphObjType.PIE_CHART                = "PieChart" ;
/**   @final @type String  */
DvtGraphObjType.PIE_FEELER               = "PFL" ;
/**   @final @type String  */
DvtGraphObjType.PIE_FRAME                = "PieFrame";
/**   @final @type String  */
DvtGraphObjType.PIE_LABEL                = "PieLabel" ;
/**   @final @type String  */
DvtGraphObjType.PIE_SLICE                = "PieSlice" ;
/**   @final @type String  */
DvtGraphObjType.PIE_SLICE_LABEL          = "PSL";
/**   @final @type String  */
DvtGraphObjType.PLOT_AREA                = "PA" ;
/**   @final @type String  */
DvtGraphObjType.REFOBJ_BACK              = "ROB" ;
/**   @final @type String  */
DvtGraphObjType.REFOBJ_FRONT             = "ROF" ;
/**   @final @type String  */
DvtGraphObjType.RANGE_SLIDER_ZOOM_BUTTON = "rszb" ;
/**   @final @type String  */
DvtGraphObjType.QUADRANT_LINE            = "QL" ;
/**   @final @type String  */
DvtGraphObjType.SCROLLABLE_LEGEND        = "ScrollableLegend" ;
/**   @final @type String  */
DvtGraphObjType.SLICE                    = "Sl" ;
/**   @final @type String  */
DvtGraphObjType.STOCK_CLOSE_TICK         = "SCT";//"StockCloseTick" ;
/**   @final @type String  */
DvtGraphObjType.SLICE_CRUST              = "SC" ;
/**   @final @type String  */
DvtGraphObjType.STOCK_FALLING_RISER      = "StockFallingRiser" ;
/**   @final @type String  */
DvtGraphObjType.STOCK_HIGH_LINE          = "StockHighLine" ;
/**   @final @type String  */
DvtGraphObjType.STOCK_LOW_LINE           = "StockLowLine" ;
/**   @final @type String  */
DvtGraphObjType.STOCK_OPEN_TICK          = "SOT";//"StockOpenTick" ;
/**   @final @type String  */
DvtGraphObjType.STOCK_RISER              = "SR";//"StockRiser" ;
/**   @final @type String  */
DvtGraphObjType.STOCK_RISING_RISER       = "StockRisingRiser" ;
/**   @final @type String  */
DvtGraphObjType.STOCK_VOLUME_RISER       = "StockVolumeRiser" ;
/**   @final @type String  */
DvtGraphObjType.SUBTITLE                 = "Subtitle" ;
/**   @final @type String  */
DvtGraphObjType.THRESHOLD                = "Threshold" ;
/**   @final @type String  */
DvtGraphObjType.TICK_LABEL               = "TickLabel" ;
/**   @final @type String  */
DvtGraphObjType.TICK_MARK                = "TickMark" ;
/**   @final @type String  */
DvtGraphObjType.TITLE                    = "Title" ;
/**   @final @type String  */
DvtGraphObjType.TIME_SELECTOR            = "TimeSelector" ;
/**   @final @type String  */
DvtGraphObjType.TOP_LABEL                = "TopLabel" ;
/**   @final @type String  */
DvtGraphObjType.X1_AXIS_LINE             = "X1AxisLine" ;
/**   @final @type String  */
DvtGraphObjType.X1_LABEL                 = "X1L" ;
/**   @final @type String  */
DvtGraphObjType.X1_MINOR_GRID            = "X1MinorGrid" ;
/**   @final @type String  */
DvtGraphObjType.X1_MAJOR_GRID            = "X1MG" ;
/**   @final @type String  */
DvtGraphObjType.X1_RANGE_SLIDER          = "X1RS" ;
/**   @final @type String  */
DvtGraphObjType.X1_TITLE                 = "X1Title" ;
/**   @final @type String  */
DvtGraphObjType.Y1_AXIS_LINE             = "Y1AxisLine" ;
/**   @final @type String  */
DvtGraphObjType.Y2_AXIS_LINE             = "Y2AxisLine" ;
/**   @final @type String  */
DvtGraphObjType.Y1_LABEL                 = "Y1L" ;
/**   @final @type String  */
DvtGraphObjType.Y2_LABEL                 = "Y2Label" ;
/**   @final @type String  */
DvtGraphObjType.Y1_MINOR_GRID            = "Y1MinorGrid" ;
/**   @final @type String  */
DvtGraphObjType.Y2_MINOR_GRID            = "Y2MinorGrid" ;
/**   @final @type String  */
DvtGraphObjType.Y1_MAJOR_GRID            = "Y1MG" ;
/**   @final @type String  */
DvtGraphObjType.Y2_MAJOR_GRID            = "Y2MajorGrid" ;
/**   @final @type String  */
DvtGraphObjType.Y1_RANGE_SLIDER          = "Y1RS" ;
/**   @final @type String  */
DvtGraphObjType.Y2_RANGE_SLIDER          = "Y2RS" ;
/**   @final @type String  */
DvtGraphObjType.Y1_TITLE                 = "Y1Title" ;
/**   @final @type String  */
DvtGraphObjType.Y2_TITLE                 = "Y2Title" ;
/**   @final @type String  */
DvtGraphObjType.ZOOM_WINDOW              = "ZoomWindow" ;
/**   @final @type String  */
DvtGraphObjType.AXIS                     = "Axis" ; // not a real obj type,
                                                    // used for substring comparisons

/**
 * Returns true if the specified object id is a legend item object.
 * @param {string} objId
 */
DvtGraphObjType.isLegendItem = function(objId) {
  return objId === DvtGraphObjType.LEGEND_TEXT || objId === DvtGraphObjType.LEGEND_MARKER;
}
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. 
// All rights reserved. 
/*--------------------------------------------------------------------------*/
/*   DvtGraphType                Chart type definitions                     */
/*--------------------------------------------------------------------------*/
/*  History:                                                                */
/*  --------                                                                */
/*--------------------------------------------------------------------------*/
/**
  *  Chart type definitions
  *  @class DvtGraphType
  *  @constructor  Chart type definitions
  */
var  DvtGraphType = function(){} ;

DvtObj.createSubclass(DvtGraphType, DvtObj, "DvtGraphType");

/**   @final @type String  */
DvtGraphType.AREA_VERT_ABS       = 'AREA_VERT_ABS' ; 
/**   @final @type String  */
DvtGraphType.AREA_VERT_PERCENT   = 'AREA_VERT_PERCENT' ; 
/**   @final @type String  */
DvtGraphType.AREA_VERT_STACK     = 'AREA_VERT_STACK' ; 
/**   @final @type String  */
DvtGraphType.BAR_HORIZ_CLUST     = 'BAR_HORIZ_CLUST' ; 
/**   @final @type String  */
DvtGraphType.BAR_HORIZ_STACK     = 'BAR_HORIZ_STACK' ; 
/**   @final @type String  */
DvtGraphType.BAR_HORIZ_STACK_2Y  = 'BAR_HORIZ_STACK_2Y' ; 
/**   @final @type String  */
DvtGraphType.BAR_LINE_AREA_COMBO = 'COMBINATION_VERT_ABS' ; 
/**   @final @type String  */
DvtGraphType.BAR_VERT_CLUST      = 'BAR_VERT_CLUST' ; 
/**   @final @type String  */
DvtGraphType.BAR_VERT_PERCENT    = 'BAR_VERT_PERCENT' ;
/**   @final @type String  */
DvtGraphType.BAR_VERT_STACK      = 'BAR_VERT_STACK' ; 
/**   @final @type String  */
DvtGraphType.BAR_VERT_STACK2Y    = 'BAR_VERT_STACK2Y' ; 
/**   @final @type String  */
DvtGraphType.BUBBLE              = 'BUBBLE' ;
/**   @final @type String  */
DvtGraphType.BUBBLE_2Y           = 'BUBBLE_2Y' ;
/**   @final @type String  */
DvtGraphType.DIAL                = 'DIAL' ;
/**   @final @type String  */
DvtGraphType.FUNNEL               = 'FUNNEL' ; 
/**   @final @type String  */
DvtGraphType.LED                 = 'LED' ; 
/**   @final @type String  */
DvtGraphType.LINE_SELECT_POINT   = 'LineSelectPoint' ;
/**   @final @type String  */
DvtGraphType.LINE_VERT_ABS       = 'LINE_VERT_ABS'  ; 
/**   @final @type String  */
DvtGraphType.LINE_VERT_CLUST     = 'LineVerticalCluster' ; 
/**   @final @type String  */
DvtGraphType.LINE_VERT_PERCENT   = 'LINE_VERT_PERCENT' ; 
/**   @final @type String  */
DvtGraphType.LINE_VERT_STACK     = 'LINE_VERT_STACK' ;
/**   @final @type String  */
DvtGraphType.LINE_VERT_STACK2Y   = 'LINE_VERT_STACK2Y' ;
/**   @final @type String  */
DvtGraphType.PIE                 = 'PIE' ; 
/**   @final @type String  */
DvtGraphType.POLAR               = 'POLAR' ; 
/**   @final @type String  */
DvtGraphType.RADAR_LINE            = 'RADAR_LINE' ;
/**   @final @type String  */
DvtGraphType.RING_BAR            = 'RING_BAR' ;
/**   @final @type String  */
DvtGraphType.RING                = 'RING' ;          // (for substring search)
/**   @final @type String  */
DvtGraphType.SCATTER             = 'SCATTER' ;   
/**   @final @type String  */
DvtGraphType.SCATTER_2Y          = 'SCATTER_2Y' ;   
/**   @final @type String  */
DvtGraphType.STATUSMETER         = 'STATUSMETER' ;
// Copyright (c) 2008, 2012, Oracle and/or its affiliates. 
// All rights reserved. 
/*--------------------------------------------------------------------*/
/*   DvtDataSummary           Chart data summary                      */
/*--------------------------------------------------------------------*/
/**
  *  Chart data summary.
  *  @class DvtDataSummary    Chart data summary.
  *  @constructor  
  *  @param {DvtGraphCore} chart  the owning chart.
  *  @returns {DvtDataSummary} A new DvtDataSummary.
  */
var   DvtDataSummary = function(chart)
{
   this.Init(chart) ;
};  

DvtObj.createSubclass(DvtDataSummary, DvtObj, "DvtDataSummary");

/*--------------------------------------------------------------------*/
/*   getChart()                                                       */
/*--------------------------------------------------------------------*/
/**
 *  Returns the associated chart for this data summary.
 *  @return {DvtGraphCore} the associated chart for this data summary.
 */
DvtDataSummary.prototype.getChart = function()
{
   return this._Chart ;
};


/*--------------------------------------------------------------------*/
/*   Init()                                                           */
/*--------------------------------------------------------------------*/
/**
 * Object initialization.
 * @param {DvtGraphCore} chart    The owning chart.
 */
DvtDataSummary.prototype.Init = function(chart)
{
   this._Chart = chart ;     // the owning chart

   // X1/Y1 axis min/max/baseline values and pixel values

   this.x1MinVal        =  0 ;   // min data value
   this.x1MaxVal        =  0 ;   // max data value

   this.y1MinVal        =  0 ;   // min data value
   this.y1MaxVal        =  0 ;   // max data value

   this.y1BaseVal       =  0 ;   // baseline data value
   this.y1Baseline      =  0 ;   // x-axis y pos pixel pos.
   this.y1BaselineFront =  0 ;   // front x-axis ypos baseline (2.5D)

   this.y2MinVal        =  0 ;   // min data value
   this.y2MaxVal        =  0 ;   // max data value

   this.y2BaseVal       =  0 ;   // baseline data value
   this.y2Baseline      =  0 ;   // x-axis y pos pixel pos.
   this.y2BaselineFront =  0 ;   // front x-axis ypos baseline (2.5D)

   this.xPxMin          =  0 ;   // x pixel min value
   this.xPxMax          =  0 ;   // x pixel max value
   this.xPxRatio        =  0 ;   // x value pixel ratio

   this.yPxMin          =  0 ;   // y pixel min value
   this.yPxRatio        =  0 ;   // y value pixel ratio

};
// Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*   DvtGraph                                                          */
/*---------------------------------------------------------------------*/
/**
  *  Public class that represents a chart.
  *  @class   DvtGraph
  *  @extends DvtContainer
  *  @constructor
  */
var  DvtGraph = function(context, id, callback, callbackObj)
{
   this._Init(context, id, callback, callbackObj) ;
};

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



/*--------------------------------------------------------------------*/
/*  getContext()                                                      */
/*--------------------------------------------------------------------*/
/**
  *   @returns {DvtContext}  The platform implementation context.
  */
DvtGraph.prototype.getCtx = function()
{
    return this._ctx ;
};

/**
  *   @returns {DvtGraphCore}  
  */
DvtGraph.prototype.getChart = function()
{
    return this._Chart ;
};

DvtGraph.prototype.setId = function(id) {
    this._id = id;
}

DvtGraph.prototype.getId = function() {
    if (this._id) 
      return this._id;
    else 
      return DvtGraph.superclass.getId.call(this);
}

/*--------------------------------------------------------------------*/
/*   _Init()                                                          */
/*--------------------------------------------------------------------*/
/**
  *   @private
  */
DvtGraph.prototype._Init = function(context, id, callback, callbackObj)
{
   if (! this.getImpl()) {
     this.setImpl(context.getImplFactory().newContainer()) ;
   }
   DvtGraph.superclass.Init.call(this, context) ;

   this._id         = id;
   this._ctx        = context ;
   this._callback   = callback;
   this._callbackObj= callbackObj;
   this._Chart      = null ;          // the active chart
   this._DCM        = null ;          // the data change manager.
} ;



/*---------------------------------------------------------------------*/
/*   getAutomation()                                                   */
/*---------------------------------------------------------------------*/
/**
  *  @returns {DvtGraphAutomation} the automation object DvtGraphAutomation.
  */
DvtGraph.prototype.getAutomation = function()
{
    return (this._Chart? this._Chart.getAutomation() : null) ;
};


/*---------------------------------------------------------------------*/
/*  getType()                                                          */
/*---------------------------------------------------------------------*/
/**
  *   Returns the chart type (see {@link DvtGraphType}).
  *   @type String
  */
DvtGraph.prototype.getType = function()
{
   return (this._Chart? this._Chart.getType() : null) ;
} ;


/*----------------------------------------------------------------------*/
/*   render()                                                           */
/*----------------------------------------------------------------------*/
/**
 *    Render of chart's det objects.
 */
DvtGraph.prototype.render = function()
{

   if (this._Chart) {
     this._Chart.render() ;
   }
} ;

DvtGraph.prototype.resetXML = function()
{
   this._bNoAnim = true;
   this.setXML(this._xml);
   this._bNoAnim = null;
} ;

/*---------------------------------------------------------------------*/
/*   setXML()       Parse the XML into an object list                  */
/*---------------------------------------------------------------------*/
/**
  *  Parses and renders the supplied graph/gauge XML, and performs intial
  *  display or datachange effects.
  *  @param {String} sXML  The graph XML.
  */
DvtGraph.prototype.setXML = function(sXML)
{
   if (! sXML) {
    return ;
   }
    
   //  Check for a prefix tag: <animate>, <ppr>, or <ads>.
    
   var  sPrefix = null ;
   var  i = sXML.indexOf('<root') ;     // find start of the main xml body (i.e the <root> tag
   
   if (i > 0) {
     sPrefix = sXML.substring(0, i) ;  // strip off the prefix part
     sPrefix = this._trim(sPrefix) ;
     if (sPrefix.length === 0) {
       sPrefix = null ;
     }
     sXML = sXML.substr(i) ;           // and remove from main xml body
     sXML = this._trim(sXML) ;
   }
   else if (i !== 0) {                  // have a tag of some sort
     sPrefix = sXML ;                  // data update only (no xml)
     sXML = null ;
   }
   
   if ((! sPrefix) && (! sXML)) {
    return ;
   }
    
    //  At this point sXML contains the xml starting at the <root> tag, and 
    //  sPrefix contains a possible <animate> or <ads> or <ppr> string.
    
    
    // Determine the prefix tag type, and create an animation detail object..
    
    var  anim = new DvtGraphAnimation() ;   // get default "no animation" object
    var  bAdi = false ;
    
    if (this._bNoAnim != true){
      if (sPrefix && this._Chart) {           // existing chart?
        if (sPrefix.indexOf("<animate") != -1) { 
          bAdi = true ;
          // BUG #12568190: animate tag is seen as a ppr
          anim.animOnDataChange = DvtGraphAnimation.PPR ;
        }
        else if (sPrefix.indexOf('ppr') > 0) {
          anim.animOnDataChange = DvtGraphAnimation.PPR ;
        }
        else if (sPrefix.indexOf('ads') > 0) {
          bAdi = true ;
          anim.animOnDataChange = DvtGraphAnimation.ADS ;
        }
      }
      else {                                               // no existing chart.
        anim.animOnDisplay = DvtGraphAnimation.DISPLAY ;  // allow possible initfx 
                                                         // if xml requests it.
      }
    }
    
    //  Process the xml into a chart object
    
    var chart =  new DvtGraphCore(this._ctx, this.getId(), this._callback, this._callbackObj) ;
    
    chart.setXML(sXML, anim) ;                          // parse the xml into the chart
    this._xml = sXML;
    
    // if the chart has keyboard focus and axes zooming is enabled, show the zoom button
    // this call is needed here to make sure the zoom button is shown after graph is zoomed in
    var zoomButton = chart.getZoomButton();
    if(zoomButton && this.isFocused())
    {
      zoomButton.show();
      zoomButton.drawOverState();
    }   
    
    //  Final check of animation request
    
    if (this._Chart && (! bAdi)) {                      // if existing chart and no <animate>
     anim.animOnDisplay = DvtGraphAnimation.NONE ;     // veto any initial effects.
    } 
    if (this._bNoAnim ||  ((anim.animOnDataChange !== DvtGraphAnimation.NONE)  &&
       (anim.animOnDataChange !== DvtGraphAnimation.AUTO)))  {      // blackbox anim required?
    
       if (! DvtBlackBoxAnimationHandler.isSupported(anim.animOnDataChange)) {
         anim.animOnDataChange = DvtGraphAnimation.NONE ; 
       }
    }
    
    //  Can now render or animate new chart
    
    if (anim && (anim.animOnDataChange === DvtGraphAnimation.NONE)) {
     if (this._Chart) {                  // if have old chart, and no
       this.removeChild(this._Chart) ;   // data change, can remove old chart.
       this._Chart.destroy();
       this._Chart = null ;
     }
    }
    
    //  If no animation to do, just do straight render.
    
    if (this._bNoAnim || (anim.animOnDisplay    === DvtGraphAnimation.NONE &&
       anim.animOnDataChange === DvtGraphAnimation.NONE)) {
     this.addChild(chart) ;
     this._Chart = chart ;
     chart.render() ;
     return ;
    }
    
    //  Need to animate for initial display or data change.
    
    var bAnimate = true ;
    
    if (anim.animOnDisplay !== DvtGraphAnimation.NONE) {
    
      if (anim.animOnDisplay === DvtGraphAnimation.AUTO) {
        bAnimate = false ;           // just render the chart, it will do initFx.
      }
      else {
        this._startBlackboxAnimation(anim, null, chart) ;
      }
    }
    else if (anim.animOnDataChange !== DvtGraphAnimation.NONE) {
      if (anim.animOnDataChange === DvtGraphAnimation.AUTO) {
        bAnimate = this._startDataChangeAnimation(chart, anim) ;
      }
      else {
        this._startBlackboxAnimation(anim, this._Chart, chart) ;
      }
    }
    
    //  If no action taken, just do straight render.
    
    if (! bAnimate) {
     // cleanup any existing charts
     if(this._Chart){
         this.removeChild(this._Chart);
         this._Chart.destroy();
     }
     this.addChild(chart) ;
     this._Chart = chart ;
     chart.render() ;
  }

} ;

// Sets the zoom mode
DvtGraph.prototype.setMode = function(mode) 
{
    if (this._Chart)
        this._Chart.setZoomMode(mode);
}

/**
 * Update the visual effects on view when it receives or loses keyboard focus 
 * 
 * @param {boolean} isFocused
 */
DvtGraph.prototype.setFocused = function(isFocused)
{
  this._hasFocus = isFocused;
  
  // Check for chart before asking for zoom button.  this._Chart is not created until after xml is set.
  var zoomButton = this._Chart ? this._Chart.getZoomButton() : null;
  if(zoomButton)
  {
    if(isFocused)
    {
      zoomButton.show();
      zoomButton.drawOverState();
    }
    else
    {
      zoomButton.hide();
    }
  }
}

/**
 * Returns true if the view currently has keyboard focus
 */
DvtGraph.prototype.isFocused = function()
{
  return this._hasFocus;
}


/*---------------------------------------------------------------------*/
/*  _startBlackBoxAnimation()                                          */
/*---------------------------------------------------------------------*/
/**
  *  Perform blackbox animation for initial display or data change.
  *  @param {DvtGraphAnimation} anim
  *  @param {DvtGraphCore} oldChart  The existing chart (if any).
  *  @param {DvtGraphCore} chart    The chart to animated in 
  *  @private
  */
DvtGraph.prototype._startBlackboxAnimation = function(anim, oldChart, newChart)
{

    if(this._blackbox){
      this._blackbox.stop();
      this._blackbox = null;
      if (this._Chart) {
        // remove the old chart. otherwise the old chart would freeze in the background 
        // and the new chart animate in front of it.
        this.removeChild(this._Chart) ;        
        this._Chart.destroy();

        this.addChild(oldChart) ;
        this._Chart = oldChart ;
      } 
    }
  
   var  bounds = new DvtRectangle(0, 0, newChart.getWidth(), newChart.getHeight()) ;
   var  ah ;
   var  dur = ((anim.animOnDisplay !== DvtGraphAnimation.NONE)? anim.displayDuration :
                                                                anim.dataChangeDuration)/1000 ;
   this._newChart = newChart ;

   this.addChild(newChart) ;
   newChart.render() ;

   if (! oldChart) {
     ah = DvtBlackBoxAnimationHandler.getInAnimation(this._ctx, anim.animOnDisplay,
                                                     newChart, bounds, dur) ;
   }
   else {
     ah = DvtBlackBoxAnimationHandler.getCombinedAnimation(this._ctx, anim.animOnDataChange,
                                                           oldChart, newChart, bounds, dur) ;
   }

   if (ah) {
     this._blackbox = ah;
     ah.setOnEnd(this._fnEndDataChange, this) ;
     ah.play() ;
   } else { // in case no blackbox animation is done
     this._Chart = newChart ;
     this._newChart = null ;
   }
};


DvtGraph.prototype._fnEndDataChange = function()
{
  if (this._Chart) {
    this.removeChild(this._Chart) ;        // remove the old chart
    this._Chart.destroy();
  }
  this._Chart    = this._newChart ;
  this._newChart = null ;
  this._blackbox = null;
};


/*---------------------------------------------------------------------*/
/*  _startDataChangeAnimation()                                        */
/*---------------------------------------------------------------------*/
/**
  *  Perform the animation of the old chart into the new chart.
  *  @returns {boolean}  returns true if animation started, or false if
  *  the DataChange manager elected to bale out on animation. 
  *  @private
  */
DvtGraph.prototype._startDataChangeAnimation = function(newChart, anim)
{
  if (this._DCM && this._DCM.isAnimating()) {
    this._DCM.stopAnimating() ;
  }

  this._DCM   = new DvtDataChangeManager(this) ;
  var bChange = this._DCM.handleDataChange(this._Chart, newChart, anim) ;

  if (bChange) {               // did we animate?
    this._Chart = newChart ;
  }

  return bChange ;             // returns true if animation started, or false
                               // if manager elected to bale out on animation. 
};


/**
 * Explode all pie slices. This method is called by AdfDhtmlDvtImageViewPeer to handle the explode all 
 * menu selections
 */
DvtGraph.prototype.__explodeAll = function()
{
  var pieChart = this._Chart.__getPieChart();
  if(pieChart != null)
  {
    pieChart.explodeAll();
  }
}

/**
 * Unite all pie slices. This method is called by AdfDhtmlDvtImageViewPeer to handle the unite all 
 * menu selections 
 */
DvtGraph.prototype.__uniteAll = function()
{
  var pieChart = this._Chart.__getPieChart();
  if(pieChart != null)
  {
    pieChart.uniteAll();
  }  
}

/**
 * returns true if the chart/pie has all its slices exploded
 */
DvtGraph.prototype.__isPieAllExploded = function() 
{
  var pieChart = this._Chart.__getPieChart();
  if (pieChart != null) 
  {
    return pieChart.isPieAllExploded();
  }
}

/**
 * returns true if the chart/pie has all its slices united (imploded, not exploded)
 */
DvtGraph.prototype.__isPieAllUnited = function() 
{
  var pieChart = this._Chart.__getPieChart();
  if (pieChart != null) 
  {
    return pieChart.isPieAllUnited();
  }
}


//  Temp TODO JRM

DvtGraph.prototype._trim = function (s) { 
  return s.replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1"); 
} ;
// Drag Source related APIs for DvtGraph
/**
 * If this object supports drag, returns the client id of the drag component.
 * Otherwise returns null.
 * @param mouseX the x coordinate of the mouse
 * @param mouseY the x coordinate of the mouse
 * @param clientIds the array of client ids of the valid drag components
 */
DvtGraph.prototype.isDragAvailable = function (mouseX, mouseY, clientIds) {
  return this._Chart.getDragSource().isDragAvailable(clientIds);
}

/**
 * Notifies the graph that a drag started.
 */
DvtGraph.prototype.initiateDrag = function () {
  this._Chart.getDragSource().initiateDrag();
}

/**
 * @export
 * Returns the transferable object for a drag initiated at these coordinates.
 */
DvtGraph.prototype.getDragTransferable = function (mouseX, mouseY) {
  return this._Chart.getDragSource().getDragTransferable(mouseX, mouseY);
}

/**
 * Returns an Object containing the drag context info.
 */
DvtGraph.prototype.getDragContext = function (mouseX, mouseY) {
  return this._Chart.getDragSource().getDragContext(mouseX, mouseY);
}

/**
 * @export
 * Returns the feedback for the drag operation.
 */
DvtGraph.prototype.getDragOverFeedback = function (mouseX, mouseY) {
  return this._Chart.getDragSource().getDragOverFeedback(mouseX, mouseY);
}

/**
 * Returns the offset to use for the drag feedback. This positions the drag
 * feedback relative to the pointer.
 */
DvtGraph.prototype.getDragOffset = function (mouseX, mouseY) {
  return this._Chart.getDragSource().getDragOffset(mouseX, mouseY);
}

/**
 * Returns the offset from the mouse pointer where the drag is considered to be located.
 */
DvtGraph.prototype.getPointerOffset = function (xOffset, yOffset) {
  return this._Chart.getDragSource().getPointerOffset(xOffset, yOffset);
}

/**
 * Clean up after the drag is completed.
 */
DvtGraph.prototype.dragDropEnd = function () {
  this._Chart.getDragSource().dragDropEnd();
}
// Drop Target related APIs for DvtGraph

/**
 * If a drop is possible at these mouse coordinates, returns the client id
 * of the drop component. Returns null if drop is not possible.
 */
DvtGraph.prototype.acceptDrag = function (mouseX, mouseY, clientIds) {
  return this._Chart.getDropTarget().acceptDrag(mouseX, mouseY, clientIds);
}

/**
 * Paints drop site feedback as a drag enters the drop site.
 */
DvtGraph.prototype.dragEnter = function () {
  this._Chart.getDropTarget().dragEnter();
}

/**
 * Cleans up drop site feedback as a drag exits the drop site.
 */
DvtGraph.prototype.dragExit = function () {
  this._Chart.getDropTarget().dragExit();
}

/**
 * Returns the object representing the drop site. This method is called when a valid
 * drop is performed.
 */
DvtGraph.prototype.getDropSite = function (mouseX, mouseY) {
  return this._Chart.getDropTarget().getDropSite(mouseX, mouseY);
}
/**
 * Subclass of DvtPieChart providing compatibility with the server layout graph.
 * @param {DvtGraphCore} chart The owning DvtGraphCore object.
 * @param {DvtContext} context The platform dependent context object (such as {@link DvtSvgContext}).
 * @param {function} callback A function that responds to events
 * @param {object} callbackObj The optional object instance that the callback function is defined on.
 * @class DvtLegacyPieChart
 * @extends DvtPieChart
 * @constructor
 */ 
var DvtLegacyPieChart = function(chart, context, callback, callbackObj)
{
  this.Init(context, chart.__getEventManager(), callback, callbackObj);
  this._chart = chart;
  this._pieSliceExplodeState = null; // Array of explode state changes
}; 

DvtObj.createSubclass(DvtLegacyPieChart, DvtPieChart, "DvtLegacyPieChart");

/**
 * Implementation of DvtDetObj.init()
 */
DvtLegacyPieChart.prototype.init = function()
{
  // We need to keep this stub since it will be called by the Chart during render.
};

/**
 * @override
 */
DvtLegacyPieChart.prototype.CreateSlice = function(pie, sliceInfo) {
  return new DvtLegacyPieSlice(pie, sliceInfo);
}

/**
 * @override
 */
DvtLegacyPieChart.prototype.__getShowPopupBehaviors = function() {
  return this._chart.__getShowPopupBehaviors();
} 

/**
 * @override 
 */
DvtLegacyPieChart.prototype.animateExplode = function(op, seriesIdx) 
{ 
  var slices = this.__getSlices();
  var length = this.getMaxSliceIndex() + 1;
  var slice;
  var sliceInfo;
  var prevExplodeCount = 0;
  var newExplodeCount = 0;
  var i;
  var j;
  
  // First update the pieSliceExplode state on the JS Component
  
  // Initialize the state array if needed
  if(this._pieSliceExplodeState == null) 
  {
    this._pieSliceExplodeState = new Array(length);
    for(i = 0; i < length; i++) 
    {
      this._pieSliceExplodeState[i] = 0;    
    }
    for(j = 0; j < slices.length; j++)
    {
      slice = slices[j];
      sliceInfo = slice.getSliceInfo();
      i = sliceInfo.getIndex();
      this._pieSliceExplodeState[i] = sliceInfo.getExplode() == 0 ? 0 : 100;
    }
  }
          
  // Count the current number of exploded slices, before this explode/unite action
  for(i=0; i<length; i++)
  {
    if(this._pieSliceExplodeState[i] != 0)
      prevExplodeCount++;
  }
          
  // Add the explode update
  var explodeVal = (op == DvtPieChart.EXPLODE ? 100 : 0);
  var sendPPR = false; // send a PPR event if this explode requires us to change 
                       // the explode all/unite all menu options shown 
  
  if(seriesIdx == -1) 
  {
    // All slices updated
    for(i = 0; i < length; i++)
      this._pieSliceExplodeState[i] = explodeVal;
  }
  else if(seriesIdx >= 0 && seriesIdx < length) 
  {
    i = slices[seriesIdx].getSliceInfo().getIndex();
    this._pieSliceExplodeState[i] = explodeVal;
  }

  // Count the current number of exploded slices, after this explode/unite action
  for(i=0; i<length; i++)
  {
    if(this._pieSliceExplodeState[i] != 0)
      newExplodeCount++;
  }
  
  // check if by this update requires us to also update
  // the explode all/unite all menu options shown.  we would need to do this if:
  // 1. no slices are exploded, and we are exploding a slice 
  // 2. all slices are exploded, and we are uniting one slice
  // 3. one slice is exploded, and we are uniting it (go from explode all and unite all to explode all)
  // 4. we unite all slices      
  // 5. all but one slice is exploded, and we are exploding that one slice (go from explode all and unite all to unite all)
  // 6. we explode all slices

  if( ( (prevExplodeCount == 0) && (newExplodeCount > 0) ) || // case 1
      ( (prevExplodeCount == length) && (newExplodeCount < length) ) || // case 2
      ( (prevExplodeCount != 0) && (newExplodeCount == 0) ) || // case 3 and 4
      ( (prevExplodeCount != length) && (newExplodeCount == length) )  // case 5 and 6
    ) 
  {
    sendPPR = true;
  }        
  
  // delegate to superclass to animate the explode
  DvtLegacyPieChart.superclass.animateExplode.call(this, op, seriesIdx);
  
  // Update JS with the new state
  this._updatePieExplodeJSState(sendPPR);
  
}

/**
 * @override
 */
DvtLegacyPieChart.prototype._updatePieExplodeJSState = function(sendPPR)
{ 
  if(this._pieSliceExplodeState == null)
    return;

  // Convert the explodeState array to a string, with commas separating each element
  var explodeStateAsString = this._pieSliceExplodeState.join();
  
  // If we are in the mobile case, set a flag to indicate what we want to PPR the graph so that we get fresh xml
  // that includes the new menu items
  if(DvtAgent.isTouchDevice())
  {
    explodeStateAsString += ";true";
  }
   

  var id = this._chart.getId();
  if (id == null)
    id = "";
  
  var ext = this.getCtx().getJsExt();
  ext.sendPPRProperty(id, DvtJsExt.PPR_ATTRIB_PIE_EXPLODE, explodeStateAsString);
  
  // force a PPR to refresh explode all/unite all menu state here
  if(sendPPR)
    setTimeout( //wait until animation completes
        function(){ext.sendPPRNonBlocking(id)}, 
        this.getAnimationDuration() * 1000 // in milliseconds
    );
}

/**
 * Hides the slice with the given index.  This only works after the pie is rendered.
 * @param {number} index The index of the slice to hide.
 */
DvtLegacyPieChart.prototype.hideSlice = function(index) {
  // Delegate to the event handler to use the standard hide/show code path
  var slices = this.__getSlices();
  var category = slices && index < slices.length ? slices[index].getSliceInfo().getId() : null;
  if(category) {
    var evt = new DvtCategoryHideShowEvent(DvtCategoryHideShowEvent.TYPE_HIDE, category);
    this.processEvent(evt);
  }
}
/**
 * Subclass of DvtSliceInfo providing compatibility with the server layout graph.
 * @class DvtLegacySliceInfo
 * @extends DvtSliceInfo
 * @constructor
 */
var DvtLegacySliceInfo = function() {
  this.Init();
};

DvtObj.createSubclass(DvtLegacySliceInfo, DvtSliceInfo, "DvtLegacySliceInfo");

/**
 * @override
 */
DvtLegacySliceInfo.prototype.Init = function() {
  DvtLegacySliceInfo.superclass.Init.call(this);

  this._shapeAttr = null;
  this._labelShapeAttr = null;
  this._userProps = null;
  this._objId = null;
};

/**
 * Returns the shape attributes for this object.
 * @return {DvtShapeAttr}
 */
DvtLegacySliceInfo.prototype.getShapeAttr = function() {
  return this._shapeAttr;
};

/**
 *  Specifies the shape attributes for this object.
 *  @param {DvtShapeAttr} sa
 */
DvtLegacySliceInfo.prototype.setShapeAttr = function(sa) {
  this._shapeAttr = sa;
};

/**
 * Returns the shape attributes for this object's label.
 * @return {DvtShapeAttr}
 */
DvtLegacySliceInfo.prototype.getLabelShapeAttr = function() {
  return this._labelShapeAttr;
};

/**
 *  Specifies the shape attributes for this object.
 *  @param {DvtShapeAttr} sa
 */
DvtLegacySliceInfo.prototype.setLabelShapeAttr = function(sa) {
  this._labelShapeAttr = sa;
};

/**
 * Returns the user props for this object.
 * @return String
 */
DvtLegacySliceInfo.prototype.getUserProps = function() {
  return this._userProps;
};

/**
 *  Specifies the user props for this object.
 *  @param String userProps
 */
DvtLegacySliceInfo.prototype.setUserProps = function(userProps) {
  this._userProps = userProps;
};

/**
 * Returns the objId for this object.
 * @return String
 */
DvtLegacySliceInfo.prototype.getObjId = function() {
  return this._objId;
};

/**
 *  Specifies the objId for this object.
 *  @param String objId
 */
DvtLegacySliceInfo.prototype.setObjId = function(objId) {
  this._objId = objId;
};
/**
 * Subclass of DvtPieSlice providing compatibility with the server layout graph.
 * @param {DvtPieChart} pieChart A reference to the DvtPieChart that contains this slice
 * @param {DvtSliceInfo} sliceInfo Slice parameters sent down from the middle tier
 * @class DvtLegacyPieSlice
 * @extends DvtPieSlice
 * @constructor
 */
var DvtLegacyPieSlice = function(pieChart, sliceInfo) {
  this.Init(pieChart, sliceInfo);
};

DvtObj.createSubclass(DvtLegacyPieSlice, DvtPieSlice, "DvtLegacyPieSlice");

/**
 * @override
 */
DvtLegacyPieSlice.prototype.getId = function() {
  return DvtGraphEventUtils.getId(this.getSliceInfo().getId(), this.getPieChart().getPieInfo().getId());
}

/**
 * @override
 */
DvtLegacyPieSlice.prototype.getTooltip = function(target) {
  // This override supports tooltips defined via the alt attribute, which 
  // take precedence over all other tooltips.
  var sa = this.getShapeAttr(target);
  if(sa && sa.alt)
    return sa.alt;
  else
    return null;
}

/**
 * Returns the shape attributes for this object.
 * @param {DvtDisplayable} target The displayable on which the event was fired.
 * @returns {DvtShapeAttr}
 */
DvtLegacyPieSlice.prototype.getShapeAttr = function(target) {
  if(target === this.getSliceLabel()) {
    // the slice label inherits the shape attributes on the slice
    // if the label does not have shape attributes, return the 
    // slice's shape attributes
    var sa = this.getSliceInfo().getLabelShapeAttr();
    if(sa)
      return sa;
    else 
      return this.getSliceInfo().getShapeAttr();
  }
  else
    return this.getSliceInfo().getShapeAttr();
}

/**
 * @override
 */
DvtLegacyPieSlice.prototype.render = function () {
  DvtLegacyPieSlice.superclass.render.call(this);

  var sa = this.getShapeAttr(this);
  if (sa && DvtShapeAttr.hasClickableShapeAttribute(sa)) {
    var displayables = this.getDisplayables();
    for (var i = 0;i < displayables.length;i++) {
      if (displayables[i] != this._feeler && displayables[i] != this._sliceLabel)
        displayables[i].setCursor(DvtSelectionEffectUtils.getSelectingCursor());
    }
  }
}

/**
 * @override
 */
DvtLegacyPieSlice.prototype.setSliceLabel = function(sliceLabel)
{
  DvtLegacyPieSlice.superclass.setSliceLabel.call(this, sliceLabel);
  
  if (sliceLabel == null) return;
  
  // If shape attributes are specified, apply additional properties
  var sa = this.getShapeAttr(sliceLabel);
  if(sa) {
    // Cursor
    if (DvtShapeAttr.hasClickableShapeAttribute(sa))
      sliceLabel.setCursor(DvtSelectionEffectUtils.getSelectingCursor());

    // Link coloring
    if(sa.bLink && sa.linkColor) {
      var alpha = sliceLabel.getFill() ? sliceLabel.getFill().getAlpha() : 1;
      sliceLabel.setSolidFill(sa.linkColor, alpha);
    }
  }
}

/**
 * @override
 */
DvtLegacyPieSlice.prototype.getCategories = function() {
  return [this.getSliceInfo().getId()];
}
// Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
/*--------------------------------------------------------------------*/
/*   Dvt2DBarRiser             2D Bar Riser component                 */
/*--------------------------------------------------------------------*/
/**
  *  2D Bar Riser with initFX support.
  *  @extends DvtRect
  *  @class Dvt2DBarRiser  Creates a graph 2D bar riser component.
  *  <p>
  *  Example:<br><br><code>
  *  var line = new Dvt2DBarRiser(context, 10, 10, 100, 150);<br>
  *  </code>
  *  @constructor  
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {number} x  The x position of the top left corner of the rectangle.
  *  @param {number} y  The y position of the top left corner of the rectangle.
  *  @param {number} w  The width of the rectangle.
  *  @param {number} h  The height of the rectangle.
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *  <p>
  *  @returns A new {@link Dvt2DBarRiser} object.
  *  @type Dvt2DBarRiser
  */

var  Dvt2DBarRiser = function(context, x, y, w, h, id)
{
   this.Init(context, x, y, w, h, id) ;
};

DvtObj.createSubclass(Dvt2DBarRiser, DvtRect, "Dvt2DBarRiser");



/*---------------------------------------------------------------------*/
/*  Init()                                                             */
/*---------------------------------------------------------------------*/
/**
  *  Object initializer.
  */
Dvt2DBarRiser.prototype.Init = function(context, x, y, w, h, id)
{
  Dvt2DBarRiser.superclass.Init.call(this, context, x, y, w, h, id) ;
  
  this._bSelected        = false;
  this._bSelecting       = false;
  this._selectionEffects = null;
  this._bSelectedShadow  = true;
  this._dataColor        = "#000000";
  this._bInitFx          = false;
  this._DCHandler        = null ;        // data change handler

  //properties related to selection
  this._savedUpStroke       = null;
  this._savedHoverStroke       = null;
  this._relatedDetObjects = null;
  this._applyStrokeTimer  = null;
  
  this._selectionType    = DvtSelectionEffectUtils.DEFAULT_SEL_TYPE;
};


/*--------------------------------------------------------------------*/
/*  doInitFx()                                                        */
/*--------------------------------------------------------------------*/
/**
  *    Perform initial effects. Called by DvtDetObj on its first render
  */
Dvt2DBarRiser.prototype.doInitFx = function()
{
   var chart = this._Dlo._Chart ;
   var bVert = chart.isVertical() ;
   var dur   = chart.getAnimation().displayDuration ;  // ms
   this._origRect = this.getImpl().getElem().getBBox();

   var from ;
   var to ;
   var animator;

   if (bVert) {
     to = this.getHeight() ;
     this.setHeight(0) ;

     animator = new DvtAnimator(this.getCtx(), dur/1000);
     animator.addProp(DvtAnimator.TYPE_NUMBER, this, this.getHeight, this.setHeight, to);

     from =  this.getY() + to ;
     to   =  this.getY() ;
     this.setY(from) ;
     animator.addProp(DvtAnimator.TYPE_NUMBER, this, this.getY, this.setY, to);
   }
   else {
     to = this.getWidth() ;
     this.setWidth(0) ;

     animator = new DvtAnimator(this.getCtx(), 1);
     animator.addProp(DvtAnimator.TYPE_NUMBER, this, this.getWidth, this.setWidth, to);
   }

   animator.play(); 
};


/*--------------------------------------------------------------------*/
/*  init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    Implementation of DvtDetObj.init()
 */
Dvt2DBarRiser.prototype.init = function()
{
   this._Dlo.setInitFx(this._bInitFx) ;     // allow initfx request
   
   var detObj = this._Dlo;
   var chart = detObj.getChart() ;
   this._dataColor = chart.getSeriesColor(detObj.getSeriesIndex()) ;
   
   if (detObj.isSelectable())   {
     var fillObj = this.getFill();
     if (fillObj instanceof DvtGradientFill)
     {
       this._dataColor = fillObj.getColors()[0];
     }
     else if (fillObj instanceof DvtSolidFill)
     {
       this._dataColor = fillObj.getColor();
     }
     
     this.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
   }
};


/**
 * Set whether this object should use initial effects.
 * 
 * @param {boolean}  bInitFx  true to use initial effects, false otherwise
 */
Dvt2DBarRiser.prototype.setInitFx = function(bInitFx)
{
  this._bInitFx = bInitFx;
};

/**
 * @protected
 * Apply any selection effects to this object.
 */
Dvt2DBarRiser.prototype.ApplySelectionState = function()
{
  DvtSelectionEffectUtils.applySelectionState(this, this._dataColor);
};

/**
 * @protected
 * Apply any mouse hover selection effects to this object.
 */
Dvt2DBarRiser.prototype.ApplySelectingState = function()
{
  DvtSelectionEffectUtils.applySelectingState(this, this._dataColor);
};

/**
 * Show the selection hover effect.
 */
Dvt2DBarRiser.prototype.showHoverEffect = function()
{
  this.SetSelectionMouseOver(true);
};


/**
 * Hide the selection hover effect.
 */
Dvt2DBarRiser.prototype.hideHoverEffect = function()
{
  this.SetSelectionMouseOver(false);
};


/**
 * Determine if the selection hover effect is shown.
 * 
 * @type boolean
 */
Dvt2DBarRiser.prototype.isHoverEffectShown = function()
{
  return this._bSelecting;
};


/**
 * @protected
 * Set whether the selection mouse hover effect is shown.
 * 
 * @param  {boolean}  bOver  true if the mouse is over the object,
 *        false otherwise
 */
Dvt2DBarRiser.prototype.SetSelectionMouseOver = function(bOver)
{
  this.SetSelectionMouseOverSelf(bOver);

  //set selection on related parts of this marker also
  if (this._relatedDetObjects)
  {
    for (var i = 0; i < this._relatedDetObjects.length; i++)
    {
      if (this._relatedDetObjects[i].SetSelectionMouseOverSelf)
      {
        this._relatedDetObjects[i].SetSelectionMouseOverSelf(bOver);
      }
    }
  }
}

/**
 * @protected
 * Set whether the selection mouse hover effect is shown.
 * 
 * @param  {boolean}  bOver  true if the mouse is over the object,
 *        false otherwise
 */
Dvt2DBarRiser.prototype.SetSelectionMouseOverSelf = function(bOver)
{
  if (this._bSelecting != bOver)
  {
    this._bSelecting = bOver;
    this.ApplySelectingState();
  }
};


/**
 * Determine whether this object is selected.
 * 
 * @type boolean
 */
Dvt2DBarRiser.prototype.isSelected = function()
{
  return this._bSelected;
};


/**
 * Set whether the selection effect is shown.
 * 
 * @param  {boolean}  bSel  true if the object is selected,
 *        false otherwise
 */
Dvt2DBarRiser.prototype.setSelected = function(bSel)
{
  this.SetSelectedSelf(bSel);

  //set selection on related parts of this marker also
  if (this._relatedDetObjects)
  {
    for (var i = 0; i < this._relatedDetObjects.length; i++)
    {
      if (this._relatedDetObjects[i].SetSelectedSelf)
      {
        this._relatedDetObjects[i].SetSelectedSelf(bSel);
      }
    }
  }
}

/**
 * @protected
 * Set whether the selection effect is shown.
 * 
 * @param  {boolean}  bSel  true if the object is selected,
 *        false otherwise
 */
Dvt2DBarRiser.prototype.SetSelectedSelf = function(bSel)
{
  if (this._bSelected != bSel)
  {
    this._bSelected = bSel;
    this.ApplySelectionState();
  }
}; 


/**
 * Add a related object of this det object.
 *
 * @param {DvtDisplayable} displayable A DvtDisplayable object related to this one
 */
Dvt2DBarRiser.prototype.addRelatedObject = function(detObj)
{
  if (!this._relatedDetObjects)
  {
    this._relatedDetObjects = [];
  }

  this._relatedDetObjects.push(detObj);
};

/**
 * @override
 */
Dvt2DBarRiser.prototype.UpdateSelectionEffect = function() 
{
// No operation to avoid duplicate bar from DvtShape function.
}
// Copyright (c) 2008, 2011, Oracle and/or its affiliates. 
// All rights reserved. 
/*--------------------------------------------------------------------*/
/*   Dvt3DBar                      3D Bar component                   */
/*--------------------------------------------------------------------*/
/**
  *  Basic 3D Bar.
  *  @extends DvtContainer
  *  @class Dvt3DBar  Creates a graph 3D bar component.
  *  <p>
  *  Example:<br><br><code>
  *  var bar = new Dvt3DBar(context);<br>
  *  </code>
  *  @constructor  
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var   Dvt3DBar = function(context, id)
{
   this.Init(context, id) ;
};  

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



/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
/**
  *  Object initializer.
  */
Dvt3DBar.prototype.Init = function(context, id)
{
  Dvt3DBar.superclass.Init.call(this, context, id) ;
  
  this._front        = null ;
  this._side         = null ;
  this._top          = null ;

  this._bSelected    = false;
  this._bSelecting   = false;
};



/*--------------------------------------------------------------------*/
/*  init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    Implementation of DvtDetObj.init()
 */
Dvt3DBar.prototype.init = function()
{
   var face = this._side ;

   for (var i = 0; i < 3; i++) {
      if (i > 0) {
        face = ((i == 1)? this._front : this._top);
      }
      if (face) {
        this.addChild(face) ;
        if (face.init) {
          face.init();
        }
      }
   }

   if (this._front.setSelectedShadow)
   {
     this._front.setSelectedShadow(false);
   }
   if (this._top && this._top.setSelectedShadow)      // note: 3d triangular bar has no top!
   {
     this._top.setSelectedShadow(false);
   }
} ;


/*--------------------------------------------------------------------*/
/*  getFront(), get Side(), getTop()                                  */
/*--------------------------------------------------------------------*/
/**
 *    @returns {DvtPolygon}  the front face of the bar.
 */
Dvt3DBar.prototype.getFront = function()
{
   return this._front ;
} ;

/**
 *    @returns {DvtPolygon}  the side face of the bar.
 */
Dvt3DBar.prototype.getSide = function()
{
   return this._side ;
} ;

/**
 *    @returns {DvtPolygon}  the top face of the bar.
 */
Dvt3DBar.prototype.getTop = function()
{
   return this._top ;
} ;



/*--------------------------------------------------------------------*/
/*  setAlpha()                                                        */
/*--------------------------------------------------------------------*/
/**
 *     Propagate chart pointer to sub-displayables.
 */
Dvt3DBar.prototype.setAlpha = function(alpha)
{
   this._front.setAlpha(alpha) ;
   this._side.setAlpha(alpha) ;
   this._top.setAlpha(alpha) ;
};


/*--------------------------------------------------------------------*/
/*  setChart()                                                        */
/*--------------------------------------------------------------------*/
/**
 *  Propagate chart pointer to subcomponents (faces) of the bar.
 *  @param {DvtGraphCore}  the owning chart.
 */
Dvt3DBar.prototype.setChart = function(chart)
{
   if (this._front) {
     this._front._Dlo.setChart(chart) ;
   }
   if (this._side) {
     this._side._Dlo.setChart(chart) ;
   }
   if (this._top) {
     this._top._Dlo.setChart(chart) ;
   }
};



/*--------------------------------------------------------------------*/
/*  setFace()                                                         */
/*--------------------------------------------------------------------*/
/**
 *  Sets a sub-displayable face polygon. Also chains the face object to this
 *  object, and updates its owning chart pointer.
 *  Propagate chart pointer to face.
 *  @param  {DvtPolygon} face   a face polygon.
 */
Dvt3DBar.prototype.setFace = function(face)
{
   var objId = face._Dlo.getObjId() ;

   if (objId === DvtGraphObjType.BAR_RISER || objId === DvtGraphObjType.PIE_BAR_RISER) {
     this._front = face ;
   }
   else if (objId === DvtGraphObjType.BAR_RISER_SIDE || objId === DvtGraphObjType.PIE_BAR_RISER_SIDE) {
     this._side  = face ;
   }
   else if (objId === DvtGraphObjType.BAR_RISER_TOP || objId === DvtGraphObjType.PIE_BAR_RISER_TOP) {
     this._top  = face ;
   }

   if (face.set3DBar)
     face.set3DBar(this);
   this.setChart(this._Dlo.getChart()) ;
};



/**
 * Show the selection hover effect.
 */
Dvt3DBar.prototype.showHoverEffect = function()
{
   this._bSelecting = true;
   
   if (this._side.SetSelectionMouseOverSelf)
   {
     this._side.SetSelectionMouseOverSelf(true);
   }
   if (this._front.SetSelectionMouseOverSelf)
   {
     this._front.SetSelectionMouseOverSelf(true);
   }
   if (this._top.SetSelectionMouseOverSelf)
   {
     this._top.SetSelectionMouseOverSelf(true);
   }
};


/**
 * Hide the selection hover effect.
 */
Dvt3DBar.prototype.hideHoverEffect = function()
{
   this._bSelecting = false;
   
   if (this._side.SetSelectionMouseOverSelf)
   {
     this._side.SetSelectionMouseOverSelf(false);
   }
   if (this._front.SetSelectionMouseOverSelf)
   {
     this._front.SetSelectionMouseOverSelf(false);
   }
   if (this._top.SetSelectionMouseOverSelf)
   {
     this._top.SetSelectionMouseOverSelf(false);
   }
};


/**
 * Determine if the selection hover effect is shown.
 * 
 * @type boolean
 */
Dvt3DBar.prototype.isHoverEffectShown = function()
{
  return this._bSelecting;
};


/**
 * Determine whether this object is selected.
 * 
 * @type boolean
 */
Dvt3DBar.prototype.isSelected = function()
{
  return this._bSelected;
};

/**
 * Set whether the selection effect is shown.
 * 
 * @param bOver {boolean}  bSel  true if the object is selected,
 *        false otherwise
 */
Dvt3DBar.prototype.setSelected = function(bSel)
{
   this._bSelected = bSel;
   
   if (this._side.SetSelectedSelf)
   {
     this._side.SetSelectedSelf(bSel);
   }
   if (this._front.SetSelectedSelf)
   {
     this._front.SetSelectedSelf(bSel);
   }
   if (this._top.SetSelectedSelf)
   {
     this._top.SetSelectedSelf(bSel);
   }
};
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. 
// All rights reserved. 
/*--------------------------------------------------------------------*/
/*   Dvt3DBarRiser             3D Bar Riser component                 */
/*--------------------------------------------------------------------*/
/**
  *  3D Bar Riser with initFX support.
  *  @extends Dvt3DBar
  *  @class Dvt3DBarRiser  Creates a graph 3D bar riser component.
  *  <p>
  *  Example:<br><br><code>
  *  var bar = new Dvt3DBarRiser(context);<br>
  *  </code>
  *  @constructor  
  *  @param {DvtContext} context The context object
  *  @param {Object} impl The platform specific implementation.
  */
var   Dvt3DBarRiser = function(context, id)
{
   this.Init(context, id) ;
}  

DvtObj.createSubclass(Dvt3DBarRiser, Dvt3DBar, "Dvt3DBarRiser");


/*---------------------------------------------------------------------*/
/*  Init()                                                             */
/*---------------------------------------------------------------------*/
/**
  *  Object initializer.
  */
Dvt3DBarRiser.prototype.Init = function(context, id)
{
  Dvt3DBarRiser.superclass.Init.call(this, context, id) ;
  
  this._bInitFx          = false;
};



/**
 * Set whether this object should use initial effects.
 * 
 * @param {boolean}  bInitFx  true to use initial effects, false otherwise
 */
Dvt3DBarRiser.prototype.setInitFx = function(bInitFx)
{
  this._bInitFx = bInitFx;
};

/*--------------------------------------------------------------------*/
/*  doInitFx()                                                        */
/*--------------------------------------------------------------------*/
/*                                                                    */
/*                           +----+   +       Rectangular (and        */
/*                    BRB   /    /   /|       diamond) cross-section  */
/*                         +----+   / |                               */
/*                         +----+  +  |                               */
/*                         |    |  |  |                               */
/*                         |    |  |  |                               */
/*                         |    |  |  |                               */
/*                         |    |  |  |                               */
/*                         | BR |  |  +                               */
/*                         |    |  | /                                */
/*                         |    |  |/  BRS                            */
/*                         +----+  +                                  */
/*                                                                    */
/*  p="botLeftX, botLeftY, botRightX, botRightY, topRightX, topRightY,*/
/*       [0]        [1]       [2]        [3]        [4]        [5]    */
/*                                                                    */
/*     topLeftX, topLeftY, botLeftX, botLeftY"                        */
/*       [6]        [7]      [8]       [9]                            */
/*--------------------------------------------------------------------*/
/*                       +   + - - +   +                              */
/*                       |\   \   /   / |    Triangular               */
/*                       | \   \ /   /  |    cross-section            */
/*                       |  +   +   +   |                             */
/*                       |  | BRB   |   |    Note that front and      */
/*                       |  |       |   |    side could be consid-    */
/*                       |  |       |   |    ered interchanged        */
/*                       |  |       |   |    compared to the rect-    */
/*                       |  |       |   |    angular c/s above.       */
/*                       +  |       |   +                             */
/*                        \ |       |  /                              */
/*                     BR  \|       |/  BRS                           */
/*                          +       +                                 */
/*                                                                    */
/*                        BR        BRS                               */
/*  For BR and BRS                                                    */
/*  p="botLeftX, botLeftY, botRightX, botRightY, topRightX, topRightY,*/
/*       [0]        [1]       [2]        [3]        [4]        [5]    */
/*                                                                    */
/*     topLeftX, topLeftY, botLeftX, botLeftY"                        */
/*       [6]        [7]      [8]       [9]                            */
/*                                                                    */
/*                                                                    */
/*  For BRB:                                                          */
/*  p="botX, botY, topRightX, topRightY, topLeftX, topLeftY           */
/*     [0]   [1]      [2]        [3]        [4]      [5]              */
/*                                                                    */
/*     botX, botY                                                     */
/*     [6]   [7]                                                      */
/*--------------------------------------------------------------------*/
/**
  *    Perform initial effects. Called by DvtDetObj on its first render
  */
Dvt3DBarRiser.prototype.doInitFx = function()
{
   var chart = this._Dlo._Chart ;
   var dur   = chart.getAnimation().displayDuration ;  // ms

   //  Prep for the front and side polygons

   var face ;
   var arTo ;
   var ar ;
   var arToSide ;
   var arToFront ;
   for (var i = 0; i < 2; i++) {
      face  = (i === 0? this._side : this._front) ;
      arTo  = face.getPoints() ;
      ar    = DvtArrayUtils.copy(arTo) ;

      ar[7] = ar[1] ;        // top left y
      ar[9] = ar[1] ;        // back to bottom left y
      if (i === 0) {
        ar[5]    = ar[3] ;   // top right y
        arToSide = arTo ;
      }
      else {
        ar[5]     = ar[1] ;  // top right y
        arToFront = arTo ;
      }
      face.setPoints(ar) ;   // set initial minimized points
   }


   //  Prep for the top

   var  height = arToFront[1] - arToFront[7];       // front face height

   arTo  = this._top.getPoints() ;
   ar    = DvtArrayUtils.copy(arTo) ;

   var bTriang = (ar.length < 10) ;

   if (bTriang) {
     ar[1] += height ;      // front point right y
     ar[3] += height ;      // top right y
     ar[5] += height ;      // top left y
     ar[7] += height ;      // front point y
   }
   else {
     ar[5] += height ;      // top right y
     ar[7] += height ;      // top left y
     ar[1] += height ;      // bottom left y
     ar[3] += height ;      // bottom right y
     ar[9] += height ;      // back to bottom left
   }
   this._top.setPoints(ar) ;

   var anim = new DvtAnimator(this.getCtx(), dur/1000);
   anim.addProp(DvtAnimator.TYPE_NUMBER_ARRAY, this._side,  this._side.getPoints,  this._side.setPoints,  arToSide);
   anim.addProp(DvtAnimator.TYPE_NUMBER_ARRAY, this._front, this._front.getPoints, this._front.setPoints, arToFront);
   anim.addProp(DvtAnimator.TYPE_NUMBER_ARRAY, this._top,   this._top.getPoints,   this._top.setPoints,   arTo);

   anim.play(); 
};


/*--------------------------------------------------------------------*/
/*  init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    Implementation of DvtDetObj.init()
 */
Dvt3DBarRiser.prototype.init = function()
{
   this._Dlo.setInitFx(this._bInitFx) ;     // allow initfx request

   Dvt3DBarRiser.superclass.init.call(this) ;
};
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. 
// All rights reserved. 
/*---------------------------------------------------------------------*/
/*   DvtAreaRiser            Graph area Riser component                */
/*---------------------------------------------------------------------*/
/**
  * Creates a graph area riser component.
  * @class DvtAreaRiser
  * @extends DvtPolygon
  * @constructor  
  * @param {DvtContext} context   The platform dependent context object (such
  *                               as {@link DvtSvgContext}).
  * @param {array} ar  An array of consecutive x,y coordinate pairs.
  * @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  * @returns {DvtAreaRiser} A new DvtAreaRiser object.
 */
var  DvtAreaRiser = function(context, arPoints, id)
{
   this.Init(context, arPoints, id) ;
};

DvtObj.createSubclass(DvtAreaRiser, DvtPolygon, "DvtAreaRiser") ;


/*--------------------------------------------------------------------*/
/*  doInitFx()                                                        */
/*--------------------------------------------------------------------*/
/**
  *    Perform initial effects. Called by DvtDetObj on its first render
  */
DvtAreaRiser.prototype.doInitFx = function()
{
    var dur = this._Dlo._Chart.getAnimation().displayDuration ;  // ms

   //  The last 3 coords just connect the last data point to the frame and
   //  back to the first data point to create an area, and are not data coords.
   //  The last of these connects to the first data point, and since the first
   //  data point's y is being tweened, we also need to tween the last connector
   //  point's y also.

   var arPoints = this.getPoints() ;
   var arFrom   = DvtArrayUtils.copy(arPoints);
   var len      = arFrom.length ;

   if (len <= 6) {                    // (3 coords = 6 array entries)
     return ;                         // bale out - no data points.
   }
   var yBase = arPoints[len-3] ;      // y value of base line

   len -= 6 ;                         // get count of data points
   for (i = 0; i < len; i += 2) {
      arFrom[i+1] = yBase ;
   }

   this.setPoints(arFrom) ;            // set initial position of area riser

   var anim = new DvtAnimator(this.getCtx(), dur/1000);
   anim.addProp(DvtAnimator.TYPE_NUMBER_ARRAY, this, this.getPoints, this.setPoints, arPoints);
   anim.play() ;

};


/*--------------------------------------------------------------------*/
/*  init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    Implementation of DvtDetObj.init()
 */
DvtAreaRiser.prototype.init = function()
{
   this._Dlo.setInitFx(true) ;     // allow initfx request
}
// Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
/*--------------------------------------------------------------------*/
/*   DvtLineRiser          Base Graph Line Riser                      */
/*--------------------------------------------------------------------*/
/**
  *  Creates a graph polyline line riser component.
  *  @extends DvtPolyline
  *  @class DvtLineRiser
  *  @constructor  
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
 *  @param {Array} arPoints  An array of consecutive x,y coordinate pairs.
 *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var  DvtLineRiser = function(context, arPoints, id)
{
   this.Init(context, arPoints, id) ;
};

DvtObj.createSubclass(DvtLineRiser, DvtPolyline, "DvtLineRiser") ;



/*--------------------------------------------------------------------*/
/*  doInitFx()                                                        */
/*--------------------------------------------------------------------*/
/**
  *    Perform initial effects. Called by DvtDetObj on its first render
  */
DvtLineRiser.prototype.doInitFx = function()
{
   var arPoints = this.getPoints() ;
   if (arPoints.length < 4) {               // if only 1 (or zero) point(s)
     return ;                               // don't attempt to animate
   }
   var arFrom  = DvtArrayUtils.copy(arPoints);
   var len     = arFrom.length ;
   var dur     = this._Dlo._Chart.getAnimation().displayDuration ; // ms

   var mean    = 0 ;
   var min     = Number.MAX_VALUE ;
   var max     = Number.MIN_VALUE ;
   var i ;

   for (i = 1; i < len; i += 2) {   // find largest and smallest y-values
      var v = arFrom[i] ;
      if (v < min) {
        min = v ;
      }
      if (v > max) {
        max = v ;
      }
      mean += v ;
   }

   if (len > 4) {                   // if we have more than 2 points
     mean -= min ;                  // discard smallest
     mean -= max ;                  // and largest values
     mean /= (len -4)/2 ;           // get avg value
   }
   else {                           // 2 points, make the second
     mean = arFrom[1] ;             // grow from the first.
     arFrom[2] = arFrom[0] ;
   }
   mean = Math.round(mean) ;        // save some space in the points string

   for (i = 1; i < len; i += 2) {
      arFrom[i] = mean ;
   }
   this.setPoints(arFrom) ;


   var anim = new DvtAnimator(this.getCtx(), dur/1000);
   anim.addProp(DvtAnimator.TYPE_NUMBER_ARRAY, this, this.getPoints, this.setPoints, arPoints);

   //  Fade in any associated line markers at the end of the line animation

   var anim2 = new DvtAnimator(this.getCtx(), (0.2 * dur/1000), (0.8 * dur/1000));

   var arLM = this.getLineMarkers() ;
   if (arLM) {
     len = arLM.length ;
     for (i = 0; i < len; i++) {
        var lm    = arLM[i] ;
        if(!lm._bAnimatedOnDisplay) {
          var alpha = lm.getAlpha() ;
          lm.setAlpha(0) ;
          anim2.addProp(DvtAnimator.TYPE_NUMBER, lm, lm.getAlpha, lm.setAlpha, alpha);
          lm._bAnimatedOnDisplay = true;
        }
     }
   }

   anim.play(); 
   anim2.play(); 
};


/*--------------------------------------------------------------------*/
/*  getLineMarkers()                                                  */
/*--------------------------------------------------------------------*/
/**
  *   Returns an array of associated line marker objects for this line riser.
  *   @type  Array
  */
DvtLineRiser.prototype.getLineMarkers = function()
{
   return this._aLMarkers ;    // (array was inserted by DvtGraphParser)
};


/*--------------------------------------------------------------------*/
/*  init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    Implementation of DvtDetObj.init()
 */
DvtLineRiser.prototype.init = function()
{
   this._Dlo.setInitFx(true) ;     // allow initfx request
}



/*--------------------------------------------------------------------*/
/*   Init()                                                           */
/*--------------------------------------------------------------------*/
/**
  *   Override of DvtDisplayable.Init()
  */
DvtLineRiser.prototype.Init = function(context, arPoints, id)
{
   DvtLineRiser.superclass.Init.call(this, context, arPoints, id) ;

   this._aLM = null ;      // associated line markers
};
// Copyright (c) 2011, 2013, Oracle and/or its affiliates. 
// All rights reserved. 
/** 
  *  A marker object for scatter and bubble charts that allows dragging.
  *  @class DvtGraphDetMarker A marker object for scatter and bubble charts that allows dragging.
  *  @extends DvtMarker
  *  @constructor
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {number} type The type of the marker (see {@link DvtMarker}).
  *  @param {number} x  The x position of the top left corner of the marker.
  *  @param {number} y  The y position of the top left corner of the marker.
  *  @param {number} w  The width of the marker.
  *  @param {number} h  The height of the marker.
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *  @param {number} sx  Optional The horizontal scale factor of the marker.
  *  @param {number} sy  Optional The vertical scale factor of the marker.
  *  @param {boolean} maintainAspect Optional Indicates whether aspect ratio should be maintained (defaults to false)
  */
var  DvtGraphDetMarker = function(context, type, skin, x, y, w, h, id, sx, sy, maintainAspect)
{
   this.Init(context, type, skin, x, y, w, h, id, sx, sy, maintainAspect) ;
};

DvtObj.createSubclass(DvtGraphDetMarker, DvtMarker, "DvtGraphDetMarker") ;

/**
  *  Object initializer.
  *  @protected
  */
DvtGraphDetMarker.prototype.Init = function(context, type, skin, x, y, w, h, id, sx, sy, maintainAspect)
{
  DvtGraphDetMarker.superclass.Init.call(this, context, type, skin, x, y, w, h, id, sx, sy, maintainAspect) ;
};

//---------------------------------------------------------------------//
// Ordering Support: DvtDraggable impl                                //
//---------------------------------------------------------------------//

/**
 * If this object can be dragged, returns the client id of the drag component.  Otherwise returns null.
 * @param  clientIds  the array of client idds of the valid drag components
 * @override
 */
DvtGraphDetMarker.prototype.isDragAvailable = function(clientIds) {
    // there is only a single drag source withing graph, so we'll return
    // the first clientId
    return clientIds[0];
};

/**
 * @export
 * Returns the transferable for a drag initiated with this object.  This transferable will be sent through
 * the javascript peer to the server.
 */
DvtGraphDetMarker.prototype.getDragTransferable = function(mouseX, mouseY) {
  return this._Dlo._Chart.getDragTransferable(mouseX, mouseY);
}

/**
 * Returns the DvtDisplayable to use as the drag feedback for this object.
 */
DvtGraphDetMarker.prototype.getDragFeedback = function(mouseX, mouseY) {
  return this._Dlo._Chart.getDragOverFeedback(mouseX, mouseY);
}

/**
 * @override
 */
DvtGraphDetMarker.prototype.UpdateSelectionEffect = function() {}
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. 
// All rights reserved. 
/*--------------------------------------------------------------------*/
/*   DvtMarkerRiser        Scatter/Bubble marker component            */
/*--------------------------------------------------------------------*/
/**
  *  Scatter/Bubble marker riser with initFX support.
  *  @extends DvtGraphDetMarker
  *  @class DvtMarkerRiser  Creates a graph scatter/bubble marker riser component.
  *  @constructor  
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {number} type  See definitions in {@link DvtMarker}.
  *  @param {number} x
  *  @param {number} y
  *  @param {number} w
  *  @param {number} y
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *  @param {number} sx  Optional The horizontal scale factor of the marker.
  *  @param {number} sy  Optional The vertical scale factor of the marker.
  *  @param {boolean} maintainAspect Optional Indicates whether aspect ratio should be maintained (defaults to false)
  */
var   DvtMarkerRiser = function(context, type, skin, x, y, w, h, id, sx, sy, maintainAspect)
{
   this.Init(context, type, skin, x, y, w, h, id, sx, sy, maintainAspect) ;
}  

DvtObj.createSubclass(DvtMarkerRiser, DvtGraphDetMarker, "DvtMarkerRiser");

//FIX BUG 12568709: do initial animation like Flash, where bubbles/markers
//grow from center
/**
 * @private
 */
DvtMarkerRiser._INIT_ANIM_MOVE = 0;
/**
 * @private
 */
DvtMarkerRiser._INIT_ANIM_SCALE = 1;
/**
 * @private
 */
DvtMarkerRiser._INIT_ANIM = DvtMarkerRiser._INIT_ANIM_SCALE;

/**
 * @override
 */
DvtMarkerRiser.prototype.Init = function(context, type, skin, x, y, w, h, id, sx, sy, maintainAspect) {
  DvtMarkerRiser.superclass.Init.call(this, context, type, skin, x, y, w, h, id, sx, sy, maintainAspect);
  
  // Cache a copy of the provided coords for init fx support
  this._mrX = x;
  this._mrY = y;
  this._mrW = w;
  this._mrH = h;
}

/*--------------------------------------------------------------------*/
/*  doInitFx()                                                        */
/*--------------------------------------------------------------------*/
/**
  *    Perform initial effects. Called by DvtDetObj on its first render
  */
DvtMarkerRiser.prototype.doInitFx = function()
{
 
   var dlo    = this._Dlo ;
   var chart  = dlo._Chart ;
   var dur    = chart.getAnimation().displayDuration ;   // ms
   
   //FIX BUG 12568709: do initial animation like Flash, where bubbles/markers
   //grow from center
   var anim = this.CreateInitAnim(chart, dur);

   if (anim)
   {
     // Bubble charts are also alpha fading
     if (chart.isBubbleChart()) {
      var bubalpha = this.getAlpha() ;
      this.setAlpha(0);
      anim.addProp(DvtAnimator.TYPE_NUMBER, this, this.getAlpha, this.setAlpha, bubalpha);
     }

     anim.play();
   }
};


/**
 * @protected
 */
DvtMarkerRiser.prototype.CreateInitAnim = function(chart, dur)
{
  switch (DvtMarkerRiser._INIT_ANIM)
  {
    case DvtMarkerRiser._INIT_ANIM_MOVE:
      return this.CreateInitAnimMove(chart, dur);
    case DvtMarkerRiser._INIT_ANIM_SCALE:
      return this.CreateInitAnimScale(chart, dur);
  }
  return null;
};


/**
 * @protected
 */
DvtMarkerRiser.prototype.CreateInitAnimMove = function(chart, dur)
{
   // divide plot into 4 quadrants  2 1
   //                               3 4
   var r      = chart.getPlotRect() ;
   var midW = r.x + r.w/2 ;  
   var midH = r.y + r.h/2 ;  
   var xTo = this._mrX + this._mrW/2;
   var yTo = this._mrY + this._mrH/2;
   var xFrom = (xTo >= midW)? (r.x + r.w) : r.x ;
   var yFrom = (yTo >= midH)? (r.y + r.h) : r.y ;
   var dx = xFrom - xTo;
   var dy = yFrom - yTo;

   // Set up the initial and final positions
   var toMatrix = this.getMatrix();
   var fromMatrix = this.getMatrix().clone();
   fromMatrix.translate(dx, dy);
   this.setMatrix(fromMatrix);
   
   // Set up the animation to go to the final position
   var anim = new DvtAnimator(this.getCtx(), dur/1000);
   anim.addProp(DvtAnimator.TYPE_MATRIX, this, this.getMatrix, this.setMatrix, toMatrix);
   return anim;
};


/**
 * @protected
 */
DvtMarkerRiser.prototype.CreateInitAnimScale = function(chart, dur)
{
   // Set up the initial and final positions
   var toMatrix = this.getMatrix();
   
   //scale down to almost nothing, but can't use 0 directly
   this.setScaleX(0.000001);
   this.setScaleY(0.000001);
   //translate scaled down marker to centerpoint of normal size marker
   var fromMatrix = this.getMatrix().clone();
   fromMatrix.translate(this._mrW/2, this._mrH/2);
   this.setMatrix(fromMatrix);
   
   // Set up the animation to go to the final position
   var anim = new DvtAnimator(this.getCtx(), dur/1000);
   anim.addProp(DvtAnimator.TYPE_MATRIX, this, this.getMatrix, this.setMatrix, toMatrix);
   return anim;
};


/*--------------------------------------------------------------------*/
/*  init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    Implementation of DvtDetObj.init()
 */
DvtMarkerRiser.prototype.init = function()
{  
   this._Dlo.setInitFx(this._bInitFx) ;     // allow initfx request
}


/**
 * Set whether this object should use initial effects.
 * 
 * @param {boolean}  bInitFx  true to use initial effects, false otherwise
 */
DvtMarkerRiser.prototype.setInitFx = function(bInitFx)
{
  this._bInitFx = bInitFx;
};
/**
 *  Graph scrollbar component.
 *  @extends DvtScrollbar
 *  @class DvtGraphScrollbar component.
 *  @constructor
 */
var DvtGraphScrollbar = function (context, uiId, x, y, w, h, isHoriz, isZoomable, id) {
  this.Init(context, uiId, x, y, w, h, isHoriz, isZoomable, id);
}

DvtObj.createSubclass(DvtGraphScrollbar, DvtScrollbar, "DvtGraphScrollbar");

DvtGraphScrollbar.prototype.Init = function (context, uiId, x, y, w, h, isHoriz, isZoomable, id) {
  this._axis                = null;
  this._scrollEventJs       = null;
  this._zoomEventJs         = null;
  this._uiId                = uiId;
    
  DvtGraphScrollbar.superclass.Init.call(this, context, x, y, w, h, isHoriz, isZoomable, false, null, id);
}



/**
 * getUIId - returns the ID identifying this scrollbar
 */
DvtGraphScrollbar.prototype.getUIId = function() {
    return this._uiId;
}

DvtGraphScrollbar.prototype.setLiveScroll = function(isLiveScroll) {
    this._isLiveScroll = isLiveScroll;
}

DvtGraphScrollbar.prototype.getAxis = function () {
  return this._axis;
}

DvtGraphScrollbar.prototype.addDataAxis = function (axisMinValue, axisMaxValue, valueFormat) {
  this._axis = new DvtDataAxis(axisMinValue, axisMaxValue, this._thumbStartMin, 
                              this._thumbStartMin + this._thumbLengthMax, this.getWidth(), this.getHeight(), 
                              this._isHoriz, valueFormat);
}

DvtGraphScrollbar.prototype.addTimeAxis = function (startDate, endDate, timeAxisResources, timeZoneOffset) {
  // TODO: convert Dates to millis
  this._axis = new DvtTimeAxis(startDate, endDate, this._thumbStartMin, this._thumbStartMin + this._thumbLengthMax, 
                              this.getWidth(), this.getHeight(), timeAxisResources, timeZoneOffset, this._isHoriz);
}

DvtGraphScrollbar.prototype.addCustomAxis = function (labels) {
  // TODO: process labels
  this._axis = new DvtCustomAxis(labels, this._thumbStartMin, this._thumbStartMin + this._thumbLengthMax, 
                                this.getWidth(), this.getHeight(), this._isHoriz);
}

/**
 *   @return String
 */
DvtGraphScrollbar.prototype.getScrollEventJs = function () {
  return this._scrollEventJs;
}

/**
 *   @param {String} ejs  scroll event Javascript.
 */
DvtGraphScrollbar.prototype.setScrollEventJs = function (ejs) {
  this._scrollEventJs = ejs;
}

/**
  *   @return String
  */
DvtGraphScrollbar.prototype.getZoomEventJs = function() {
  return this._zoomEventJs ;
}

/**
  *   @param {String} ejs  zoom event Javascript.
  */
DvtGraphScrollbar.prototype.setZoomEventJs = function(ejs) {
  this._zoomEventJs = ejs ;
}
/*--------------------------------------------------------------------*/
/*   DvtZoomButton   Scrollbar Zoom Button Component                  */
/*--------------------------------------------------------------------*/
/**
  *  Scrollbar Zoom Button Component.
  *  @extends DvtContainer
  *  @class DvtZoomButton scrollbar zoom button component. 
  *  @constructor  
  */
var   DvtZoomButton = function(context, resources, skin, flipped, id) {
    this.Init(context, resources, skin, flipped, id);
}

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

// Constants
DvtZoomButton.BUTTON_SHOW = 0;
DvtZoomButton.BUTTON_HIDE = 1;

DvtZoomButton.BUTTON_STATE_UP = 0;
DvtZoomButton.BUTTON_STATE_OVER = 1;
DvtZoomButton.BUTTON_STATE_DOWN = 2;

DvtZoomButton.UP_BORDER_COLOR = "#ffffff";
DvtZoomButton.UP_BORDER_WIDTH = NaN;
DvtZoomButton.UP_FILL_COLORS = ["#ffffff", "#ffffff"];
DvtZoomButton.UP_FILL_ALPHAS = [0, 0];
DvtZoomButton.UP_FILL_RATIOS = [0, 255];
     
DvtZoomButton.OVER_BORDER_COLOR = "#097bf8";
DvtZoomButton.OVER_BORDER_WIDTH = NaN;
//DvtZoomButton.OVER_FILL_COLORS = ["#0d8cf9", "#4ab5fc"];
DvtZoomButton.OVER_FILL_COLORS = ["#4ab5fc", "#0d8cf9"];
DvtZoomButton.OVER_FILL_ALPHAS = [1, 1];
DvtZoomButton.OVER_FILL_RATIOS = [0, 255];
     
DvtZoomButton.DOWN_BORDER_COLOR = "#097bf8";
DvtZoomButton.DOWN_BORDER_WIDTH = NaN;
DvtZoomButton.DOWN_FILL_COLORS = ["#4e555e", "#a7b7d4"];
DvtZoomButton.DOWN_FILL_ALPHAS = [1, 1];
DvtZoomButton.DOWN_FILL_RATIOS = [0, 255];
     
DvtZoomButton.MG_UP_BORDER_COLOR = "#000000";
DvtZoomButton.MG_UP_FILL_COLORS= ["#FFFFFF", "#FFFFFF", "#8BB9FC", "#BDE3FD", "#BDE3FD"];
DvtZoomButton.MG_UP_FILL_ALPHAS = [1, 1, 1, 1, 0.5];
DvtZoomButton.MG_UP_FILL_RATIOS = [0, 50, 90, 190, 255];
DvtZoomButton.MG_UP_FOCAL_POINT = 0.3;
DvtZoomButton.MG_HANDLE_COLOR = "#303950";

DvtZoomButton.WIDTH = 20;
DvtZoomButton.HEIGHT = 16;
DvtZoomButton.CORNER_RADIUS = 4;
DvtZoomButton.MAGNIFYING_GLASS_RADIUS = 4;


DvtZoomButton.prototype.Init = function(context, resources, skin, flipped, id) {
    if (skin != null) {
        //TODO
        //this.setSkin(skin);
    }
    this._context = context;
    
    var container = new DvtContainer(context, "scrollbarZoomButton" + id);
    var impl = container.getImpl();
    DvtZoomButton.superclass.Init.call(this, context, impl.getId());
    
    this._menuRenderer = null;
    this._menu = null;
    this._resources = resources;
    this._mouseEnabled = true;
    this._buttonMode = true;
    this._flipped = flipped;
    this._tooltip = null;
    this._resourceActualSize = null;
    this._resourceZoomIn = null;
    this._resourceZoomOut = null;
    this._resourceXAxis = null;
    this._resourceYAxis = null;
    this._resourceY2Axis = null;
    this._sX1InJS = null;                // zoom in/out event javascript
    this._sX1OutJS = null;
    this._sY1InJS = null;
    this._sY1OutJS = null;
    this._sY2InJS = null;
    this._sY2OutJS = null;

    this._sX1ActualJS = null;
    this._sY1ActualJS = null;
    this._sY2ActualJS = null;
    this._sAllActualJS = null;
    this._buttonState = null;

    // the contents of the current DvtZoomMenuRenderer, represented as
    // a DvtRect. from a keyboarding perspective, we are 
    // interested in the DvtRect's child DvtZoomMenuItemRenderer and DvtZoomSubmenuRenderer objects
    this._curMenuRendererContents = null;
  
    // index of the menu item with keyboard focus
    this._curRendererIdx = -1;
  
    // the contents of the parent zoom menu renderer, represented as
    // a DvtRect. from a keyboarding perspective, we are 
    // interested in the DvtRect's child DvtZoomMenuItemRenderer and DvtZoomSubmenuRenderer objects
    // if we wanted to make this more general, we would use a stack or array to keep track of the
    // entire menu chain
    this._prevMenuRendererContents = null;
  
    // index of the menu item from the previous menu that triggered the current menu
    this._prevRendererIdx = -1;    
    
    this.drawUpState();
    if (DvtAgent.isTouchDevice()) {
        this.drawOverState();
        this._isMouseOver = true;
    } else {
        this.hide();
        this._isMouseOver = false;
    }
    
    this.setCursor(DvtSelectionEffectUtils.getSelectingCursor());

    if (!DvtAgent.isTouchDevice()) {
        this.addEvtListener(DvtMouseEvent.MOUSEOUT, this.mouseOutHandler, false, this);
        this.addEvtListener(DvtMouseEvent.MOUSEOVER, this.mouseOverHandler, false, this);
        this.addEvtListener(DvtMouseEvent.CLICK, this.clickHandler, false, this);
    } else {
        this.addEvtListener(DvtTouchEvent.TOUCHSTART, this._touchHandler, false, this);
        this.addEvtListener(DvtTouchEvent.TOUCHMOVE, this._touchHandler, false, this);
        this.addEvtListener(DvtTouchEvent.TOUCHEND, this._touchHandler, false, this);
    }
}  



DvtZoomButton.prototype.onZoomEvent = function(event) {
    try {
        var sZoomType = event.getZoomType();
        var uiAxis = event.getAxis();
        var sJS;
        
        if (sZoomType == DvtGraphZoomEvent.ZOOM_IN)
        {
            if (uiAxis == DvtGraphZoomEvent.AXIS_X1)
            sJS = this._sX1InJS ;
             else if (uiAxis == DvtGraphZoomEvent.AXIS_Y1)
               sJS = this._sY1InJS ;
             else if (uiAxis == DvtGraphZoomEvent.AXIS_Y2)
               sJS = this._sY2InJS ;
        }
        else if (sZoomType == DvtGraphZoomEvent.ZOOM_OUT)
        {
             if (uiAxis == DvtGraphZoomEvent.AXIS_X1)
               sJS = this._sX1OutJS ;
             else if (uiAxis == DvtGraphZoomEvent.AXIS_Y1)
               sJS = this._sY1OutJS ;
             else if (uiAxis == DvtGraphZoomEvent.AXIS_Y2)
               sJS = this._sY2OutJS ;
        }
        else if (sZoomType == DvtGraphZoomEvent.ZOOM_ACTUAL_SIZE)
        {
             if (uiAxis == DvtGraphZoomEvent.AXIS_X1)
               sJS = this._sX1ActualJS ;
             else if (uiAxis == DvtGraphZoomEvent.AXIS_Y1)
               sJS = this._sY1ActualJS ;
             else if (uiAxis == DvtGraphZoomEvent.AXIS_Y2)
               sJS = this._sY2ActualJS ; 
             else if (uiAxis == DvtGraphZoomEvent.AXIS_ALL)
               sJS = this._sAllActualJS ;
        }

        if (sJS != null) {
            //eval(sJS);
            // this eventually calls eval(sJS)
            this._context.getJsExt().callJs(sJS);
        }
        
    } catch (err) {
        console.log(err);
    }
}

DvtZoomButton.prototype.clickHandler = function(event) {
    this.processClick(event.target);
    event.stopPropagation();  
}

DvtZoomButton.prototype.processClick = function(target) {
    // Do not perform any action if the menu (or its children) have been clicked
    if (this._menuRenderer != null && target instanceof DvtDisplayable && this._menuRenderer.contains(target))
    return;
    
    if (!this._menuRenderer) {
        this.drawDownState();
        this._menuRenderer = new DvtZoomMenuRenderer(this._context, this._menu, this._flipped, this.onZoomEvent, this);
        if (!this.isFlipped()) {
            this._menuRenderer.setTranslateX(0); 
        } else {
            this._menuRenderer.setTranslateX(-this._menuRenderer._w + DvtZoomButton.WIDTH); 
        }
        this._menuRenderer.setTranslateY(-this._menuRenderer._h);
        this.addChild(this._menuRenderer);
        
        // Add drop shadow to menu
        var angle = 45; 
        if (this._flipped) {
            angle = 135;
        }
        var shadowRGBA = DvtColorUtils.makeRGBA(00,00,00, 0.45);

        var shadow =  new DvtShadow(shadowRGBA, 
                              5, // distance
                              7, // blurX
                              7, // blurY
                              angle, // angle of the shadow
                              1, // strength or the imprint/spread
                              3, // quality
                              false, // inner shadow
                              false, // knockout effect
                              false // hide object
                              );
        this._menuRenderer.__bg.addDrawEffect(shadow);
        
        this.getParent().addChild(this);
    } else {
        this.resetButton();
    }

}

DvtZoomButton.prototype.mouseOverHandler = function(event) {
    if (this._buttonState != DvtZoomButton.BUTTON_STATE_DOWN) {
        this.drawOverState();
    }
    this._isMouseOver = true;
    event.stopPropagation();
}

DvtZoomButton.prototype.mouseOutHandler = function(event) {
    if (this._buttonState != DvtZoomButton.BUTTON_STATE_DOWN) {
        this.drawUpState();
    }
    this._isMouseOver = false;
    event.stopPropagation();
}

/**
 * Processes "touchstart" event
 * @param {DvtTouchEvent} event Touch event
 * @param {DvtTouch} touch Touch object
 */
DvtZoomButton.prototype.immediateTouchAttempted = function(event, touch) {
    var tm = this.getTouchManager();
    tm.saveProcessedTouchDefault(touch.identifier, this, this, "zoomButton", "zoomButton", this);
    this.drawDownState();
    event.blockTouchHold();
}

DvtZoomButton.prototype.immediateTouchMoved = function(event, touch) {
    event.preventDefault();
}

DvtZoomButton.prototype.immediateTouchEnded = function(event, touch) {
    this.drawOverState();
    this.processClick(event.target);
    event.stopPropagation();
}

/**
 * Handler for the touch event
 * @param {DvtTouchEvent} event Touch event
 * @private
 */
DvtZoomButton.prototype._touchHandler = function(event) {
  this.getTouchManager().preEventBubble(event);
  this.getTouchManager().processAssociatedTouchDefault(event, this);
  this.getTouchManager().postEventBubble(event);
}

//  Zoom Event Javascript setter methods

DvtZoomButton.prototype.setX1ScrollInJS = function(sJS)
{
    this._sX1InJS = sJS ;
}
DvtZoomButton.prototype.setX1ScrollOutJS = function(sJS)
{
    this._sX1OutJS = sJS ;
}
DvtZoomButton.prototype.setY1ScrollInJS = function(sJS)
{
    this._sY1InJS = sJS ;
}
DvtZoomButton.prototype.setY1ScrollOutJS = function(sJS)
{
    this._sY1OutJS = sJS ;
}
DvtZoomButton.prototype.setY2ScrollInJS = function(sJS)
{
    this._sY2InJS = sJS ;
}
DvtZoomButton.prototype.setY2ScrollOutJS = function(sJS)
{
    this._sY2OutJS = sJS ;
}
DvtZoomButton.prototype.setX1ActualJS = function(sJS)
{
    this._sX1ActualJS = sJS ;
}
DvtZoomButton.prototype.setY1ActualJS = function(sJS)
{
    this._sY1ActualJS = sJS ;
}
DvtZoomButton.prototype.setY2ActualJS = function(sJS) 
{
    this._sY2ActualJS = sJS ;
}
DvtZoomButton.prototype.setAllActualJS = function(sJS)
{
    this._sAllActualJS = sJS ;
}


// Create the menu object, menu items, submenus

DvtZoomButton.prototype.createMenu = function(bVerticalChart)
{
    var x1Axis = this._sX1InJS != null || this._sX1OutJS != null ;
    var y1Axis = this._sY1InJS != null || this._sY1OutJS != null ;
    var y2Axis = this._sY2InJS != null || this._sY2OutJS != null ;
    
    // Parse the resources for the menu items
    if(this._resources != null) {
        var resourceArray = this._resources.split(",");
        this._tooltip            = resourceArray[0];
        this._resourceActualSize = resourceArray[1];
        this._resourceZoomIn     = resourceArray[2];
        this._resourceZoomOut    = resourceArray[3];
          
        if(resourceArray.length == 7) // has X, Y, Y2 Axis strings
        {
        this._resourceXAxis    = resourceArray[4];
        this._resourceYAxis    = resourceArray[5];
        this._resourceY2Axis   = resourceArray[6];
        }
    }
        
    // Only one axis, no submenus needed
    if(x1Axis && !y1Axis && !y2Axis)
        this._menu = this.createSubmenu(null, DvtGraphZoomEvent.AXIS_X1);
    else if(!x1Axis && y1Axis && !y2Axis)
        this._menu = this.createSubmenu(null, DvtGraphZoomEvent.AXIS_Y1);
    else if(!x1Axis && !y1Axis && y2Axis)
        this._menu = this.createSubmenu(null, DvtGraphZoomEvent.AXIS_Y2);

    if (this._menu != null)
        return ;

    // Create menu

    this._menu = new DvtZoomMenu(this._context, "", true, true);
        
    // Slightly different order based on vertical or horizontal
    // Render the x1Axis either before or after the y1/y2 axes.
    if (x1Axis && bVerticalChart) 
        this._menu.add(this.createSubmenu(this._resourceXAxis,  DvtGraphZoomEvent.AXIS_X1));
    if (y1Axis)
        this._menu.add(this.createSubmenu(this._resourceYAxis,  DvtGraphZoomEvent.AXIS_Y1));
    if (y2Axis)
        this._menu.add(this.createSubmenu(this._resourceY2Axis, DvtGraphZoomEvent.AXIS_Y2));
    if (x1Axis && !bVerticalChart)
        this._menu.add(this.createSubmenu(this._resourceXAxis,  DvtGraphZoomEvent.AXIS_X1));
         
    // Add the "Actual Size" option on the rootmenu  
    this._menu.addSeparator();
    var bActualSizeEnabled = (this._sAllActualJS != null);
    this._menu.add(new DvtZoomMenuItem(this._context, this._resourceActualSize, DvtGraphZoomEvent.ZOOM_ACTUAL_SIZE, bActualSizeEnabled, true, DvtGraphZoomEvent.AXIS_ALL));   
}
     

DvtZoomButton.prototype.createSubmenu = function(label, axis)
{      
    var submenu = new DvtZoomMenu(this._context, label, true, true, axis);

    // Enable/Disable zoom menu items depending on JS availability
    var bZoomInEnabled = false;
    var bZoomOutEnabled = false;
    if (axis == DvtGraphZoomEvent.AXIS_X1) {
        bZoomInEnabled = this._sX1InJS != null;
        bZoomOutEnabled = this._sX1OutJS != null;
    }
    else if (axis == DvtGraphZoomEvent.AXIS_Y1) {
        bZoomInEnabled = this._sY1InJS != null;
        bZoomOutEnabled = this._sY1OutJS != null;
    }
    else if (axis == DvtGraphZoomEvent.AXIS_Y2) {
        bZoomInEnabled = this._sY2InJS != null;
        bZoomOutEnabled = this._sY2OutJS != null
    }
       
    submenu.add(new DvtZoomMenuItem(this._context, this._resourceZoomIn, DvtGraphZoomEvent.ZOOM_IN, bZoomInEnabled, true, axis));
    submenu.add(new DvtZoomMenuItem(this._context, this._resourceZoomOut, DvtGraphZoomEvent.ZOOM_OUT, bZoomOutEnabled, true, axis));
    submenu.addSeparator();
    submenu.add(new DvtZoomMenuItem(this._context, this._resourceActualSize, DvtGraphZoomEvent.ZOOM_ACTUAL_SIZE, bZoomOutEnabled, true, axis));  
    
    return submenu;
}

DvtZoomButton.prototype.resetButton = function() {
    if (this._menuRenderer != null) {
            this._menuRenderer.destroy();
    }
    this._menuRenderer = null;

    if (!DvtAgent.isTouchDevice()) {
        if (this._isMouseOver) {
            this.drawOverState();
        } else {
            this.drawUpState();
        }
    } else {
        this.drawOverState();
    }
}

DvtZoomButton.prototype.drawUpState = function(){
    this._buttonState = DvtZoomButton.BUTTON_STATE_UP;
    if(!this._buttonBg){
        this._buttonBg = this._drawButtonBackground();
        this.addChild(this._buttonBg);
    }
    if (!this._mgGlass) {
        this._mgGlass = this._drawMagnifyingGlass(DvtZoomButton.MG_UP_BORDER_COLOR, 
                                                  DvtZoomButton.MG_UP_FILL_COLORS,
                                                  DvtZoomButton.MG_UP_FILL_ALPHAS,
                                                  DvtZoomButton.MG_UP_FILL_RATIOS,
                                                  DvtZoomButton.MG_UP_FOCAL_POINT);
        this.addChild(this._mgGlass);
    }
    this._buttonBg.setAlpha(0);
    this._mgGlass.setAlpha(1);      
    var buttonFill = new DvtLinearGradientFill(90, DvtZoomButton.UP_FILL_COLORS, DvtZoomButton.UP_FILL_ALPHAS);
    var buttonStroke = new DvtSolidStroke(DvtZoomButton.UP_BORDER_COLOR, 1);
    this._buttonBg.setFill(buttonFill);
    this._buttonBg.setStroke(buttonStroke);
    var mgGlassFill = new DvtRadialGradientFill(DvtZoomButton.MG_UP_FILL_COLORS, DvtZoomButton.MG_UP_FILL_ALPHAS);
    var mgGlassStroke = new DvtSolidStroke(DvtZoomButton.MG_UP_BORDER_COLOR, 1);
    this._mgGlass.setFill(mgGlassFill);
    this._mgGlass.setStroke(mgGlassStroke);
}

DvtZoomButton.prototype.drawOverState = function(){
    this._buttonState = DvtZoomButton.BUTTON_STATE_OVER;
    DvtGraphSysUtils.makeDetObject(this._buttonBg);
    this._bgDlo = this._buttonBg._Dlo;
    this._bgDlo.setTooltip(this._tooltip);
    var buttonFill = null;
    if (!DvtAgent.isTouchDevice()) {
        buttonFill = new DvtLinearGradientFill(90, DvtZoomButton.OVER_FILL_COLORS, DvtZoomButton.OVER_FILL_ALPHAS);
    } else {
        buttonFill = new DvtLinearGradientFill(90, DvtZoomButton.UP_FILL_COLORS, DvtZoomButton.UP_FILL_ALPHAS);
    }
    this._buttonBg.setFill(buttonFill);
    this._buttonBg.setAlpha(1);
    this._mgGlass.setAlpha(1);   
}

DvtZoomButton.prototype.drawDownState = function(){
    this._buttonState = DvtZoomButton.BUTTON_STATE_DOWN;
    this._context.getTooltipManager().hideTooltip();
    this._bgDlo.setTooltip(null);
    var buttonFill = new DvtLinearGradientFill(90, DvtZoomButton.DOWN_FILL_COLORS, DvtZoomButton.DOWN_FILL_ALPHAS);
    this._buttonBg.setFill(buttonFill);
}

/**
 * Draws the geometry for the background of the button.
*/ 
DvtZoomButton.prototype._drawButtonBackground = function() {
    // Start at top left, go clockwise.
    // This makes a shape like this:
    //   +---------- .
    //   |            \
    //   |            |
    //   \            |
    //    `-----------+
    // (top right and bottom left corners curved)
		
    // Above shape changes rounded corner location depending on if the application is in bidi locale or not
    var bidi = this.isFlipped();
    var cmds;
    if (!bidi) {
        cmds = DvtPathUtils.moveTo(0, 0) +
               DvtPathUtils.lineTo(DvtZoomButton.WIDTH - DvtZoomButton.CORNER_RADIUS, 0) +
               DvtPathUtils.cubicTo(DvtZoomButton.WIDTH, 0, DvtZoomButton.WIDTH, DvtZoomButton.CORNER_RADIUS, DvtZoomButton.WIDTH, DvtZoomButton.CORNER_RADIUS) +
               DvtPathUtils.lineTo(DvtZoomButton.WIDTH, DvtZoomButton.HEIGHT) +
               DvtPathUtils.lineTo(DvtZoomButton.CORNER_RADIUS, DvtZoomButton.HEIGHT) +
               DvtPathUtils.cubicTo(0, DvtZoomButton.HEIGHT, 0, DvtZoomButton.HEIGHT - DvtZoomButton.CORNER_RADIUS, 0, DvtZoomButton.HEIGHT - DvtZoomButton.CORNER_RADIUS) +
               DvtPathUtils.lineTo(0, 0);
    } else {
        cmds = DvtPathUtils.moveTo(DvtZoomButton.CORNER_RADIUS, 0) +
               DvtPathUtils.lineTo(DvtZoomButton.WIDTH, 0) +
               DvtPathUtils.lineTo(DvtZoomButton.WIDTH, DvtZoomButton.HEIGHT - DvtZoomButton.CORNER_RADIUS) + 
               DvtPathUtils.cubicTo(DvtZoomButton.WIDTH, DvtZoomButton.HEIGHT, DvtZoomButton.HEIGHT, DvtZoomButton.WIDTH - DvtZoomButton.CORNER_RADIUS, DvtZoomButton.HEIGHT, DvtZoomButton.WIDTH - DvtZoomButton.CORNER_RADIUS) +
               DvtPathUtils.lineTo(0, DvtZoomButton.HEIGHT) +
               DvtPathUtils.lineTo(0, DvtZoomButton.CORNER_RADIUS) +
               DvtPathUtils.cubicTo(0, 0, DvtZoomButton.CORNER_RADIUS, 0, DvtZoomButton.CORNER_RADIUS, 0);
    }
    DvtPathUtils.closePath();
    var buttonBg = new DvtPath(this._context, cmds);
    return buttonBg;
}

/**
 * Draws the geometry for the manifying glass object.
 */ 
DvtZoomButton.prototype._drawMagnifyingGlass = function(borderColor, fillColors, fillAlphas, fillRatios, focalPoint) {
    // There's quite a bit of voodoo magic in this method (i.e., magic constants) that
    // was the result of lots of trial and error. Before you try to modify this method, 
    // I'd see if you can do the same thing by manipulating the well-defined constants
    // at the top of this file.
    
    var xx = DvtZoomButton.WIDTH/2 + 2;
    var yy = DvtZoomButton.HEIGHT/3 +1;
    var mgGlass = new DvtCircle(this._context, xx, yy, DvtZoomButton.MAGNIFYING_GLASS_RADIUS);
    var cmds = DvtPathUtils.moveTo(xx-DvtZoomButton.MAGNIFYING_GLASS_RADIUS*1.8, yy+DvtZoomButton.MAGNIFYING_GLASS_RADIUS*1.8) +
               DvtPathUtils.lineTo(xx-DvtZoomButton.MAGNIFYING_GLASS_RADIUS/1.4, yy+DvtZoomButton.MAGNIFYING_GLASS_RADIUS/1.4) +
               DvtPathUtils.moveTo(0, 0);
    DvtPathUtils.closePath();
    var mgPath = new DvtPath(this._context, cmds);
    var mgPathStroke = new DvtSolidStroke(DvtZoomButton.MG_HANDLE_COLOR, 1);
    mgPathStroke.setWidth(2);
    mgPath.setStroke(mgPathStroke);
    mgGlass.addChild(mgPath);
    return mgGlass;
}

DvtZoomButton.prototype.hide = function() {
    if (this._buttonSHState != DvtZoomButton.BUTTON_HIDE) {
        
        this.resetButton();
        this._buttonBg.setAlpha(0);
        this._mgGlass.setAlpha(0);
        this._buttonSHState = DvtZoomButton.BUTTON_HIDE;
    }
}

DvtZoomButton.prototype.show = function() {
    if (this._buttonSHState != DvtZoomButton.BUTTON_SHOW) {
        this.resetButton();
        this._mgGlass.setAlpha(1); 
        this._buttonSHState = DvtZoomButton.BUTTON_SHOW
    } 
}

DvtZoomButton.prototype.isFlipped = function() {
    return this._flipped;
}

/**
 * @return {Number} Returns the zoom button's state
 */
DvtZoomButton.prototype.getButtonState = function() {
    return this._buttonState;
}

/**
 * Handle a keyboard event to show, hide, and select menus displayed by the zoom button
 * @param {DvtKeyboardEvent} event
 */
DvtZoomButton.prototype.processKeyboardEvent = function(event) 
{
  var key = event.keyCode;
  var currentMenuItemRenderer;

  if(key == DvtKeyboardEvent.ENTER)
  {
    if(!this._curMenuRendererContents)  
    {
      // if the zoom menu is not currently being shown
      // show the menu and highlight the first menu item
      this.processClick(null);
      
      this._curMenuRendererContents = this._menuRenderer.__getContents();
      this._curRendererIdx = this._getNextRendererIndex(this._curMenuRendererContents,
                                                        -1,
                                                        true);
      currentMenuItemRenderer = this._curMenuRendererContents.getChildAt(this._curRendererIdx);
      currentMenuItemRenderer.drawOverState();
    }
    else
    {
      // fire the click event on the current item
      currentMenuItemRenderer = this._curMenuRendererContents.getChildAt(this._curRendererIdx);
      currentMenuItemRenderer.processClick();
    }
  }
  else if(key == DvtKeyboardEvent.UP_ARROW || key == DvtKeyboardEvent.DOWN_ARROW)
  {
    // highlight the next menu item
    var searchForward = key == DvtKeyboardEvent.DOWN_ARROW ? true : false;
  
    currentMenuItemRenderer = this._curMenuRendererContents.getChildAt(this._curRendererIdx);
    currentMenuItemRenderer.drawBaseState();

    this._curRendererIdx = this._getNextRendererIndex(this._curMenuRendererContents,
                                                      this._curRendererIdx,
                                                      searchForward);
    currentMenuItemRenderer = this._curMenuRendererContents.getChildAt(this._curRendererIdx);
    currentMenuItemRenderer.drawOverState();
  }
  else if(key == DvtKeyboardEvent.LEFT_ARROW || key == DvtKeyboardEvent.RIGHT_ARROW)
  {
    var isLeftArrow = (key == DvtKeyboardEvent.LEFT_ARROW) ? true : false;
    
    // figure out if we are showing a submenu or hiding the current one
    var expandSubmenu = (DvtAgent.isRightToLeft() && isLeftArrow) || (!DvtAgent.isRightToLeft() && !isLeftArrow) ?
                        true : false;

    if(expandSubmenu)
    {
      // expand the submenu if we are showing the top-level menu and if the current menu item is a submenu
      if(this._curMenuRendererContents)
      {
        currentMenuItemRenderer = this._curMenuRendererContents.getChildAt(this._curRendererIdx);
        if(currentMenuItemRenderer instanceof DvtZoomSubmenuRenderer)
        {
          this._prevMenuRendererContents = this._curMenuRendererContents;
          this._prevRendererIdx = this._curRendererIdx;
          
          currentMenuItemRenderer.showMenu();
          
          this._curMenuRendererContents = currentMenuItemRenderer.__getSubmenuRenderer().__getContents();
          this._curRendererIdx = this._getNextRendererIndex(this._curMenuRendererContents, 
                                                            -1,
                                                            true);
          currentMenuItemRenderer = this._curMenuRendererContents.getChildAt(this._curRendererIdx);
          currentMenuItemRenderer.drawOverState();          
        }
      }
    }
    else
    {
      // if focus is on a submenu, hide this submenu and restore focus to the previous menu item 
      // in the parent menu
      if(this._prevMenuRendererContents)
      {
        this._hideCurrentMenu();
      } 
    }
  }
  else if(key == DvtKeyboardEvent.ESCAPE )
  {
    // hide the currently displayed menu, regardless of whether it is a top level menu or a submenu 
    if(this._curMenuRendererContents)
    {
      this._hideCurrentMenu();
    }
  }    
}

/**
 * Private helper function to hide the current menu and restore focus to the menu item in the parent
 * menu, if necessary
 */
DvtZoomButton.prototype._hideCurrentMenu = function()
{
  this._curMenuRendererContents = this._prevMenuRendererContents;
  this._curRendererIdx = this._prevRendererIdx;
  
  this._prevMenuRendererContents = null;
  this._prevRendererIdx = -1;
  
  if(this._curMenuRendererContents)
  {
    // hiding submenu
    currentZoomRenderer = this._curMenuRendererContents.getChildAt(this._curRendererIdx);
    currentZoomRenderer.hideSubmenu();
    currentZoomRenderer.drawOverState();
  }
  else
  {
    // hiding top-level menu
    this._menuRenderer.hideMenu();
    this.drawOverState();
  }
}

/**
 * Returns the index of the next zoom renderer
 * @param {DvtRect} contents 
 * @param {Number} curIndex the index of the current menu item renderer (either a DvtZoomSubmenuRenderer
 *                 or a DvtZoomMenuItemRenderer)
 * @param {Boolean} searchForward true if we want to return the index of the next renderer, false if want
 *                 to return the index of the previous renderer
 * @return {Number} the index of the next zoom renderer, or curIndex if there are no other renderers in 
 *                 the direction specified by searchForward
 * @private
 */
DvtZoomButton.prototype._getNextRendererIndex = function(contents, curIndex, searchForward)
{
  if (contents) 
  {
    var child;
    var numChildren = contents.getNumChildren();
    var increment = searchForward ? 1 : -1;
    
    // adjust the starting loop counter, based on which direction we are searching
    var i = searchForward ? (curIndex + 1) : (curIndex - 1);
    
    for ( i;
          searchForward ? (i < numChildren) : (i >= 0); 
          i = i + increment ) {
      child = contents.getChildAt(i);
            
      if (child instanceof DvtZoomSubmenuRenderer || (child instanceof DvtZoomMenuItemRenderer && child.__isEnabled()) ) {
        return i;
      }
    }
  }
  
  return curIndex;
}

/**
 * Creates if necessary and returns a touch manager object
 * @return {DvtTouchManager} touch manager object
 */
DvtZoomButton.prototype.getTouchManager = function () {
  if (!this._touchManager && DvtAgent.isTouchDevice())
    this._touchManager = new DvtTouchManager("_touchManager_zoombutton", this._context);
  return this._touchManager;  
}
/**
 * @extends DvtBaseComponentEvent
 * @class DvtGraphZoomEvent
 * @constructor
 */
var DvtGraphZoomEvent = function(axis, type, bubbles, cancelable) {
    this.Init(axis, type, bubbles, cancelable);
}

DvtObj.createSubclass(DvtGraphZoomEvent, DvtBaseComponentEvent, "DvtGraphZoomEvent");

DvtGraphZoomEvent.ZOOM = "rangeSliderZoom"; 

DvtGraphZoomEvent.AXIS_ALL = 0;
DvtGraphZoomEvent.AXIS_X1 = 1;
DvtGraphZoomEvent.AXIS_Y1 = 2;
DvtGraphZoomEvent.AXIS_Y2 = 3;

DvtGraphZoomEvent.ZOOM_IN = "Zoom In";
DvtGraphZoomEvent.ZOOM_OUT = "Zoom Out";
DvtGraphZoomEvent.ZOOM_ACTUAL_SIZE = "Actual Size";


/**
 * Initializer
 * @protected
 */
DvtGraphZoomEvent.prototype.Init = function(axis, type, bubbles, cancelable) {

    DvtGraphZoomEvent.superclass.Init.call(this, type);

    this._axis = axis;
    this._zoomType = type;
}

DvtGraphZoomEvent.prototype.getAxis = function() {
    return this._axis;
}

DvtGraphZoomEvent.prototype.getZoomType = function() {
    return this._zoomType;
}

/*--------------------------------------------------------------------*/
/*   DvtZoomMenuItem   Scrollbar Zoom Menu Item Component             */
/*--------------------------------------------------------------------*/
/**
  *  Scrollbar Zoom Menu Item Component.
  *  @extends DvtObj
  *  @class DvtZoomMenuItem scrollbar zoom menu item component. 
  *  @constructor  
  */
var DvtZoomMenuItem = function(context, sLabel, sEventType, bEnabled, bVisible, id) {
    this.Init(context, sLabel, sEventType, bEnabled, bVisible, id);
}

DvtObj.createSubclass(DvtZoomMenuItem, DvtObj, "DvtZoomMenuItem");


DvtZoomMenuItem.prototype.Init = function(context, sLabel, sEventType, bEnabled, bVisible, id) {
    this._context = context;
    this._sLabel = sLabel;          // menu item text
    this._sEventType = sEventType;  // event type
    this._bEnabled = bEnabled;      // true == menu item is enabled
    this._bVisible = bVisible;
    this._id = id;                  // associated ID
    this._userContext = null;       // optional user supplied context
}



DvtZoomMenuItem.prototype.getId = function() {
    return this._id;
}

DvtZoomMenuItem.prototype.getUserContext = function() {
    return this._userContext;
}

DvtZoomMenuItem.prototype.setUserContext = function(obj) {
    this._userContext = obj;
}

DvtZoomMenuItem.prototype.isEnabled = function() {
    return this._bEnabled;
}

DvtZoomMenuItem.prototype.setEnabled = function(bEnabled) {
    this._bEnabled = bEnabled;
}

DvtZoomMenuItem.prototype.getLabel = function() {
    return this._sLabel;
}

DvtZoomMenuItem.prototype.setLabel = function(sLabel) {
    this._sLabel = sLabel;
}

DvtZoomMenuItem.prototype.getEventType = function() {
    return this._sEventType;
}

DvtZoomMenuItem.prototype.setEventType = function(sEventType) {
    this._sEventType = sEventType;
}

DvtZoomMenuItem.prototype.getVisible = function() {
    return this._bVisible;
}

DvtZoomMenuItem.prototype.setVisible = function(bVisible) {
    this._bVisible = bVisible;
}

/*--------------------------------------------------------------------*/
/*   DvtZoomMenu   Scrollbar Zoom Menu Component                      */
/*--------------------------------------------------------------------*/
/**
  *  Scrollbar Zoom Menu Component.
  *  @extends DvtZoomMenuItem
  *  @class DvtZoomMenu scrollbar zoom menu component. 
  *  @constructor  
  */
var DvtZoomMenu = function(context, sLabel, bEnabled, bVisible, id) {
    this.Init(context, sLabel, bEnabled, bVisible, id);
}

DvtObj.createSubclass(DvtZoomMenu, DvtZoomMenuItem, "DvtZoomMenu");

// Constants
DvtZoomMenu.HANDLED = 0;
DvtZoomMenu.SEPARATOR = "-";

DvtZoomMenu.prototype.Init = function(context, sLabel, bEnabled, bVisible, id) {
    DvtZoomMenu.superclass.Init.call(this, context, sLabel, "", bEnabled, bVisible, id);
    this._arMenuItems = [];
}



DvtZoomMenu.prototype.add = function(item) {
    this._arMenuItems.push(item);
}

DvtZoomMenu.prototype.addSeparator = function() {
    this._arMenuItems.push(DvtZoomMenu.SEPARATOR);
}

DvtZoomMenu.prototype.getArMenuItems = function() {
    return this._arMenuItems;
}
/*----------------------------------------------------------------------------*/
/*   DvtZoomMenuItemRenderer   Scrollbar Zoom MenuItem Renderer Component     */
/*----------------------------------------------------------------------------*/
/**
  *  Scrollbar Zoom MenuItem Renderer Component.
  *  @extends DvtContainer
  *  @class DvtZoomMenuItemRenderer scrollbar zoom menuitem renderer component. 
  *  @constructor  
  */
var DvtZoomMenuItemRenderer = function(context, zoomMenuRenderer, zoomMenuItem, w, h, flipped, callback, callbackObj, id) {
    this.Init(context, zoomMenuRenderer, zoomMenuItem, w, h, flipped, callback, callbackObj, id);
}

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

// Constants
DvtZoomMenuItemRenderer.PADDING_TOP = 2;   
DvtZoomMenuItemRenderer.PADDING_LEFT = 21;  //25
DvtZoomMenuItemRenderer.PADDING_RIGHT = 7;

DvtZoomMenuItemRenderer.FONT_FAMILY = "Tahoma";
DvtZoomMenuItemRenderer.FONT_SIZE = 11;
DvtZoomMenuItemRenderer.FONT_COLOR = "#000000";
DvtZoomMenuItemRenderer.DISABLED_FONT_COLOR = "#a2acc7";
DvtZoomMenuItemRenderer.BACKGROUND_COLOR = "#ffffff";
DvtZoomMenuItemRenderer.BACKGROUND_COLOR_OVER = "#c1e6ff";

DvtZoomMenuItemRenderer.prototype.Init = function(context, zoomMenuRenderer, zoomMenuItem, w, h, flipped, callback, callbackObj, id) {
    this._context = context;
    var container = new DvtContainer(context, "zoomMenuItemRenderer" + id);
    var impl = container.getImpl();
    DvtZoomMenuItemRenderer.superclass.Init.call(this, context, impl);
    this._w = w;
    this._h = h;
    this._zoomMenuRenderer = zoomMenuRenderer;
    this._item = zoomMenuItem;
    this._flipped = flipped;
    this._enabled = zoomMenuItem.isEnabled();
    this._callback = callback;
    this._callbackObj = callbackObj;
    this._bg = new DvtRect(context, 0, 0, w, h);
    var bgfill = new DvtSolidFill(DvtZoomMenuItemRenderer.BACKGROUND_COLOR, 1);
    this._bg.setFill(bgfill);
    this.addChild(this._bg);
    
    var labelTxt = zoomMenuItem.getLabel();

    // create text label
    var label = new DvtText(this._context, labelTxt, DvtZoomMenuItemRenderer.PADDING_LEFT, DvtZoomMenuItemRenderer.PADDING_TOP);
    var fill;
    if (zoomMenuItem.isEnabled()) {
        fill = new DvtSolidFill(DvtZoomMenuItemRenderer.FONT_COLOR, 1);
    } else {
        fill = new DvtSolidFill(DvtZoomMenuItemRenderer.DISABLED_FONT_COLOR, 1);
    }
    // There is a bug in IE9 with aligning to top in SVG.
    // Wait until a feasible fix is in the toolkit for alignTop API.  For now the baseline is used.
    label.alignBaseline();
    label.setTranslateY(DvtZoomMenuItemRenderer.FONT_SIZE);

    label.setFill(fill);
    label.setFontSize(DvtZoomMenuItemRenderer.FONT_SIZE);
    label.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
    this.addChild(label);
    
    this.drawBaseState();
    
    if (this._enabled) {
        if (!DvtAgent.isTouchDevice()) {
            this.addEvtListener(DvtMouseEvent.MOUSEOUT, this.mouseOutHandler, false, this);
            this.addEvtListener(DvtMouseEvent.MOUSEOVER, this.mouseOverHandler, false, this);
        } else {
            this.addEvtListener(DvtTouchEvent.TOUCHSTART, this._touchHandler, false, this);
            this.addEvtListener(DvtTouchEvent.TOUCHMOVE, this._touchHandler, false, this);
            this.addEvtListener(DvtTouchEvent.TOUCHEND, this._touchHandler, false, this);
        }
    }
    if (!DvtAgent.isTouchDevice()) {
        this.addEvtListener(DvtMouseEvent.CLICK, this.clickHandler, false, this);
    }
}



DvtZoomMenuItemRenderer.prototype.processClick = function() {
    if (this._enabled) {
      // zoom event call back
      var zoomEvent = new DvtGraphZoomEvent(this._item.getId(), this._item.getEventType());
      this._callback.call(this._callbackObj, zoomEvent);
      //dispatchEvent(new DvtGraphZoomEvent(this._item.getId(), this._item.getEventType()));
      
      // Hiding the zoom menu and resetting the button. Need to reset the _isMouseOver flag for the button so it renders normally.
      // We definitely know the mouse is not over the button if we're processing the menu item click
      this._callbackObj._isMouseOver = false; 
      this._callbackObj.resetButton();
    }
}
        
DvtZoomMenuItemRenderer.prototype.clickHandler = function(event) {
    this.processClick();
    event.stopPropagation();
}

DvtZoomMenuItemRenderer.prototype.mouseOverHandler = function(event) {
    this.drawOverState();
}

DvtZoomMenuItemRenderer.prototype.mouseOutHandler = function(event) {
    this.drawBaseState();
}

DvtZoomMenuItemRenderer.prototype.drawBaseState = function() {
    if (this._bg) {
        var fill = new DvtSolidFill(DvtZoomMenuItemRenderer.BACKGROUND_COLOR, 1);
        this._bg.setFill(fill);
    } 
}

DvtZoomMenuItemRenderer.prototype.drawOverState = function() {
    if (this._bg) {
        var overFill = new DvtSolidFill(DvtZoomMenuItemRenderer.BACKGROUND_COLOR_OVER, 1);
        this._bg.setFill(overFill);
    }
}

/**
 * Processes "touchstart" event
 * @param {DvtTouchEvent} event Touch event
 * @param {DvtTouch} touch Touch object
 */
DvtZoomMenuItemRenderer.prototype.immediateTouchAttempted = function(event, touch) {
    var tm = this.getTouchManager();
    tm.saveProcessedTouchDefault(touch.identifier, this, this, "zoomButton", "zoomButton", this);
    this.drawOverState();
    event.blockTouchHold();
}

DvtZoomMenuItemRenderer.prototype.immediateTouchMoved = function(event, touch) {
    event.preventDefault();
}

DvtZoomMenuItemRenderer.prototype.immediateTouchEnded = function(event, touch) {
    this.drawBaseState();
    this.processClick();
    event.stopPropagation();
}

/**
 * Handler for the touch event
 * @param {DvtTouchEvent} event Touch event
 * @private
 */
DvtZoomMenuItemRenderer.prototype._touchHandler = function(event) {
  this.getTouchManager().preEventBubble(event);
  this.getTouchManager().processAssociatedTouchDefault(event, this);
  this.getTouchManager().postEventBubble(event);
}

DvtZoomMenuItemRenderer.prototype.__isEnabled = function() {
  return this._enabled;
}

/**
 * Creates if necessary and returns a touch manager object
 * @return {DvtTouchManager} touch manager object
 */
DvtZoomMenuItemRenderer.prototype.getTouchManager = function () {
  if (!this._touchManager && DvtAgent.isTouchDevice())
    this._touchManager = new DvtTouchManager("_touchManager_zoomitem", this._context);
  return this._touchManager;  
}
/*--------------------------------------------------------------------*/
/*   DvtZoomMenuRenderer   Scrollbar Zoom Menu Renderer Component     */
/*--------------------------------------------------------------------*/
/**
  *  Scrollbar Zoom Menu Renderer Component.
  *  @extends DvtContainer
  *  @class DvtZoomMenuRenderer scrollbar zoom menu renderer component. 
  *  @constructor  
  */
var DvtZoomMenuRenderer = function(context, zoomMenu, flipped, callback, callbackObj, id) {
    this.Init(context, zoomMenu, flipped, callback, callbackObj, id);
}

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

// Constants
DvtZoomMenuRenderer.PADDING = 1;        // padding in pixels   
DvtZoomMenuRenderer.SEPARATOR_PADDING = 2 * DvtZoomMenuRenderer.PADDING; // addition padding in pixels for sep
      
DvtZoomMenuRenderer.MIN_WIDTH         = 120;         // the min width of the menu
DvtZoomMenuRenderer.ITEM_HEIGHT       = 20;
DvtZoomMenuRenderer.SEPARATOR_HEIGHT  = 3;
      
DvtZoomMenuRenderer.BORDER_COLOR_TOP    = "#A9B6CB";
DvtZoomMenuRenderer.BORDER_COLOR_BOTTOM = "#00000A";
DvtZoomMenuRenderer.BORDER_COLOR_SIDES  = "#768092";
DvtZoomMenuRenderer.SEPARATOR_COLOR     = "#ADB9CD";

DvtZoomMenuRenderer.prototype.Init = function(context, zoomMenu, flipped, callback, callbackObj, id) {
    if (zoomMenu == null || zoomMenu.getArMenuItems() == null) {
        console.log("Null or Empty Menu Received");
        return;
    }
    var container = new DvtContainer(context, "zoomMenuRenderer" + id);
    var impl = container.getImpl();
    DvtZoomMenuRenderer.superclass.Init.call(this, context, impl);
    
    this._context = context;    
    this._flipped = flipped;
    this._callback = callback;
    this._callbackObj = callbackObj;
    this._contents = this._renderMenuContents(zoomMenu);
    
    this._contents.setTranslateX(DvtZoomMenuRenderer.PADDING);
    this._contents.setTranslateY(DvtZoomMenuRenderer.PADDING);
    this._w = this._contents.getWidth() + DvtZoomMenuRenderer.PADDING;  // no padding needed on the right
    this._h = this._contents.getHeight() + 2*DvtZoomMenuRenderer.PADDING;
    
    this.__bg = new DvtRect(context, 0, 0, this._w, this._h);
    
    var fill = new DvtSolidFill("#FFFFFF", 1);
    // Change this if we want borders with different color
    var stroke = new DvtSolidStroke(DvtZoomMenuRenderer.BORDER_COLOR_SIDES, 1);
    
    this._contents.setFill(fill);
    this._contents.setStroke(stroke);
    this.__bg.setFill(fill);
    this.addChild(this.__bg);
    this.addChild(this._contents);
    
    this.setTranslateY(-this._h);
    
    if (!DvtAgent.isTouchDevice()) {
        this.addEvtListener(DvtMouseEvent.CLICK, this.clickHandler, false, this);
    } else {
        this.addEvtListener(DvtTouchEvent.TOUCHSTART, this._touchHandler, false, this);
        this.addEvtListener(DvtTouchEvent.TOUCHMOVE, this._touchHandler, false, this);
        this.addEvtListener(DvtTouchEvent.TOUCHEND, this._touchHandler, false, this);
    }
}

DvtZoomMenuRenderer.prototype.destroy = function() {
    if (this._bDestroyed)
    return;
    
    DvtZoomMenuRenderer.superclass.destroy.call(this) ;

    if (!DvtAgent.isTouchDevice()) {
        this.removeEvtListener(DvtMouseEvent.CLICK, this.clickHandler);
    } else {
        this.removeEvtListener(DvtTouchEvent.TOUCHSTART, this._touchHandler);
        this.removeEvtListener(DvtTouchEvent.TOUCHMOVE, this._touchHandler);
        this.removeEvtListener(DvtTouchEvent.TOUCHEND, this._touchHandler);
    }
    
    // call destroy on all child menu item renderers
    var child;
    for (var i = 0; i < this.getNumChildren(); i++) {
        child = this.getChildAt(i);
        if (child instanceof DvtZoomMenuItemRenderer) {
            child.destroy();
        }
    }
    this.removeChild(this._contents);
    if (this.getParent() != null) {
        this.getParent().removeChild(this);
    }
    this._bDestroyed = true;
}

DvtZoomMenuRenderer.prototype._renderMenuContents = function(zoomMenu) {
    var contents = new DvtRect(this._context, 0, 0);
    var items = zoomMenu.getArMenuItems();
    var w = DvtZoomMenuRenderer.MIN_WIDTH;      // keep track of the max
    var separatorCoords = [];                   // keep track of y coords of all separators
    var curry = 0;

    // iterate through the list of items, which are composed of MenuItems and Separators
    for (var i = 0; i<items.length; i++) {
        if (items[i] == DvtZoomMenu.SEPARATOR) {
            // draw a separator
            separatorCoords.push(curry + DvtZoomMenuRenderer.SEPARATOR_HEIGHT/2);
            curry += DvtZoomMenuRenderer.SEPARATOR_HEIGHT;
        } else {
            var menuItem = items[i];
            var item;
            
            if (menuItem instanceof DvtZoomMenu) {
                item = new DvtZoomSubmenuRenderer(this._context, this, menuItem, DvtZoomMenuRenderer.MIN_WIDTH, DvtZoomMenuRenderer.ITEM_HEIGHT, this._flipped, this._callback, this._callbackObj);   
            } else {
                item = new DvtZoomMenuItemRenderer(this._context, this, menuItem, DvtZoomMenuRenderer.MIN_WIDTH, DvtZoomMenuRenderer.ITEM_HEIGHT, this._flipped, this._callback, this._callbackObj);  
            }
            item.setTranslateX(0);
            item.setTranslateY(curry);
            
            // Add as child, we will iterate through all children and tell them the width later
            contents.addChild(item);
            
            // if this item is longer than the max width so far, keep track
            //w = Math.max(w, item.getMinWidth());
            curry += DvtZoomMenuRenderer.ITEM_HEIGHT;
        }
    }
   
    // Draw all separators
    for (var i = 0; i < separatorCoords.length; i++) {
        // first draw the background then the separator itself
        var rect = new DvtRect(this._context, 0, separatorCoords[i] - DvtZoomMenuRenderer.SEPARATOR_HEIGHT/2, DvtZoomMenuItemRenderer.PADDING_LEFT - 1, DvtZoomMenuRenderer.SEPARATOR_HEIGHT);
        
        var sep = new DvtLine(this._context, DvtZoomMenuItemRenderer.PADDING_LEFT + DvtZoomMenuRenderer.SEPARATOR_PADDING, separatorCoords[i], w, separatorCoords[i]);
        
        var fill = new DvtSolidFill(DvtZoomMenuItemRenderer.BACKGROUND_COLOR, 1);
        var stroke = new DvtSolidStroke(DvtZoomMenuRenderer.SEPARATOR_COLOR, 1);
        
        rect.setFill(fill);
        sep.setStroke(stroke);
        contents.addChild(rect);
        contents.addChild(sep);
    }
    contents.setWidth(w);
    contents.setHeight(curry);
    return contents;
}

DvtZoomMenuRenderer.prototype.processClick = function(target) {
    var isThisOrChild = (target instanceof DvtDisplayable && this.contains(target));
    var isThis = (target == this);
    // if target is not this menu or its child, then hide the menu
    // defer to menu items for hiding this menu when they are clicked
    if (!isThisOrChild) {
        this.hideMenu();
    } else if (isThis) {
        // if the click was on this menu, we hide all submenus and eat the event
        // so the submenus don't react to it
        this.hideAllSubmenus();
        return true;
    }
    return false;
}

DvtZoomMenuRenderer.prototype.clickHandler = function(event) {
    var target = event.target;
    var ret = this.processClick(target);
    if (ret)    
        event.stopPropagation();
}

/**
 * Processes "touchstart" event
 * @param {DvtTouchEvent} event Touch event
 * @param {DvtTouch} touch Touch object
 */
DvtZoomMenuRenderer.prototype.immediateTouchAttempted = function(event, touch) {
    var tm = this.getTouchManager();
    tm.saveProcessedTouchDefault(touch.identifier, this, this, "zoomButton", "zoomButton", this);
    event.blockTouchHold();
}

DvtZoomMenuRenderer.prototype.immediateTouchMoved = function(event, touch) {
    event.preventDefault();
}

DvtZoomMenuRenderer.prototype.immediateTouchEnded = function(event, touch) {
    var ret = this.processClick(event.target);
    if (ret)    
        event.stopPropagation();
}

/**
 * Handler for the touch event
 * @param {DvtTouchEvent} event Touch event
 * @private
 */
DvtZoomMenuRenderer.prototype._touchHandler = function(event) {
  this.getTouchManager().preEventBubble(event);
  this.getTouchManager().processAssociatedTouchDefault(event, this);
  this.getTouchManager().postEventBubble(event);
}

DvtZoomMenuRenderer.prototype.hideMenu = function() {
    var parent = this.getParent();
    // If parent is another menu, then hide it too
    if (parent != null) {
        if (parent instanceof DvtZoomSubmenuRenderer) {
            parent.hideSubmenu();
        } else if (parent instanceof DvtZoomButton) {
            parent.resetButton();
        }
    }
    // Hide this menu and all submenus
    this.destroy();

}

DvtZoomMenuRenderer.prototype.hideAllSubmenus = function() {
    var submenuHidden = false;
    if (this._contents != null) {
        var child;
        for (var i = 0; i < this._contents.getNumChildren(); i++) {
            child = this._contents.getChildAt(i);
            
            if (child instanceof DvtZoomSubmenuRenderer) {
                child.hideSubmenu();
                submenuHidden = true;
            }
        }
    }
    return submenuHidden;
}

/** 
 * Returns the contents of the menu
 * @return {DvtRect}
 */
DvtZoomMenuRenderer.prototype.__getContents = function() {
  return this._contents;
}

/**
 * Creates if necessary and returns a touch manager object
 * @return {DvtTouchManager} touch manager object
 */
DvtZoomMenuRenderer.prototype.getTouchManager = function () {
  if (!this._touchManager && DvtAgent.isTouchDevice())
    this._touchManager = new DvtTouchManager("_touchManager_zoommenu", this._context);
  return this._touchManager;  
}
/*--------------------------------------------------------------------------*/
/*   DvtZoomSubmenuRenderer   Scrollbar Zoom Submenu Renderer Component     */
/*--------------------------------------------------------------------------*/
/**
  *  Scrollbar Zoom Submenu Renderer Component.
  *  @extends DvtZoomMenuItemRenderer
  *  @class DvtZoomSubmenuRenderer scrollbar zoom submenu renderer component. 
  *  @constructor  
  */
var DvtZoomSubmenuRenderer = function(context, zoomMenuRenderer, submenu, w, h, flipped, callback, callbackObj, id) {
    this.Init(context, zoomMenuRenderer, submenu, w, h, flipped, callback, callbackObj, id);
}

DvtObj.createSubclass(DvtZoomSubmenuRenderer, DvtZoomMenuItemRenderer, "DvtZoomSubmenuRenderer");

// Constants
DvtZoomSubmenuRenderer.ARROW_WIDTH = 4;
DvtZoomSubmenuRenderer.ARROW_HEIGHT = 6;
DvtZoomSubmenuRenderer.menuDisplayDelay = 300;

DvtZoomSubmenuRenderer.prototype.Init = function(context, zoomMenuRenderer, submenu, w, h, flipped, callback, callbackObj, id) {
    DvtZoomSubmenuRenderer.superclass.Init.call(this, context, zoomMenuRenderer, submenu, w, h, flipped, callback, callbackObj, id);
    // Cache the menu info
    this._submenu = submenu;
    this._submenuRenderer = null;
    this._callback = callback;
    this._callbackObj = callbackObj;
    this.drawExpandArrow();
    // Create a new timer
    
    if (DvtAgent.isTouchDevice()) {
        DvtZoomSubmenuRenderer.menuDisplayDelay = 0;
    }
    this._menuDisplayTimer = new DvtTimer(context, DvtZoomSubmenuRenderer.menuDisplayDelay, this.onTimer, this, 1);
}

DvtZoomSubmenuRenderer.prototype.isSubmenuVisible = function() {
    if (this._submenuRenderer != null) {
        return true;
    } else {
        return false;
    }
}

DvtZoomSubmenuRenderer.prototype.destroy = function() {

   this._menuDisplayTimer = null;
   
   if (this._submenuRenderer != null) {
       this._submenuRenderer.destroy();
       this._submenuRenderer = null;
   }
}

DvtZoomSubmenuRenderer.prototype.hideSubmenu = function() {
    if (this._submenuRenderer != null) {
        this._submenuRenderer.destroy();
        this._submenuRenderer = null;
    }
}
DvtZoomSubmenuRenderer.prototype.showMenu = function() {
    // First hide all other submenus
    this._zoomMenuRenderer.hideAllSubmenus(null);
    
    // Find the global point of the right side of this item
    // +1 for the border width
    this._submenuRenderer = new DvtZoomMenuRenderer(this._context, this._submenu, this._flipped, this._callback, this._callbackObj);
   
    if (!this._flipped) {
        this._submenuRenderer.setTranslateX(this._w + 1);
    } else {
        this._submenuRenderer.setTranslateX(-this._w - 4);
    }
    this._submenuRenderer.setTranslateY(this._h - this._submenuRenderer._h);
    this.addChild(this._submenuRenderer);
    
    //Add drop shadow to submenu
    var angle = 45; 
    if (this._flipped) {
        angle = 135;
    }
    var shadowRGBA = DvtColorUtils.makeRGBA(00,00,00, 0.45);

    var shadow =  new DvtShadow(shadowRGBA, 
                              5, // distance
                              7, // blurX
                              7, // blurY
                              angle, // angle of the shadow
                              1, // strength or the imprint/spread
                              3, // quality
                              false, // inner shadow
                              false, // knockout effect
                              false // hide object
                              );
    this._submenuRenderer.__bg.addDrawEffect(shadow);
}

DvtZoomSubmenuRenderer.prototype.processClick = function() {
    if (this._enabled) {
        if (this._submenuRenderer == null) {
            this.showMenu();
        } else {
            this.hideSubmenu();
        }
    }
    this._menuDisplayTimer.stop();
}

DvtZoomSubmenuRenderer.prototype.clickHandler = function(event) {
    this.processClick();
    event.stopPropagation();
}

DvtZoomSubmenuRenderer.prototype.mouseOverHandler = function(event) {
    // This event only applies to the submenu menu item, not the submenu
    //if (event.target != this) 
    // return;
    
    DvtZoomSubmenuRenderer.superclass.mouseOverHandler.call(this, event);
    
    this._menuDisplayTimer.start();
}

DvtZoomSubmenuRenderer.prototype.mouseOutHandler = function(event) {

    DvtZoomSubmenuRenderer.superclass.mouseOutHandler.call(this, event);
    
    this._menuDisplayTimer.stop();
}

DvtZoomSubmenuRenderer.prototype.onTimer = function() {
    // show the menu when timer is reached if mouse still over this object
    if (this._submenuRenderer == null) {
        this.showMenu();
    }
}

DvtZoomSubmenuRenderer.prototype.drawBaseState = function() {
    //call super class
    DvtZoomSubmenuRenderer.superclass.drawBaseState.call(this);
    // Draw the arrow indicating this is a submenu
    this.drawExpandArrow();
}

DvtZoomSubmenuRenderer.prototype.drawOverState = function() {
    //call super class
    DvtZoomSubmenuRenderer.superclass.drawOverState.call(this);
    // Draw the arrow indicating this is a submenu
    this.drawExpandArrow();
}

DvtZoomSubmenuRenderer.prototype.drawExpandArrow = function() {
    var cmds = DvtPathUtils.moveTo(-DvtZoomSubmenuRenderer.ARROW_WIDTH/2, -DvtZoomSubmenuRenderer.ARROW_HEIGHT/2) + 
               DvtPathUtils.lineTo(DvtZoomSubmenuRenderer.ARROW_WIDTH/2, 0) +
               DvtPathUtils.lineTo(-DvtZoomSubmenuRenderer.ARROW_WIDTH/2, DvtZoomSubmenuRenderer.ARROW_HEIGHT/2) +
               DvtPathUtils.lineTo(-DvtZoomSubmenuRenderer.ARROW_WIDTH/2, -DvtZoomSubmenuRenderer.ARROW_HEIGHT/2);
    DvtPathUtils.closePath();
    var arrow = new DvtPath(this._context, cmds);
    var fill = new DvtSolidFill("#000000", 1);
    arrow.setFill(fill);
    
    var arrowX = this._w - 5 - DvtZoomSubmenuRenderer.ARROW_WIDTH/2;
    var arrowY = this._h/2;
    
    if (this._flipped) {
        arrowX = 0 + 5 + DvtZoomSubmenuRenderer.ARROW_WIDTH/2;
        // Rotate 180 degrees
        arrow.setRotation(Math.PI);
    }
    arrow.setTranslateX(arrowX);
    arrow.setTranslateY(arrowY);
    this.addChild(arrow);             
}

/**
 * @return {DvtZoomMenuRenderer} The renderer used to render this submenu
 */
DvtZoomSubmenuRenderer.prototype.__getSubmenuRenderer = function() {
    return this._submenuRenderer
}
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. 
// All rights reserved. 

/*---------------------------------------------------------------------------*/
/*   DvtDialIndicator  Represents the indicator component in a dial guage    */
/*---------------------------------------------------------------------------*/

/**
 *  @class DvtDialIndicator
 *  @extends DvtPath
 *  @constructor
 */  
var  DvtDialIndicator = function(context)
{
   this.Init(context) ;
};

DvtObj.createSubclass(DvtDialIndicator, DvtPath, "DvtDialIndicator") ;


/*--------------------------------------------------------------------*/
/*  doInitFx()                                                        */
/*--------------------------------------------------------------------*/
/**
  *    Perform initial effects. Called by DvtDetObj on its first render
  */
DvtDialIndicator.prototype.doInitFx = function()
{
   var curVal = this._Dlo._Chart.getGaugeData()._curVal;
   var minVal = this._Dlo._Chart.getGaugeData()._minVal;
   var maxVal = this._Dlo._Chart.getGaugeData()._maxVal;
   var dialAngleStart = this._Dlo._Chart.getGaugeData()._dialAngleStart;
   var dialAngleExtent = this._Dlo._Chart.getGaugeData()._dialAngleExtent;
   if (curVal < minVal) curVal = minVal;
   else if (curVal > maxVal) curVal = maxVal;
   var angle = ((Math.abs(curVal-minVal) / Math.abs(maxVal - minVal)) * dialAngleExtent) * (Math.PI / 180);
   
   //  Start the animation

   if (angle) {
     // First set the angle to 0
     var mat = this.getMatrix().clone();
     // translate the matrix so the center of rotation will be at _xPor, _yPor
     mat.translate(this._xPor * -1, this._yPor * -1);
     // Rotate to 0
     mat.rotate(-angle);
     // translate back
     mat.translate(this._xPor, this._yPor);
     this.setMatrix(mat);
    
     var dur  = this._Dlo._Chart.getAnimation().displayDuration ;  // ms
     var anim = new DvtAnimator(this.getCtx(), dur/1000);
     this._setRotationParams(0);
     anim.addProp(DvtAnimator.TYPE_NUMBER, this, this._getRotationParams, this._setRotationParams, angle);
     
     anim.play() ;
   }
};

 
/*--------------------------------------------------------------------*/
/*  init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    Implementation of DvtDetObj.init()
 */
DvtDialIndicator.prototype.init = function()
{
   this._Dlo.setInitFx(true) ;     // allow initfx request
};


/*--------------------------------------------------------------------*/
/*   Init()                                                           */
/*--------------------------------------------------------------------*/
/**
  *   Override of DvtDisplayable.Init()
  */
DvtDialIndicator.prototype.Init = function(context)
{
   DvtDialIndicator.superclass.Init.call(this, context) ;
   this._xPor = 0;
   this._yPor = 0;
   this._angle = 0;
};


/*--------------------------------------------------------------------*/
/*   _getRotationParams/_setRotationParams properties for tweening                    */
/*--------------------------------------------------------------------*/

DvtDialIndicator.prototype._getRotationParams = function()
{
   return this._angle ;
};

DvtDialIndicator.prototype._setRotationParams = function(ang)
{
  var mat = this.getMatrix().clone();
  // translate the matrix so the center of rotation will be at _xPor, _yPor 
  mat.translate(this._xPor * -1, this._yPor * -1);
  mat.rotate(ang-this._angle);
  // translate back
  mat.translate(this._xPor, this._yPor);
  this.setMatrix(mat);
  this._angle = ang;
};
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. 
// All rights reserved. 

/*---------------------------------------------------------------------------*/
/*   DvtStatusIndicator    Represents the indicator component in a status    */
/*---------------------------------------------------------------------------*/

/**
 *  @class DvtStatusIndicator
 *  @extends DvtPath
 *  @constructor
 */  
var  DvtStatusIndicator = function(context)
{
   this.Init(context) ;
};


DvtObj.createSubclass(DvtStatusIndicator, DvtPath, "DvtStatusIndicator") ;


//  private  static const PATH_FORMAT_CHANGED:String = "Bad status indicator path" ;






/*--------------------------------------------------------------------*/
/*  doInitFx()                                                        */
/*--------------------------------------------------------------------*/
/**
  *    Perform initial effects. Called by DvtDetObj on its first render
  */
DvtStatusIndicator.prototype.doInitFx = function()
{
   var dur    = this._Dlo._Chart.getAnimation().displayDuration ;  // ms

   //-------------------------------------------------------------------------
   //  Adjust the initial x positions of the path so that the line L3 width
   //  is zero for horizontal status meters, or the y positions so that the
   //  indicator L3 height is zero for vertical status meters.
   //                             
   //     M1      Z7                  HORIZONTAL status meters
   //      +------------- \  C6
   //      |               |           0  M1  xy
   //      | L2            | L5        3  L2  xy
   //      |               |           6  L3  xy
   //      +------------- /  C4        9  C4  xy xy xy
   //             L3                  16  L5  xy
   //                                 19  C6  xy xy xy                          
   //                                 26  Z7                         
   //                                                          
   //          M1   Z7                VERTICAL status meters
   //     C2  /----------\ C6  
   //         |          |             0  M1  xy
   //         |          |             3  C2  xy xy xy
   //         |          |            10  L3  xy
   //     L3  |          | L5         13  L4  xy
   //         |          |            16  L5  xy
   //         |          |            19  C6  xy xy xy
   //         |          |            26  Z7          
   //         |          |     
   //         +----------+     
   //             L4           
   //                                                             
   //  Notes:   M x y
   //           L x y
   //           C x1 y1 x2 y2 x3 y3  where 3 is the curve end point and 1 and
   //                                2 are the control points for the start
   //                                and end respectively.
   //           Q x1 y1 x2 y2        where 2 is the curve end point and 1 is
   //                                the control point.
   //-------------------------------------------------------------------------

   //  Verify that we have received what we expect from the MT.

   var tval        = 0 ;                      // the value to be tweened
   var ar          = this.getCommands() ;
   var aPath       = DvtArrayUtils.copy(ar) ;
   
   if (this._Dlo._Chart.isVertical()) {
     if ((ar[0]  === 'M')    &&   // M1      //  Verify that we have received 
         (ar[3]  === 'C')    &&   // C2      //  what we expect from the MT.
         (ar[16] === 'L')    &&   // L5
         (ar[19] === 'C')) {      // C6

       tval        = ar[12] - ar[9] ;       // length of L3 for tweening
       this._tval  = tval ;
       this._bVert = true ;

       aPath[2]  += tval ;   // y in M1     // set up initial 
       aPath[5]  += tval ;   // y's         // height of indicator
       aPath[7]  += tval ;   // in
       aPath[9]  += tval ;   // C2
       aPath[18] += tval ;   // y in L5
       aPath[21] += tval ;   // y's
       aPath[23] += tval ;   // in
       aPath[25] += tval ;   // C6
       this.setCommands(aPath) ;
     }
   }
   else  {
      // horizontal case

      if ((ar[6]  === 'L')   &&   // L3      //  Verify that we have received
          (ar[9]  === 'C')   &&   // C4      //  what we expect from the MT.
          (ar[16] === 'L')   &&   // L5
          (ar[19] === 'C')) {     // C6

        tval        = ar[7] - ar[4] ;        // length of L3 for tweening
        this._tval  = tval ;
        this._bVert = false ;

        aPath[7]  -= tval ;   // x in L3     // set up initial 
        aPath[10] -= tval ;   // x's         // width of indicator
        aPath[12] -= tval ;   // in
        aPath[14] -= tval ;   // C4
        aPath[17] -= tval ;   // x in L5
        aPath[20] -= tval ;   // x's
        aPath[22] -= tval ;   // in
        aPath[24] -= tval ;   // C6
        this.setCommands(aPath) ;
      }
   }
   
   this._aAnimCmds = DvtArrayUtils.copy(aPath); // for tweening.
    
   // Start the animation
   
   if (tval) {
     var anim = new DvtAnimator(this.getCtx(), dur/1000);
     this._setPseudo(0);                        // initialize the start state
     anim.addProp(DvtAnimator.TYPE_NUMBER, this, this._getPseudo, this._setPseudo, tval);
     anim.play() ;
   }
};

 
/*--------------------------------------------------------------------*/
/*  init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    Implementation of DvtDetObj.init()
 */
DvtStatusIndicator.prototype.init = function()
{
   this._Dlo.setInitFx(true) ;     // allow initfx request
};


/*--------------------------------------------------------------------*/
/*   Init()                                                           */
/*--------------------------------------------------------------------*/
/**
  *   Override of DvtDisplayable.Init()
  */
DvtStatusIndicator.prototype.Init = function(context)
{
   DvtStatusIndicator.superclass.Init.call(this, context) ;

   this._aCmds      = null ;         // used for 
   this._aAnimCmds  = null;          // initfx
};


/*--------------------------------------------------------------------*/
/*   _getPseudo/_setPseudo properties for tweening                    */
/*--------------------------------------------------------------------*/

DvtStatusIndicator.prototype._getPseudo = function()
{
   return this._tval ;
};

DvtStatusIndicator.prototype._setPseudo = function(tval)
{
   var ar    = this.getCommands() ;
   var aCmds = this._aAnimCmds ;
   
   if (this._bVert) {
     ar[2]  = aCmds[2]  - tval ;  // y in M1
     ar[5]  = aCmds[5]  - tval ;  // y's
     ar[7]  = aCmds[7]  - tval ;  // in
     ar[9]  = aCmds[9]  - tval ;  // C2
     ar[18] = aCmds[18] - tval ;  // y in L5
     ar[21] = aCmds[21] - tval ;  // y's
     ar[23] = aCmds[23] - tval ;  // in
     ar[25] = aCmds[25] - tval ;  // C6
   }
   else {
     ar[7]  = aCmds[7]  + tval ;  // x in L3
     ar[10] = aCmds[10] + tval ;  // x's
     ar[12] = aCmds[12] + tval ;  // in
     ar[14] = aCmds[14] + tval ;  // C4
     ar[17] = aCmds[17] + tval ;  // x in L5
     ar[20] = aCmds[20] + tval ;  // x's
     ar[22] = aCmds[22] + tval ;  // in
     ar[24] = aCmds[24] + tval ;  // C6
   }

   this.setCommands(ar) ;
   this._tval = tval;
};



/*--------------------------------------------------------------------*/
/*   DvtTSGrabber          Time Selector Grabber Component            */
/*--------------------------------------------------------------------*/
/**
  *  Time selector grabber component.
  *  @extends DvtContainer
  *  @class DvtTSGrabber  Graph time selector grabber component. Created and used
  *  by DvtTimeSelector
  *  <p>
  *  @constructor  
  */
/*  
var   DvtTSGrabber = function(context)
{
   this.Init(context) ;
}  
*/

var   DvtTSGrabber = function(ts, side) {
    this.Init(ts, side);
}

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

// Constants
//
DvtTSGrabber.WIDTH = 8;
DvtTSGrabber.HEIGHT = 12;

DvtTSGrabber.LEFT = 1;
DvtTSGrabber.RIGHT = 2;


/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    @private
 */
DvtTSGrabber.prototype.Init = function(ts, side)
{
    this._ts = ts;
    this._side = side;
 
    var bounds = this.getGrabberBounds(ts, side);
    
    var context = ts.getCtx();
    
    DvtTSGrabber.superclass.Init.call(this, context, "tsgrabber");
    this._container = new DvtRect(context, bounds.x, bounds.y, bounds.w, bounds.h, null) ;
    this.addChild(this._container);

    this._fill = DvtSolidFill.invisibleFill();   // need transparent fill
}

DvtTSGrabber.prototype.getGrabberBounds = function(ts, side) {
    var grabberWidth = this.getGrabberWidth();
    var grabberHeight = this.getGrabberHeight();

    // create and initialize the underlying rect
    var x = 0;
    if (side == DvtTSGrabber.LEFT)
        x = ts.getX() - grabberWidth / 2 ;
    else
        x = ts.getX() + ts.getWidth() - grabberWidth / 2;
    var y = ts.getY() + ts.getHeight() / 2 - grabberHeight / 2 ;
    return new DvtRectangle(x, y, grabberWidth, grabberHeight);
}




/*--------------------------------------------------------------------*/
/*  init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    Implementation of DvtDetObj.init()
 */
DvtTSGrabber.prototype.init = function()
{
    this.setFill(this._fill);

    if (DvtAgent.isTouchDevice()) {
    } else {
        this.addEvtListener(DvtMouseEvent.MOUSEOVER, this.mouseOverHandler, false, this);
        this.addEvtListener(DvtMouseEvent.MOUSEOUT, this.mouseOutHandler, false, this);
        this.addEvtListener(DvtMouseEvent.MOUSEDOWN, this.mouseDownHandler, false, this);
    }

    if (this._side == DvtTSGrabber.LEFT)
        this._container.getImpl().getElem().setAttributeNS(null, "cursor", "w-resize"); // TODO Remove SVG Hack
    else if (this._side == DvtTSGrabber.RIGHT)
        this._container.getImpl().getElem().setAttributeNS(null, "cursor", "e-resize"); // TODO Remove SVG Hack

    DvtGraphSysUtils.makeDetObject(this._container);
    this._container.setPixelHinting(true);
    this._container._Dlo.setUseTooltip(true);
}

DvtTSGrabber.prototype.getGrabberHeight = function()
{
    return DvtTSGrabber.HEIGHT;
}

DvtTSGrabber.prototype.getGrabberWidth = function()
{
    return DvtTSGrabber.WIDTH;
}

/*
 * get/setTimeSelector()
 * 
 * Gets/sets the owning time selector.
 */
DvtTSGrabber.prototype.getTimeSelector = function() {
    return this._ts;
}


DvtTSGrabber.prototype.setTimeSelector = function(ts) {
    this._ts = ts;
}

/*
 * get/setSide()
 * 
 * Gets/sets the side property of this grabber.
 */
DvtTSGrabber.prototype.getSide = function() {
    return this._side;
}


DvtTSGrabber.prototype.setSide = function(side) {
    this._side = side;
}

/**
 * isPointInside(x, y)
 * 
 * Returns true if the point specify by [x, y] is within the grabber rect.
 * 
 */
DvtTSGrabber.prototype.isPointInside = function(x, y) {
    return (x >= this._container.getX() && x <= (this._container.getX() + this._container.getWidth())
            && y >= this._container.getY() && y <= (this._container.getY() + this._container.getHeight()));
}


/*
 * Mouse event handlers
 */


DvtTSGrabber.prototype.mouseOverHandler = function(event) {

    this._Dlo.setTooltip(this._ts.makeTooltipText());

    // delegate to the container's handler
    this._ts.mouseOverHandler(event);

}


DvtTSGrabber.prototype.mouseOutHandler = function(event) {
    // delegate to the container's handler
    this._ts.mouseOutHandler(event);
}


DvtTSGrabber.prototype.mouseDownHandler = function(event) {

    // delegate to the container's handler
    this._ts.mouseDownHandler(event);
    //this._ts.handleGrabberDragStart(this, event);
}

DvtTSGrabber.prototype.getContainer = function() {
    return this._container;
}

DvtTSGrabber.prototype.setStroke = function(stroke) {
    this._container.setStroke(stroke);
}

DvtTSGrabber.prototype.setSolidStroke = function(color, alpha, strokeWidth) {
    this._container.setSolidStroke(color, alpha, strokeWidth);
}

DvtTSGrabber.prototype.setFill = function(fill) {
    this._container.setFill(fill);
}

DvtTSGrabber.prototype.setSolidFill = function(color, alpha) {
    this._container.setSolidFill(color, alpha);
}

DvtTSGrabber.prototype.setX = function(val) {
    this._container.setX(val);
}

// Mobile grabber
/**
 * @constructor
 */
var   DvtMobileTSGrabber = function(ts, side) {
    this.Init(ts, side);
}


DvtObj.createSubclass(DvtMobileTSGrabber, DvtTSGrabber, "DvtMobileTSGrabber");

/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    @private
 */
DvtMobileTSGrabber.prototype.Init = function(ts, side)
{
    DvtMobileTSGrabber.superclass.Init.call(this, ts, side) ;
}

/**
 * isPointInside(x, y)
 * 
 * Returns true if the point specify by [x, y] is within the grabber rect.
 * 
 */
DvtMobileTSGrabber.prototype.isPointInside = function(x, y) {
    var hitRect = new DvtRectangle(this._container.getX()-4, this._container.getY(), this._container.getWidth()+8, this._container.getHeight());
    return hitRect.containsPoint(x, y);
}

DvtMobileTSGrabber.prototype.getGrabberHeight = function()
{
    return 25;
}

DvtMobileTSGrabber.prototype.getGrabberWidth = function()
{
    return 20;
}
/*--------------------------------------------------------------------*/
/*   DvtTimeSelector          Time Selector Component                 */
/*--------------------------------------------------------------------*/
/**
  *  Time selector component.
  *  @extends DvtContainer
  *  @class DvtTimeSelector  Graph time slector component.
  *  <p>
  *  Example:<br><br><code>
  *  var ts = new DvtTimeSelector(context) ;<br>
  *  </code>
  *  @constructor  
  */
var   DvtTimeSelector = function(context, x, y, w, h, id)
{
   this.Init(context, x, y, w, h, id) ;
}  

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

DvtTimeSelector.TOK_X         = "__X__";
DvtTimeSelector.TOK_Y         = "__Y__";
DvtTimeSelector.TOK_STARTDATE = "__START_DATE__";
DvtTimeSelector.TOK_ENDDATE   = "__END_DATE__";

 
/*--------------------------------------------------------------------*/
/*  init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    Implementation of DvtDetObj.init()
 */
DvtTimeSelector.prototype.init = function()
{
    // var chart = this._Dlo._Chart;

    this._pxRatio = (this._timeAxisEndDate - this._timeAxisStartDate) 
                    / (this._timeAxisEndPixel - this._timeAxisStartPixel);

    // make sure the time selector area is withing axis pixel limits
    var pLeft = this.getLPos();
    var pRight = this.getRPos();
    if (pLeft < this._timeAxisStartPixel)
        this._container.setX(this._timeAxisStartPixel);
    if (pRight > this._timeAxisEndPixel)
        this._container.setWidth(this._timeAxisEndPixel - this.getX());

    // make sure at least a line is visible if TS width is zero
    if (this.getWidth() <= 0) 
        this._container.setWidth(1);

    // set up the time axis for labels and tooltips
    this._timeAxis = new DvtTimeAxis(this._timeAxisStartDate, this._timeAxisEndDate,
                            this._timeAxisStartPixel, this._timeAxisEndPixel,
                            300, 10,  // meaningless
                            this._aTimeAxisMonths, this._timeZoneOffset, true);
                            
    if (!DvtAgent.isTouchDevice()) {
        // register mouse event handlers
        this.addEvtListener(DvtMouseEvent.MOUSEOVER, this.mouseOverHandler, false, this);
        this.addEvtListener(DvtMouseEvent.MOUSEOUT, this.mouseOutHandler, false, this);
        this.addEvtListener(DvtMouseEvent.MOUSEDOWN, this.mouseDownHandler, false, this);
    } else {
        this._Dlo._Chart.addEvtListener(DvtTouchEvent.TOUCHSTART, this._touchHandler, false, this);
        this._Dlo._Chart.addEvtListener(DvtTouchEvent.TOUCHMOVE, this._touchHandler, false, this);
        this._Dlo._Chart.addEvtListener(DvtTouchEvent.TOUCHEND, this._touchHandler, false, this);
    }

    this._grabberLeft = this.createGrabber(DvtTSGrabber.LEFT);
    this._grabberRight = this.createGrabber(DvtTSGrabber.RIGHT);

    this._grabberLeft.setStroke(this.getStroke());
    this._grabberRight.setStroke(this.getStroke());
    this._grabberLeft.setVisible(false);
    this._grabberRight.setVisible(false);

    this._grabberLeft.init();
    this._grabberRight.init();

    this._container.addChild(this._grabberLeft);
    this._container.addChild(this._grabberRight);
    this.addChild(this._container);

    if (DvtAgent.isTouchDevice()) {
        this._showSelection();
    }

    this._container.setPixelHinting(true);
    DvtGraphSysUtils.makeDetObject(this._container);
    this._container._Dlo.setTooltip(this.makeTooltipText());
    
    this._standardStroke = this.getStroke();
    this._selectStroke = this._standardStroke.clone();
    this._selectStroke.setWidth(2);
};

DvtTimeSelector.prototype.createGrabber = function(type)
{
    var grabber = null;
    if (DvtAgent.isTouchDevice()) {
        grabber = new DvtMobileTSGrabber(this, type);
    } else {
        grabber = new DvtTSGrabber(this, type);
    }
    DvtGraphSysUtils.makeDetObject(grabber);
    return grabber;
}

/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    @private
 */
DvtTimeSelector.prototype.Init = function(context, x, y, w, h, id)
{
   DvtTimeSelector.superclass.Init.call(this, context, "timeselector");
   this._container = new DvtRect(context, x, y, w, h, id) ;
   
   this._timeAxisStartDate   = 0 ;
   this._timeAxisEndDate     = 0 ;
   this._timeAxisStartPixel  = 0 ;
   this._timeAxisEndPixel    = 0 ;
   this._aTimeAxisMonths     = null ;     // month names, am/pm strings
   this._timeZoneOffset      = 0 ;
   this._timeAxisEventJs     = null ;
   this._timeSelectorEventJs = null ;
   this._translatedStart     = null ;
   this._translatedEnd       = null ;

   this._timeAxis            = null;
   
   this._pxRatio             = 0;
   
   this._grabberLeft         = null;
   this._grabberRight        = null;
   this._isMouseOver         = false;
   this._isDragging          = false;
   this._dragX               = -1;
   this._dragY               = -1;
   this._origLPos            = -1;
   this._origRPos            = -1;
   this._dragTarget          = 0;
   this._overTarget          = 0;
   
   this._standardStroke      = null;
   this._selectStroke        = null;
   this._targets = new Object();
   this._dragXMap = new Object();
   this._dragItems = new Array();
};


/*--------------------------------------------------------------------*/
/*  get/setTimeAxisStartDate()                                        */
/*--------------------------------------------------------------------*/

DvtTimeSelector.prototype.getTimeAxisStartDate = function()
{
    return this._timeAxisStartDate ;
};

DvtTimeSelector.prototype.setTimeAxisStartDate = function(sd)
{
    this._timeAxisStartDate = sd ;
};

/*--------------------------------------------------------------------*/
/*  get/setTimeAxisEndDate()                                          */
/*--------------------------------------------------------------------*/

DvtTimeSelector.prototype.getTimeAxisEndDate = function()
{
    return this._timeAxisEndDate ;
};

DvtTimeSelector.prototype.setTimeAxisEndDate = function(ed)
{
    this._timeAxisEndDate = ed ;
};

/*--------------------------------------------------------------------*/
/*  get/setTimeAxisStartPixel()                                       */
/*--------------------------------------------------------------------*/
/**
  *   @type number
  */
DvtTimeSelector.prototype.getTimeAxisStartPixel = function() 
{
    return this._timeAxisStartPixel ;
} ;

/**
  *   @param {number}  sp
  */
DvtTimeSelector.prototype.setTimeAxisStartPixel = function(sp) 
{
    this._timeAxisStartPixel =  sp ;
} ;

/*--------------------------------------------------------------------*/
/*  get/setTimeAxisEndPixel()                                         */
/*--------------------------------------------------------------------*/
/**
  *   @type number
  */
DvtTimeSelector.prototype.getTimeAxisEndPixel = function()
{
    return this._timeAxisEndPixel  ;
};

/**
  *   @param {number}  ep
  */
DvtTimeSelector.prototype.setTimeAxisEndPixel = function(ep)
{
    this._timeAxisEndPixel =  ep ;
};

/*--------------------------------------------------------------------*/
/*  get/setTimeAxisMonths()                                           */
/*--------------------------------------------------------------------*/
/**
  *   @type Array
  */
DvtTimeSelector.prototype.getTimeAxisMonths = function()
{
    return this._aTimeAxisMonths ;
} ;

/**
  *   @param {Array}  arm  array of months
  */
DvtTimeSelector.prototype.setTimeAxisMonths = function(arm)
{
    this._aTimeAxisMonths = arm ;
} ;

/*--------------------------------------------------------------------*/
/*  get/setTimeZoneOffset()                                           */
/*--------------------------------------------------------------------*/

DvtTimeSelector.prototype.getTimeZoneOffset = function()
{
    return this._timeZoneOffset ;
} ;

DvtTimeSelector.prototype.setTimeZoneOffset = function(tzo)
{
    this._timeZoneOffset = tzo ;
} ;

/*--------------------------------------------------------------------*/
/*  get/setTimeAxisEventJs()                                          */
/*--------------------------------------------------------------------*/
/**
  *   @type String
  */
DvtTimeSelector.prototype.getTimeAxisEventJs = function()
{
    return this._timeAxisEventJs ;
};

/**
  *   @param {String} ejs  Time axis event Javascript.
  */
DvtTimeSelector.prototype.setTimeAxisEventJs = function(ejs)
{
    this._timeAxisEventJs = ejs ;
};

/*--------------------------------------------------------------------*/
/*  get/setTimeSelectorEventJs()                                      */
/*--------------------------------------------------------------------*/
/**
  *   @type String
  */
DvtTimeSelector.prototype.getTimeSelectorEventJs = function()
{
    return this._timeSelectorEventJs ;
} ;

/**
  *   @param {String} evjs  Time selector event Javascript.
  */
DvtTimeSelector.prototype.setTimeSelectorEventJs = function(evjs)
{
    this._timeSelectorEventJs = evjs ;
} ;


/*--------------------------------------------------------------------*/
/*  get/setTranslatedStart()                                          */
/*--------------------------------------------------------------------*/
/**
  *   @type String
  */
DvtTimeSelector.prototype.getTranslatedStart = function()
{
    return this._translatedStart ;
} ;

/**
  *   @param {String}  start
  */
DvtTimeSelector.prototype.setTranslatedStart = function(start)
{
    this._translatedStart = start ;
} ;


/*--------------------------------------------------------------------*/
/*  get/setTranslatedEnd()                                            */
/*--------------------------------------------------------------------*/
/**
  *   @type String
  */
DvtTimeSelector.prototype.getTranslatedEnd = function()
{
    return this._translatedEnd ;
} ;

/**
  *   @param {String}  end
  */
DvtTimeSelector.prototype.setTranslatedEnd = function(end)
{
    this._translatedEnd = end ;
} ;


DvtTimeSelector.prototype.isPointInside = function(x, y) {
    return (x >= this.getX() && x <= (this.getX() + this.getWidth())
            && y >= this.getY() && y <= (this.getY() + this.getHeight()));
}

/*-------------------------------------------------------------------
 *
 * Mouse handling methods
 *
 *-------------------------------------------------------------------*/
 
/**
 * _getMouseTarget(x, y)
 * 
 * returns an integer representing the area under the current position specified
 * by the [x, y] coordinates:
 * 
 *   0 ... area outside of the time selector
 *   1 ... the main time selector rectangle
 *   2 ... left grabber
 *   3 ... right grabber
 * 
 * @private
 */ 
DvtTimeSelector.prototype._getMouseTarget = function(x, y) {
    var target = 0;
    
    if (this._isMouseOver || this._isDragging) {
        if (this._grabberLeft.isPointInside(x, y)) {
            target = 2;     // in left grabber
        }
        else if (this._grabberRight.isPointInside(x, y)) {
            target = 3;     // in right grabber
        }
    }
    if (target == 0 && this.isPointInside(x, y)) {
        target = 1;
    }
    return target;
}


/**
 * mouseOverHandler(event)
 * 
 * "mouseover" DOM event handler. Highlights the TS area when
 * hovered over with mouse.
 * 
 */
DvtTimeSelector.prototype.mouseOverHandler = function(event) {
    var pos = this.getCtx().getRelativePosition(event.pageX, event.pageY);
    var x = pos.x;
    var y = pos.y;
    
    var target = this._getMouseTarget(x, y);    

    // if already dragging, return, just set the 'over' flag
    if (this._isDragging) {
        if (target > 0) {
            this._isMouseOver = true;
        }
        return;
    }
    
    // if not over TimeSelector, do nothing
    if (target == 0)
        return;
        
    // also if already over, do nothing    
    if (this._isMouseOver && this._overTarget == target)
        return;
        
    this._isMouseOver = true;
    this._overTarget = target;

    // set mouse cursor
    if (target == 1)
        this._container.setCursor("move");

    // highlight the TS area
    this._showSelection(target);
}


/**
 * mouseOutHandler(event)
 * 
 * "mouseout" DOM event handler. Unselects the TS area when
 * the mouse is outside (unless in dragging mode).
 * 
 */
DvtTimeSelector.prototype.mouseOutHandler = function(event) {
    var pos = this.getCtx().getRelativePosition(event.pageX, event.pageY);
    var x = pos.x;
    var y = pos.y;

    var target = this._getMouseTarget(x, y);    

    // if dragging, keep tracking mouse actions, just reset the _isOver flag
    if (this._isDragging) {
        if (target == 0)
            this._isMouseOver =false;
            this._overTarget = 0;
        return;
    }
    
    if (target == 0) {
        if (this._isMouseOver == false)
            return;
    
        this._isMouseOver = false;
    
        // reset mouse cursor to default
        this._container.setCursor("default");
/*
        this._grabberLeft.removeEvtListener(DvtMouseEvent.MOUSEOVER, this.mouseOverHandler, false, this);
        this._grabberLeft.removeEvtListener(DvtMouseEvent.MOUSEOUT, this.mouseOutHandler, false, this);
        this._grabberLeft.removeEvtListener(DvtMouseEvent.MOUSEOVER, this.mouseOverHandler, false, this);
        this._grabberLeft.removeEvtListener(DvtMouseEvent.MOUSEOUT, this.mouseOutHandler, false, this);
*/
        this._hideSelection();
    }
}

/**
 * Handler for the touch event
 * @param {DvtTouchEvent} event Touch event
 * @private
 */
DvtTimeSelector.prototype._touchHandler = function(event) {
    this._isMouseOver = true;
    this.getTouchManager().processAssociatedTouchDefault(event, this);
}

/**
 * Processes "touchstart" event
 * @param {DvtTouchEvent} event Touch event
 * @param {DvtTouch} touch Touch object
 */
DvtTimeSelector.prototype.immediateTouchAttempted = function(event, touch) {
    var pos = this.getCtx().getRelativePosition(touch.pageX, touch.pageY);
    var x = pos.x;
    var y = pos.y;
    var target = this._getMouseTarget(x, y);   
    var allowTouch = true;
    if (target == 0) {
        return;
    }

    for (var i=0;i<this._dragItems.length; i++) {
        var dragItemId = this._dragItems[i];
        var targetId = this._targets[dragItemId];
        // Gripper only allows grippers
        if (targetId == 1) {
            allowTouch = false;
        } else if (targetId > 1) {
            if (target == 1) {
                allowTouch = false;
            }
        }
        if (targetId == target) {
            allowTouch = false;
        }
    }

    if (allowTouch) {
        this._dragItems.push(touch.identifier);
        this._targets[touch.identifier] = target;
        this._dragXMap[touch.identifier] = x;
        this._dragStart(target, x, y); 
        
        this.getTouchManager().saveProcessedTouchDefault(touch.identifier, this, this._container, "timeselector", "timeselector"+target, this);
        event.blockTouchHold();

    }
}

DvtTimeSelector.prototype.immediateTouchMoved = function(event, touch) {
    var pos = this.getCtx().getRelativePosition(touch.pageX, touch.pageY);
    var x = pos.x;
    var y = pos.y;
    var target = this._getMouseTarget(x, y);   
    event.preventDefault();

    if (this._isDragging == false)          // if not dragging, ignore
        return;

    var target = this._targets[touch.identifier];
    if (target != null && target > 0) {

        var xPos = 0;
        var yPos = 0;
        var targetTouches = event.touches;
        for (var i=0;i<this._dragItems.length; i++) {
            var dragItemId = this._dragItems[i];
            for (var j=0; j<targetTouches.length; j++) {
                var targetTouch = targetTouches[j];
                if (targetTouch.identifier == dragItemId) {
                    xPos += targetTouch.pageX;
                    yPos += targetTouch.pageY;
                }
            }
        }
        
        var dragX = this._dragXMap[touch.identifier];

        this._dragTarget = target;
        this._dragX = dragX;

        this._dragMove(x, y);
    }
}

/**
 * Processes "touchend" event
 * @param {DvtTouchEvent} event Touch event
 * @param {DvtTouch} touch Touch object
 */
DvtTimeSelector.prototype.immediateTouchEnded = function(event, touch) {
    var pos = this.getCtx().getRelativePosition(touch.pageX, touch.pageY);
    var x = pos.x;
    var y = pos.y;
    var target = this._getMouseTarget(x, y);   
    
    if (this._isDragging == false)
        return;
    // finish drag  
    var touchIndex = DvtArrayUtils.getIndex(this._dragItems, touch.identifier);
    if (touchIndex != -1) {
       this._dragItems.splice(touchIndex, 1);
       this._targets[touch.identifier] = null;
       this._dragXMap[touch.identifier] = null;
       this.getTouchManager().removeTooltipInfo(touch.identifier);
    }
    if (this._dragItems.length == 0) {
        this._dragItems = new Array();
        this._dragEnd(x, y);
        this._targets = new Object();
        this._dragXMap = new Object();
    }
}

/**
 * mouseDownHandler(event)
 * 
 * "mousedown" DOM event handler. Used to start the dragging when
 * the mouse pointer is above the active TS area.
 * 
 */
DvtTimeSelector.prototype.mouseDownHandler = function(event) {

    // remove 'mouseover' and 'mouseout' listeners while dragging
    // this.removeEvtListener(DvtMouseEvent.MOUSEOVER, this.mouseOverHandler, false, this);
    // this.removeEvtListener(DvtMouseEvent.MOUSEOUT, this.mouseOutHandler, false, this);

    var pos = this.getCtx().getRelativePosition(event.pageX, event.pageY);
    var x = pos.x;
    var y = pos.y;

    var target = this._getMouseTarget(x, y);

    // start dragging 
    this._dragStart(target, x, y);

    if (event)
        event.preventDefault();

    // add drag tracking listeners, listen everywhere
    this._Dlo._Chart.addEvtListener(DvtMouseEvent.MOUSEMOVE, this.mouseMoveHandler, false, this);
    this._Dlo._Chart.addEvtListener(DvtMouseEvent.MOUSEUP, this.mouseUpHandler, false, this);
}
 

/**
 * mouseUpHandler(event)
 * 
 * "mouseup" DOM event handler. Used to finish the drag operation.
 * 
 */
DvtTimeSelector.prototype.mouseUpHandler = function(event) {

    // remove drag tracking listeners
    this._Dlo._Chart.removeEvtListener(DvtMouseEvent.MOUSEMOVE, this.mouseMoveHandler, false, this);
    this._Dlo._Chart.removeEvtListener(DvtMouseEvent.MOUSEUP, this.mouseUpHandler, false, this);

    // restore 'mouseover' and 'mouseout' listeners
    //this.addEvtListener(DvtMouseEvent.MOUSEOVER, this.mouseOverHandler, false, this);
    //this.addEvtListener(DvtMouseEvent.MOUSEOUT, this.mouseOutHandler, false, this);

    var pos = this.getCtx().getRelativePosition(event.pageX, event.pageY);
    var x = pos.x;
    var y = pos.y;
        
    // finish drag    
    this._dragEnd(x, y);

    if (this._isMouseOver == false) {
        this._hideSelection();
    }

    if (event)
        event.preventDefault();

}

 
/**
 * mouseMoveHandler(event)
 * 
 * "mousemove" DOM event handler. Used to provide visual feedback
 * during dragging.
 * 
 */
DvtTimeSelector.prototype.mouseMoveHandler = function(event) {

    if (this._isDragging == false)          // if not dragging, ignore
        return;
    
    var pos = this.getCtx().getRelativePosition(event.pageX, event.pageY);
    var x = pos.x;
    var y = pos.y;
    
    this._dragMove(x, y);

    if (event)
        event.preventDefault();

}


/**
 * Grabber drag start handler
 */
/*
DvtTimeSelector.prototype.handleGrabberDragStart = function(grabber, event) {

    var chart = this._Dlo._Chart;
    var pos = DvtGraphEventUtils.getRelativePosition(chart, event.pageX, event.pageY);
    var x = pos.x;
    var y = pos.y;

    var target = 0;

    switch (grabber.getSide()) {
        case DvtTSGrabber.LEFT:
            target = 2;
            break;
        case DvtTSGrabber.RIGHT:
            target = 3;
            break;
    }

    // start dragging
    if (target)
        this._dragStart(target, x, y);

    // add drag tracking listeners, listen everywhere
    this._Dlo._Chart.addEvtListener(DvtMouseEvent.MOUSEMOVE, this.mouseMoveHandler, false, this);
    this._Dlo._Chart.addEvtListener(DvtMouseEvent.MOUSEUP, this.mouseUpHandler, false, this);

}
*/


/* -------------------------------------------------------------------
 * 
 * Generic interaction methods. Useful for updating TS active area
 * params in response to mouse, touch, or keyboard actions.
 * 
 * -------------------------------------------------------------------*/

/** 
 * _showSelection()
 * 
 * Updates the TS appearance to higlight the active area and show points
 * that can be dragged.
 * 
 * Called when the mouse pointer is over the active area, or when the area
 * is selected using a touch gesture.
 * 
 * @private
 */
DvtTimeSelector.prototype._showSelection = function(target) {
    
    // highlight the area and add grabbers
    if (target == 1) {
        this.setStroke(this._selectStroke);
        this._grabberLeft.setStroke(this._standardStroke);
        this._grabberRight.setStroke(this._standardStroke);
    }
    else if (target == 2) {
        this.setStroke(this._standardStroke);
        this._grabberLeft.setStroke(this._selectStroke);
        this._grabberRight.setStroke(this._standardStroke);
    }
    else if (target == 3) {
        this.setStroke(this._standardStroke);
        this._grabberLeft.setStroke(this._standardStroke);
        this._grabberRight.setStroke(this._selectStroke);
    }
    this._grabberLeft.setVisible(true);
    this._grabberRight.setVisible(true);
}


/** 
 * _hideSelection()
 * 
 * Updates the TS appearance to un-higlight the active area and hide 
 * the drag grabers.
 * 
 * Called when the mouse pointer is outside the active area, or when
 * the dragging is finished, or when unselected using a touch gesture.
 * 
 * @private
 */
DvtTimeSelector.prototype._hideSelection = function() {

    // reset the stroke and remove grabbers
    this.setStroke(this._standardStroke);
    this._grabberLeft.setStroke(this._standardStroke);
    this._grabberRight.setStroke(this._standardStroke);
    this._grabberLeft.setVisible(false);
    this._grabberRight.setVisible(false);
}


/**
 * _dragStart(target, x, y)
 * 
 * Called when the mouse (or touch) drag is started.
 * 
 * @param  target  time selector area selected for dragging
 * @param  x  current x-coordinate of the pointing device
 * @param  y  current y-coordinate of the pointing device
 *
 * @private
 */
DvtTimeSelector.prototype._dragStart = function(target, x, y) {

    if (this._isDragging)   // do nothing, if already dragging
        return;
        
    this._isDragging = true;
    this._dragX = x;                    // start drag x-coordinate
    this._origLPos = this.getLPos();    // original left edge in pixels
    this._origRPos = this.getRPos();    // original right edge in pixels

    this._dragTarget = target;          // the TS area being dragged
}


/**
 * _dragEnd(x, y)
 * 
 * Called when the mouse (or touch) drag is finished.
 *
 * @param  x  current x-coordinate of the pointing device
 * @param  y  current y-coordinate of the pointing device
 *
 * @private
 */
DvtTimeSelector.prototype._dragEnd = function(x, y) {

    // TODO: do we need x, y here? Should use the last _dragMove position.
    if (this._isDragging == false)
        return;

    this._isDragging = false;
    this._dragX = -1;
    this._dragTarget = 0;
    
    // Do not send any event if the bounds did not change
    if(this._origLPos == this.getLPos() && this._origRPos == this.getRPos())
      return;

    // execute the time selector event javascript
    if (this._timeSelectorEventJs && this._timeSelectorEventJs.length > 0) {
        var startTS = this._timeAxisStartDate + ((this.getLPos() - this._timeAxisStartPixel) * this._pxRatio);
        var endTS   = this._timeAxisEndDate - (this._timeAxisEndPixel - this.getRPos()) * this._pxRatio;

        var startD = new Date();
        var endD = new Date();

        startD.setTime(startTS);
        endD.setTime(endTS);

        // TODO: need to convert x, y to global coordinates ???

        // TODO: should use StrUtils token replace and EventTokens constants when available
        var sJS = this._timeSelectorEventJs;
        var sJS1 = sJS.replace(DvtTimeSelector.TOK_X, x);
        var sJS2 = sJS1.replace(DvtTimeSelector.TOK_Y, y);
        var sJS3 = sJS2.replace(DvtTimeSelector.TOK_STARTDATE, startD.getTime());
        var finalJS = sJS3.replace(DvtTimeSelector.TOK_ENDDATE, endD.getTime());

        // execute the JS code 
        //BUG 34184877 - MAKE DVT COMPATIBLE WITH CSP STANDARDS
        var src = finalJS.match(/'(.*?)'/g)[0].replace(/\'/g, "")
        AdfPage.PAGE.findComponent(src).getPeer().sendToServerPPR3({source:src,event:'bi_gtse',bi_gtsS:startD.getTime(),bi_gtsE:endD.getTime()});
    }
}
 
 
/**
 * _dragMove(x, y)
 * 
 * Handles dragging either by mouse or touch. Time selector must be in 'dragging' mode.
 * 
 * @param  x  current x-coordinate of the pointing device
 * @param  y  current y-coordinate of the pointing device
 * 
 * @private
 */
DvtTimeSelector.prototype._dragMove = function(x, y) {
    if (this._isDragging == false)          // if not dragging, nothing to do
        return;

    var offsetX = x - this._dragX;
    var newX = this.getLPos();
    var newWidth = Math.floor(this.getWidth());
    var grabberWidth = this._grabberLeft.getGrabberWidth();
    var minWidth = 0;   // minimum TS area width is 0
    if (this._dragTarget == 1) {            // main TS area
        newX = this._origLPos + offsetX;
        if (newX < this._timeAxisStartPixel) {
            newX = this._timeAxisStartPixel;
        }
        if ((newX + Math.floor(this.getWidth())) > this._timeAxisEndPixel) {
            newX = this._timeAxisEndPixel - Math.floor(this.getWidth());
        }
    }
    else if (this._dragTarget == 2) {       // left grabber
        newX = this._origLPos + offsetX;
        if (newX < this._timeAxisStartPixel) {
            newX = this._timeAxisStartPixel;
        }
        if (newX > this.getRPos() - minWidth) {
            newX = this.getRPos() - minWidth;
        }
        newWidth = this.getRPos() - newX;
    }
    else if (this._dragTarget == 3) {       // right grabber
        var newRPos = this._origRPos + offsetX;
        if (newRPos < this.getLPos() + minWidth) {
            newRPos = this.getLPos() + minWidth;
        }
        if (newRPos > this._timeAxisEndPixel) {
            newRPos = this._timeAxisEndPixel;
        }
        newWidth = newRPos - this.getLPos();
    }

    if (newWidth == 0)
        newWidth = 1;         // must ensure non zero width

    // set new Time Selector coordinates        
    this._container.setX(newX);
    this._container.setWidth(newWidth);
    
    var leftX = newX - grabberWidth / 2;
    var rightX = newX + Math.floor(this.getWidth()) - grabberWidth / 2;

    this._grabberLeft.setX(leftX);
    this._grabberRight.setX(rightX);
  
    //this._grabberLeft.update();
    //this._grabberRight.update();
    
    // update tooltip
    var tooltipText = this.makeTooltipText();

    // tooltip range is the same for grippers / time selector
    this._container._Dlo.setTooltip(tooltipText);
    this._grabberLeft.getContainer()._Dlo.setTooltip(tooltipText);
    this._grabberRight.getContainer()._Dlo.setTooltip(tooltipText);
}


 /**
  * getLPos()
  * 
  * returns position of TimeSelector's left edge in pixels
  * 
  * @private
  */
DvtTimeSelector.prototype.getLPos = function() {
    return this.getX();
}
 
/**
* getRPos()
*
* returns position of TimeSelector's right edge in pixels
*
* @private
*/
DvtTimeSelector.prototype.getRPos = function() {
    return this.getX() + Math.floor(this.getWidth());
}



/**
 * makeTootipText()
 *
 * Returns the text to be displayed in tooltip.
 *
 * @private
 */
DvtTimeSelector.prototype.makeTooltipText = function () {
//diff var inorder to determine what label to use
    var diff = this.getRPos() - this.getLPos();
    if (DvtAgent.isRightToLeft())
        diff = this.getLPos() - this.getRPos();
    var startLabel = this._timeAxis.getLabelAt(this.getLPos(), diff);
    var endLabel = this._timeAxis.getLabelAt(this.getRPos(),  - diff);

    if (DvtAgent.isRightToLeft())
        return endLabel + " - " + startLabel;
    return startLabel + " - " + endLabel;
}
DvtTimeSelector.prototype.getStroke = function() {
    return this._container.getStroke();
} 

DvtTimeSelector.prototype.setStroke = function(stroke) {
    this._container.setStroke(stroke);
}

DvtTimeSelector.prototype.setSolidStroke = function(color, alpha, strokeWidth) {
    this._container.setSolidStroke(color, alpha, strokeWidth);
}

DvtTimeSelector.prototype.setFill = function(fill) {
    this._container.setFill(fill);
}

DvtTimeSelector.prototype.setSolidFill = function(color, alpha) {
    this._container.setSolidFill(color, alpha);
}

DvtTimeSelector.prototype.getX = function() {
    return this._container.getX();
}

DvtTimeSelector.prototype.getY = function() {
    return this._container.getY();
}

DvtTimeSelector.prototype.getWidth = function() {
    return this._container.getWidth();
}

DvtTimeSelector.prototype.getHeight = function() {
    return this._container.getHeight();
}

/**
 * Gets a touch manager object
 * @return {DvtTouchManager} touch manager object
 */
DvtTimeSelector.prototype.getTouchManager = function () {
  return this._touchManager;  
}

/**
 * Sets a touch manager object
 * @param {DvtTouchManager} touch manager object
 */
DvtTimeSelector.prototype.setTouchManager = function (touchManager) {
  this._touchManager = touchManager;  
}
/**
 *  Data cursor component.
 *  @extends DvtContainer
 *  @class DvtDataCursor  Creates a data cursor component.
 *  @constructor
 *  @param {DvtContext} context The context object
 *  @param {boolean} bHoriz True if this is a data cursor for horizontal graphs
 */
var DvtDataCursor = function(context, bHoriz) {
  this.Init(context, bHoriz);
}

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

DvtDataCursor.DEFAULT_BACKGROUND_COLOR = "#ffffff";
DvtDataCursor.BEHAVIOR_SNAP = "SNAP";
DvtDataCursor.BEHAVIOR_SMOOTH = "SMOOTH";
DvtDataCursor.BEHAVIOR_AUTO = "AUTO";

DvtDataCursor._CURSOR_LINE_WIDTH = 2;
DvtDataCursor._DATATIP_BORDER_WIDTH = 2;
DvtDataCursor._DATATIP_TEXT_PADDING = 2;
DvtDataCursor._MARKER_SIZE_OUTER = 16;
DvtDataCursor._MARKER_SIZE_MIDDLE = 12;
DvtDataCursor._MARKER_SIZE_INNER = 8;

/**
 * Initializes the data cursor.
 * @param {DvtContext} context The context object
 * @param {boolean} bHoriz True if this is a data cursor for horizontal graphs
 */
DvtDataCursor.prototype.Init = function (context, bHoriz) {
  DvtDataCursor.superclass.Init.call(this, context);
  this._bHoriz = bHoriz;
  
  // Data cursor is never the target of mouse events
  this.setMouseEnabled(false);
  
  // Initially invisible until shown
  this.setVisible(false);
  
  //******************************************* Data Cursor Line ******************************************************/
  if(bHoriz) {
    this._cursorLineRect = new DvtRect(this.getCtx(), 0, 0, 0, DvtDataCursor._CURSOR_LINE_WIDTH, "dcLine");
    this._cursorLineRect.setTranslateY(-DvtDataCursor._CURSOR_LINE_WIDTH/2);
  }
  else { // Vertical
    this._cursorLineRect = new DvtRect(this.getCtx(), 0, 0, DvtDataCursor._CURSOR_LINE_WIDTH, 0, "dcLine");
    this._cursorLineRect.setTranslateX(-DvtDataCursor._CURSOR_LINE_WIDTH/2);
  }
  this._cursorLineRect.setSolidFill("black");
  this._cursorLineRect.setMouseEnabled(false);
  this.addChild(this._cursorLineRect);

  //****************************************** Data Cursor Marker *****************************************************/
  this._marker = new DvtContainer(this._context, "dotContainer");
  this._marker.setMouseEnabled(false);
  this.addChild(this._marker);
  
  var outerCircle = new DvtMarker(this._context, DvtMarker.CIRCLE, null, 0, 0, DvtDataCursor._MARKER_SIZE_OUTER, DvtDataCursor._MARKER_SIZE_OUTER);
  outerCircle.setTranslateX(-DvtDataCursor._MARKER_SIZE_OUTER/2);
  outerCircle.setTranslateY(-DvtDataCursor._MARKER_SIZE_OUTER/2);
  outerCircle.setSolidFill("black");
  outerCircle.setMouseEnabled(false);
  this._marker.addChild(outerCircle);
  
  var middleCircle = new DvtMarker(this._context, DvtMarker.CIRCLE, null, 0, 0, DvtDataCursor._MARKER_SIZE_MIDDLE, DvtDataCursor._MARKER_SIZE_MIDDLE);
  middleCircle.setTranslateX(-DvtDataCursor._MARKER_SIZE_MIDDLE/2);
  middleCircle.setTranslateY(-DvtDataCursor._MARKER_SIZE_MIDDLE/2);
  middleCircle.setSolidFill("white");
  middleCircle.setMouseEnabled(false);
  this._marker.addChild(middleCircle);
      
  // Inner circle will be filled to correspond to the data item color    
  this._markerInnerCircle = new DvtMarker(this._context, DvtMarker.CIRCLE, null, 0, 0, DvtDataCursor._MARKER_SIZE_INNER, DvtDataCursor._MARKER_SIZE_INNER);
  this._markerInnerCircle.setTranslateX(-DvtDataCursor._MARKER_SIZE_INNER/2);
  this._markerInnerCircle.setTranslateY(-DvtDataCursor._MARKER_SIZE_INNER/2);
  this._markerInnerCircle.setMouseEnabled(false);
  this._marker.addChild(this._markerInnerCircle);
}

/**
 * Renders this data cursor.
 * @param {DvtRectangle} plotAreaBounds The bounds of the plot area.
 * @param {number} dataX The x coordinate of the actual data point, where the marker should be placed. 
 * @param {number} dataY The y coordinate of the actual data point, where the marker should be placed. 
 * @param {number} lineCoord The x coordinate of a vertical data cursor, or the y coordinate of a horizontal data cursor.
 * @param {string} text The text for the datatip.
 * @param {string} dataColor The primary color of the associated data item.
 */
DvtDataCursor.prototype.render = function(plotAreaBounds, dataX, dataY, lineCoord, text, dataColor) {
  var bHoriz = this.isHorizontal();
  var bRtl = DvtAgent.isRightToLeft(this.getCtx());
    
  // First render the datatip to retrieve its size.
  var stagePageCoords = DvtSvgDocumentUtils.getStageAbsolutePosition(this.getCtx().getStage());
  var tooltipManager = this.getCtx().getTooltipManager();
  tooltipManager.showDatatip(dataX + stagePageCoords.x, dataY + stagePageCoords.y, text, dataColor, false); 
  var tooltipBounds = tooltipManager.getTooltipBounds(); // tooltipBounds is in the page coordinate space
  
  // Then reposition to the right location
  var tooltipX, tooltipY; // tooltipX and tooltipY in the stage coordinate space
  if(bHoriz) {
    tooltipX = bRtl ? plotAreaBounds.x - 0.75*tooltipBounds.w : plotAreaBounds.x + plotAreaBounds.w - tooltipBounds.w/4;
    tooltipY = dataY - tooltipBounds.h/2;
    
    // Add a buffer between the tooltip and data point. This may be rejected in positionTip due to viewport location.
    if(!bRtl && tooltipX - dataX < DvtDataCursor._MARKER_SIZE_OUTER)
      tooltipX = dataX + DvtDataCursor._MARKER_SIZE_OUTER;
    else if(bRtl && dataX - (tooltipX + tooltipBounds.w) < DvtDataCursor._MARKER_SIZE_OUTER)
      tooltipX = dataX - DvtDataCursor._MARKER_SIZE_OUTER - tooltipBounds.w;
  }
  else {
    tooltipX = lineCoord - tooltipBounds.w/2;
    tooltipY = plotAreaBounds.y - 0.75*tooltipBounds.h;
    
    // Add a buffer between the tooltip and data point. This may be rejected in positionTip due to viewport location.
    if(dataY - (tooltipY + tooltipBounds.h) < DvtDataCursor._MARKER_SIZE_OUTER)
      tooltipY = dataY - DvtDataCursor._MARKER_SIZE_OUTER - tooltipBounds.h;
  }
  tooltipManager.positionTip(tooltipX + stagePageCoords.x, tooltipY + stagePageCoords.y);
  
  // Finally retrieve the rendered bounds to calculate the attachment point for the cursor line.
  tooltipBounds = tooltipManager.getTooltipBounds(); // tooltipBounds is in the page coordinate space
  tooltipX = tooltipBounds.x - stagePageCoords.x;
  tooltipY = tooltipBounds.y - stagePageCoords.y;
  
  // Set the datatip and marker color
  this._markerInnerCircle.setSolidFill(dataColor);
  
  // Position the data cursor and its subcomponents
  if(bHoriz) {
    // Translate the entire data cursor to the lineCoord
    this.setTranslateX(plotAreaBounds.x);
    this.setTranslateY(lineCoord);
    
    // Position the marker
    this._marker.setTranslateX(dataX - plotAreaBounds.x);
    this._marker.setTranslateY(dataY - lineCoord);
    
    // Position the cursor line
    this._cursorLineRect.setWidth(Math.max(tooltipX + DvtDataCursor._DATATIP_BORDER_WIDTH/2 - plotAreaBounds.x, 0));
    if(bRtl) {
      var startX = tooltipX + tooltipBounds.w + DvtDataCursor._DATATIP_BORDER_WIDTH/2 - plotAreaBounds.x;
      this._cursorLineRect.setX(startX);
      this._cursorLineRect.setWidth(Math.max(plotAreaBounds.w - startX, 0));
    }
  }
  else { // Vertical
    this.setTranslateX(lineCoord);
    this.setTranslateY(plotAreaBounds.y + DvtDataCursor._DATATIP_BORDER_WIDTH/2); // half the border lies outside the rect
    
    // Position the marker
    this._marker.setTranslateX(dataX - lineCoord);
    this._marker.setTranslateY(dataY - plotAreaBounds.y);
    
    // Position the cursor line
    this._cursorLineRect.setTranslateY(tooltipY + tooltipBounds.h - (plotAreaBounds.y + DvtDataCursor._DATATIP_BORDER_WIDTH/2));
    this._cursorLineRect.setHeight(Math.max(plotAreaBounds.y + plotAreaBounds.h - tooltipY - tooltipBounds.h, 0));  
  }
}

/**
 * Returns true if this is a data cursor for a horizontal graph.
 * @return {boolean}
 */ 
DvtDataCursor.prototype.isHorizontal = function() {
  return this._bHoriz;
}

/**
 * Specifies the font for the data cursor text.
 * @param {DvtFont}
 */
DvtDataCursor.prototype.setFont = function (font) {
  if(font)
    this._font = font;
}

/**
 * Specifies the color of the data cursor line.
 */
DvtDataCursor.prototype.setLineColor = function (lineColor) {
  this._cursorLineRect.setSolidFill(lineColor);
}

/**
 * Specifies the fill for the datatip.
 * @param {DvtFill} fill
 */
DvtDataCursor.prototype.setTooltipFill = function (tooltipFill) {
  // TODO remove this function and clean up all usages (in the graph)
}

/**
 * Returns the behavior of the data cursor.
 * @return {string}
 */
DvtDataCursor.prototype.getBehavior = function () {
  return this._behavior ? this._behavior : DvtDataCursor.BEHAVIOR_AUTO;
}

/**
 * Specifies the behavior of the data cursor.
 * @param {string} behavior
 */
DvtDataCursor.prototype.setBehavior = function (behavior) {
  this._behavior = behavior;
}
// Legend selector.
/**
 * @constructor
 */
var   DvtLegendSelector = function(context)
{
    this._context = context;
    this._x = 0;
    this._y = 0;

   this._width = 10;
   this._height = 10;

   DvtLegendSelector.superclass.Init.call(this, context, "legendselector");

   // The root for legend selector
   this._container = new DvtRect(context, 0, 0, this._width, this._height);
}

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

/**
 * Renders this legend selector.
 */
DvtLegendSelector.prototype.Render = function() {
    
    // First remove previous rendering
    if (this._container)
        this.removeChild(this._container);   
    if (this._pointer)
        this._container.removeChild(this._pointer);

    // Position container
    var cursorX = this._x;
    var cursorY = this._y;
    var cursorHeight = 10;
    var cursorWidth = 10;

    // Arrow
    var pointerCmds = DvtPathUtils.moveTo(cursorX,cursorY) + 
                      DvtPathUtils.lineTo(cursorX+cursorWidth,cursorY-cursorHeight/2) + 
                      DvtPathUtils.lineTo(cursorX+cursorWidth,cursorY+cursorHeight/2) + 
                      DvtPathUtils.closePath();

    var pointer = new DvtPath(this._context, pointerCmds);
    var dataCursorArrowFill = new DvtSolidFill("rgb(193, 194, 211)", 1);

    pointer.setFill(dataCursorArrowFill);
    this._pointer = pointer;

    this._container.addChild(pointer);
    
    // Add container    
    this.addChild(this._container);

}

DvtLegendSelector.prototype.setX = function(x) {
    this._x = x;
}

DvtLegendSelector.prototype.setY = function(y) {
    this._y = y;
}

DvtLegendSelector.prototype.getX = function() {
    return this._x;
}

DvtLegendSelector.prototype.getY = function() {
    return this._y;
}
// Copyright (c) 2011, 2013, Oracle and/or its affiliates. 
// All rights reserved. 
/**
  *   Creates a line drawn from (x1, y1) to (x2, y2).
  *   @extends DvtLine
  *   @constructor  
  *   @param {DvtContext} context The context object
  *   @param {number} x1  An x end-position of the line.
  *   @param {number} y1  The associated y end-point (for x1).
  *   @param {number} x2  The other end-point's x coordinate.
  *   @param {number} y2  The y coordinate associated with x2.
  *   @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var  DvtGraphDetLine = function(context, x1, y1, x2, y2, id)
{
   this.Init(context, x1, y1, x2, y2, id) ;
};

DvtObj.createSubclass(DvtGraphDetLine, DvtLine, "DvtGraphDetLine") ;

/**
 * @protected
 */
DvtGraphDetLine.prototype.Init = function(context, x1, y1, x2, y2, id)
{
  DvtGraphDetLine.superclass.Init.call(this, context, x1, y1, x2, y2, id);
  
  this._bSelected = false;
  this._bSelecting = false;
  this._dataColor = "#000000";
  
  //properties related to selection
  this._savedStroke = null;
  this._relatedDetObjects = null;
  this._selHighlightLine = null;
};


/*--------------------------------------------------------------------*/
/*  init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    Implementation of DvtDetObj.init()
 */
DvtGraphDetLine.prototype.init = function()
{
   var detObj = this._Dlo;
   var chart = detObj.getChart() ;
   this._dataColor = chart.getSeriesColor(detObj.getSeriesIndex());
   
   if (detObj.isSelectable())
   {
     var strokeObj = this.getStroke();
     if (strokeObj instanceof DvtGradientFill)
     {
       this._dataColor = strokeObj.getColors()[0];
     }
     else if (strokeObj instanceof DvtSolidStroke)
     {
       this._dataColor = strokeObj.getColor();
     }
     
     this.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
   }

};


/**
 * @protected
 * Apply any selection effects to this object.
 */
DvtGraphDetLine.prototype.ApplySelectionState = function()
{
  if (this._bSelected)
  {
    //offset the highlight line by half the width of the line itself, up to 2 
    //pixels
    var coordOffset = -(this.getStroke().getWidth() / 2.0);
    if (coordOffset < -2)
    {
      coordOffset = -2;
    }
    var selLine = new DvtLine(this.getCtx(),
                              this.getX1() + coordOffset, 
                              this.getY1() + coordOffset,
                              this.getX2() + coordOffset, 
                              this.getY2() + coordOffset);
    var stroke = DvtSelectionEffectUtils.createSelectingStroke(this._dataColor);
    if (this.getStroke())
    {
      stroke.setWidth(this.getStroke().getWidth());
    }
    selLine.setStroke(stroke);
    selLine.setMouseEnabled(false);
    this._selHighlightLine = selLine;
    this.addChild(selLine);
    
    //undo hover effect
    if (this._bSelecting) {
        stroke = this._savedStroke;
        this.setStroke(stroke);
    }
  }
  else
  {
    if (this._selHighlightLine)
    {
      this.removeChild(this._selHighlightLine);
      this._selHighlightLine = null;
    }
  }
};


/**
 * @protected
 * Apply any mouse hover selection effects to this object.
 */
DvtGraphDetLine.prototype.ApplySelectingState = function()
{
  var stroke;
  //if selecting and not selected, just change the color of the line to the 
  //highlight color
  if (!this._bSelected)
  {
    if (this._bSelecting)// && !this._bSelected)
    {
      stroke = DvtSelectionEffectUtils.createSelectingStroke(this._dataColor);
      if (this.getStroke())
      {
        stroke.setWidth(this.getStroke().getWidth());
      }
      this._savedStroke = this.getStroke();
      this.setStroke(stroke);
    }
    else
    {
      stroke = this._savedStroke;
      this._savedStroke = null;
      this.setStroke(stroke);
    }
  }
};


/**
 * Show the selection hover effect.
 */
DvtGraphDetLine.prototype.showHoverEffect = function()
{
  this.SetSelectionMouseOver(true);
};


/**
 * Hide the selection hover effect.
 */
DvtGraphDetLine.prototype.hideHoverEffect = function()
{
  this.SetSelectionMouseOver(false);
};


/**
 * Determine if the selection hover effect is shown.
 * 
 * @type boolean
 */
DvtGraphDetLine.prototype.isHoverEffectShown = function()
{
  return this._bSelecting;
};


/**
 * @protected
 * Set whether the selection mouse hover effect is shown.
 * 
 * @param bOver {boolean}  bOver  true if the mouse is over the object,
 *        false otherwise
 */
DvtGraphDetLine.prototype.SetSelectionMouseOver = function(bOver)
{
  this.SetSelectionMouseOverSelf(bOver);

  //set selection on related parts of this marker also
  if (this._relatedDetObjects)
  {
    for (var i = 0; i < this._relatedDetObjects.length; i++)
    {
      if (this._relatedDetObjects[i].SetSelectionMouseOverSelf)
      {
        this._relatedDetObjects[i].SetSelectionMouseOverSelf(bOver);
      }
    }
  }
}

/**
 * @protected
 * Set whether the selection mouse hover effect is shown.
 * 
 * @param bOver {boolean}  bOver  true if the mouse is over the object,
 *        false otherwise
 */
DvtGraphDetLine.prototype.SetSelectionMouseOverSelf = function(bOver)
{
  if (this._bSelecting != bOver)
  {
    var detObj = this._Dlo;
    var objId = detObj.getObjId();
    var bSelectionFeedback = this.IsShowSelectionFeedbackObjId(objId);
    
    this._bSelecting = bOver;
    
    if (bSelectionFeedback)
    {
      this.ApplySelectingState();
    }
  }
};


/**
 * Determine whether this object is selected.
 * 
 * @type boolean
 */
DvtGraphDetLine.prototype.isSelected = function()
{
  return this._bSelected;
};

/**
 * Set whether the selection effect is shown.
 * 
 * @param bOver {boolean}  bSel  true if the object is selected,
 *        false otherwise
 */
DvtGraphDetLine.prototype.setSelected = function(bSel)
{
  this.SetSelectedSelf(bSel);

  //set selection on related parts of this marker also
  if (this._relatedDetObjects)
  {
    for (var i = 0; i < this._relatedDetObjects.length; i++)
    {
      if (this._relatedDetObjects[i].SetSelectedSelf)
      {
        this._relatedDetObjects[i].SetSelectedSelf(bSel);
      }
    }
  }
}

/**
 * @protected
 * Set whether the selection effect is shown.
 * 
 * @param bOver {boolean}  bSel  true if the object is selected,
 *        false otherwise
 */
DvtGraphDetLine.prototype.SetSelectedSelf = function(bSel)
{
  if (this._bSelected != bSel)
  {
    var detObj = this._Dlo;
    var objId = detObj.getObjId();
    var bSelectionFeedback = this.IsShowSelectionFeedbackObjId(objId);
    
    this._bSelected = bSel;
    
    if (bSelectionFeedback)
    {
      this.ApplySelectionState();
    }
  }
};

/**
 * Determine if selection feedback should be shown for the object.
 * 
 * @param oId objectId
 * 
 * @return true if selection feedback should be shown, false if not
 */
DvtGraphDetLine.prototype.IsShowSelectionFeedbackObjId = function(oId)
{
  return (oId !== DvtGraphObjType.STOCK_HIGH_LINE &&
          oId !== DvtGraphObjType.STOCK_LOW_LINE);
};


/**
 * Add a related object of this det object.
 *
 * @param {DvtDisplayable} displayable A DvtDisplayable object related to this one
 */
DvtGraphDetLine.prototype.addRelatedObject = function(detObj)
{
  if (!this._relatedDetObjects)
  {
    this._relatedDetObjects = [];
  }

  this._relatedDetObjects.push(detObj);
};
// Copyright (c) 2011, 2013, Oracle and/or its affiliates. 
// All rights reserved. 
/**
  * Creates a graph polygon from an array of consecutive (x,y) coordinates.
  * @extends DvtGraphSelectablePolygon
  * @constructor  
  * @param {DvtContext} context   The platform dependent context object (such
  *                               as {@link DvtSvgContext}).
  * @param {Array} ar  An array of consecutive x,y coordinate pairs.
  * @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  */
var  DvtGraphDetPolygon = function(context, arPoints, id)
{
   this.Init(context, arPoints, id) ;
};

DvtObj.createSubclass(DvtGraphDetPolygon, DvtGraphSelectablePolygon, "DvtGraphDetPolygon") ;

/**
  *  Object initializer.
  *  @protected
  */
DvtGraphDetPolygon.prototype.Init = function(context, arPoints, id)
{
  DvtGraphDetPolygon.superclass.Init.call(this, context, arPoints, id) ;

  this._3DBar            = null;
};

/**
 * @override
 */
DvtGraphDetPolygon.prototype.showHoverEffect = function()
{
  if (this._3DBar)
    this._3DBar.showHoverEffect();
  else
    this.SetSelectionMouseOver(true);
};

/**
 * @override
 */
DvtGraphDetPolygon.prototype.hideHoverEffect = function()
{
  if (this._3DBar)
    this._3DBar.hideHoverEffect();
  else 
    this.SetSelectionMouseOver(false);
};

/**
 * @override
 */
DvtGraphDetPolygon.prototype.isHoverEffectShown = function()
{
  if (this._3DBar)
    return this._3DBar.isHoverEffectShown();
  else
    return this._bSelecting;
};

/**
 * @override
 */
DvtGraphDetPolygon.prototype.isSelected = function()
{
  if (this._3DBar)
    return this._3DBar.isSelected();
  else
    return this._bSelected;
};

/**
 * @override
 */
DvtGraphDetPolygon.prototype.setSelected = function(bSel)
{
  if (this._3DBar)
    this._3DBar.setSelected(bSel);
  else
    this.setSelected(bSel);
}

/**
 * Set the containing Dvt3DBar, if there is one.
 *
 * @param {Dvt3DBar}  bar3d  containing Dvt3DBar object
 */
DvtGraphDetPolygon.prototype.set3DBar = function(bar3d)
{
  this._3DBar = bar3d;
};
// Panel used for marquee operations
/**
 * @constructor
 */
var   DvtGraphMarqueePanel = function(context)
{
    this.Init(context);
}  

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

DvtGraphMarqueePanel.DEFAULT_BACKGROUND_COLOR = "#ffffff";

DvtGraphMarqueePanel.prototype.Init = function(context) {
   if (! this.getImpl()) {
     this.setImpl(context.getImplFactory().newContainer("marqueepanel")) ;
   }
   this._backgroundFill = new DvtSolidFill(DvtGraphMarqueePanel.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");
}

DvtGraphMarqueePanel.prototype.Render = function() {
    this._rect.setWidth(this._panelWidth);
    this._rect.setHeight(this._panelHeight);
}

DvtGraphMarqueePanel.prototype.setPanelSize = function(width, height) {
    this._panelWidth = width;
    this._panelHeight = height;
}

DvtGraphMarqueePanel.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);
}
// Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
/*--------------------------------------------------------------------*/
/*    DvtDataChangeManager                                            */
/*--------------------------------------------------------------------*/
/**
  *  Animates the transition between two DvtGraphCore's.
  *  @class DvtDataChangeManager
  *  @constructor
  */
var  DvtDataChangeManager = function(graph)
{
   this._Init(graph) ;
} ;

DvtObj.createSubclass(DvtDataChangeManager, DvtObj, "DvtDataChangeManager");

/**
  *  Builds two (supplied) arrays of data change handlers (such as {@link DvtDC3DBar}
  *  for the old and new charts. Also creates this._Y1Animation list of gridline
  *  playables if axis scale change.
  *  @private
  */ 
DvtDataChangeManager.prototype._buildAnimLists = function(aOldRisers, oldChart, aNewRisers, newChart)
{
   //------------//
   //  OLD Chart //
   //------------//

   var  i,j, k ;
   var  dch ;
   var ar, ar2 ;
   var aLen, aLen2 ;
   var aOut ;

   //  Flatten out the riser arrays for the old and new charts into aOldRisers and aNewRisers.

   ar   = oldChart.getRisers() ;      // ar is an array of arrays in series index order
   aOut = aOldRisers ;

   for (i = 0; i < 2; i++) {
      if (ar) {
        aLen = ar.length ;
        for (j = 0; j < aLen; j++) {
           ar2   = ar[j] ;
           if (ar2) {
             aLen2 = ar2.length
             for (k = 0; k < aLen2; k++) {
                dch = ar2[k]._Dlo._DCHandler ;
                if (dch)
                  aOut.push(dch) ;
             }
           }
        }
      }

      ar   = newChart.getRisers() ;    //  Now handle new chart 
      aOut = aNewRisers ;
   }

   
   //-----------------//
   // Gauge related   //
   //-----------------//
   if (oldChart.isGauge() && newChart.isGauge()) {
       var oldDlos = oldChart.getDLOs();
       var newDlos = newChart.getDLOs();
       for (i=0; i<oldDlos.length; i++) {
           dch = oldDlos[i]._DCHandler;
           if (dch)
           aOldRisers.push(dch);
       }
       
       for (i=0; i<newDlos.length; i++) {
           dch = newDlos[i]._DCHandler;
           if (dch)
           aNewRisers.push(dch);
       }
   }

   //----------------//
   //  Pie related   //
   //----------------//
   
   // Pies are handled slightly differently from other risers, since they are logical 
   // objects that are rendered on the client.
   if (oldChart.isPie() && newChart.isPie()) {
     var oldPie = oldChart.__getPieChart();
     var newPie = newChart.__getPieChart();

     if (oldPie && newPie) {
       aOldRisers.push(oldPie); 
       aNewRisers.push(newPie);
     }
   }
   
   //----------------//
   //  Axis related  //
   //----------------//

   var bScaleChange = this._isY1AxisScaleChange() ;

   if (bScaleChange) {                    // If y1 axis scale change
     this._buildY1AxisAnimation() ;       // need animators for it.
   }

   var axis = oldChart.getXAxis() ;       // old x-axis labels
   aOut     = aOldRisers ;

   for (i = 0; i < 2; i++) {
      if (axis) {
        ar = axis.getLabels() ;
        if (ar) {
          arLen = ar.length ;
          for (j = 0; j < arLen; j++) {
             dch = ar[j]._Dlo._DCHandler ;
             if (dch)  {
               aOut.push(dch) ;
             }
          }
        }
      }

      axis = newChart.getXAxis() ;        // new x-axis labels
      aOut = aNewRisers ;
   }

};


/*--------------------------------------------------------------------*/
/*   _buildY1AxisAnimation()                                          */
/*--------------------------------------------------------------------*/

DvtDataChangeManager.prototype._buildY1AxisAnimation = function()
{
   var  ctx            = this._graph.getCtx() ;
   var  dur            = (this._newChart.getAnimation().dataChangeDuration)/1000 ;
   var  delContainer   = new DvtContainer(ctx, 'YDelTainer') ;
   var  ah             = new DvtDataAnimationHandler(ctx, delContainer) ;
   var  nodePlayable ;
   var  obj;

   this._Y1DelContainer = delContainer ;

   //  Fade-out the old grid lines

   var axis = this._oldChart.getY1Axis() ;
   if (axis) {
     ar = axis.getGridLines() ;           // old y1-axis gridlines
     if (ar) {

       Y1AxisIndex = axis.getAxisLine() ;

       arLen = ar.length ;
       for (i = 0; i < arLen; i++) {
          obj = ar[i] ;
          delContainer.addChild(obj);     // Move from the old chart to the delete
                                          // container on top of the new chart.
          nodePlayable = new DvtAnimFadeOut(ctx, obj, dur);
          ah.add(nodePlayable, 0);
       }
     }
   }

   //  Fade-out the old labels

   if (axis) {
     ar = axis.getLabels() ;              // old y1-axis labels
     if (ar) {
       arLen = ar.length ;
       for (i = 0; i < arLen; i++) {
          obj = ar[i] ;
          delContainer.addChild(obj);     // Move from the old chart to the delete
                                          // container on top of the new chart.
          nodePlayable = new DvtAnimFadeOut(ctx, obj, dur);
          ah.add(nodePlayable, 0);
       }
     }
   }

   //  Fade-in the new grid lines

   axis = this._newChart.getY1Axis() ;
   if (axis) {
     ar = axis.getGridLines() ;           // new y1-axis gridlines
     if (ar) {
       arLen = ar.length ;
       for (i = 0; i < arLen; i++) {
          obj = ar[i] ;
          nodePlayable = new DvtAnimFadeIn(ctx, obj, dur);
          ah.add(nodePlayable, 0);
       }
     }
   }

   //  Fade-in and scroll-up the new Y1 labels from the base line.

   var x, y, inity, alpha;
   if (axis) {
     ar = axis.getLabels() ;              // new y1-axis labels
     if (ar) {
       arLen = ar.length ;
       for (i = 0; i < arLen; i++) {
          obj   = ar[i] ;
          x     = obj.getX() ;            // final
          y     = obj.getY() ;            // position.
          alpha = obj.getAlpha() ;        // and alpha.

          if (i === 0) {
            inity = y ;                   // note y position.
          }

          nodePlayable = new DvtCustomAnimation(ctx, this, dur);
          nodePlayable.getAnimator().addProp(DvtAnimator.TYPE_NUMBER, obj,
                                        obj.getX, obj.setX, x);
          nodePlayable.getAnimator().addProp(DvtAnimator.TYPE_NUMBER, obj,
                                        obj.getY, obj.setY, y);
          nodePlayable.getAnimator().addProp(DvtAnimator.TYPE_NUMBER, obj,
                                        obj.getAlpha, obj.setAlpha, alpha);
          ah.add(nodePlayable, 0);
          obj.setY(inity) ;
          obj.setAlpha(0) ;
       }
     }
   }

   this._Y1Animation = ah.getAnimation() ;
   this._Y1Animation.setOnEnd(this._onY1End, this) ;

};


/**
 *   @private
 */
DvtDataChangeManager.prototype._onY1End = function()
{
   //  Remove the container of deleted objects from the new chart.

   if (this._Y1DelContainer.getNumChildren() > 0) {
     this._newChart.removeChild(this._Y1DelContainer) ;
     this._Y1DelContainer.destroy();
   }
   this._Y1DelContainer = null ;
};


/*--------------------------------------------------------------------*/
/*   _canAnimate                                                      */
/*--------------------------------------------------------------------*/
/**
  *  Checks if animation between the two charts is possible.
  *  @returns {boolean} true if animation can be performed, else false.
  *  @private
  */
DvtDataChangeManager.prototype._canAnimate = function()
{
   //  Test for conditions for which we will not animate.
   var  oldChart = this._oldChart ;
   var  newChart = this._newChart ;
   
   if (!oldChart || !newChart)
     return false;
   if ((oldChart.getType() !== newChart.getType()) || (oldChart.is3D() !== newChart.is3D()))
     return false;                  // will not animate
   if (!oldChart.isGauge() && !newChart.isGauge()) {
       // BUG #12804904: Pie graphs never have risers, but animation is still allowed
       if (oldChart.getType() != 'PIE') { 
            if (oldChart.getRisers() === null || newChart.getRisers() === null) {
                return false;                  // will not animate
            }
       }
   }

   //------ temp  JRM TODO remove as more support is added --
   return (oldChart.getType().indexOf('BUBBLE') >= 0      ||
           oldChart.getType().indexOf('SCATTER') >= 0     ||
           oldChart.getType().indexOf('LINE') >= 0        ||
           oldChart.getType().indexOf('AREA') >= 0        ||
           oldChart.getType().indexOf('COMBINATION') >= 0 ||
           oldChart.getType().indexOf('BAR') >= 0         || 
           oldChart.getType().indexOf('STATUSMETER') >= 0 ||
           oldChart.getType().indexOf('DIAL') >= 0        ||
           oldChart.getType() === 'PIE') ;
} ;


/**
  *   @private
  */
DvtDataChangeManager.prototype._clear = function()
{
   this._bAnimating      = false ;
   this._oldChart        = null ;
   this._newChart        = null ;
   this._delContainer    = null ;
   this._Y1DelContainer  = null ;
   this._Y1Animation     = null ;
};


/*--------------------------------------------------------------------*/
/*   handleDataChange()                                               */
/*--------------------------------------------------------------------*/
/**
  *   Perform the data change animation between two charts.
  *   @param {DvtGraphCore}  oldChart  the existing displayed chart.
  *   @param {DvtGraphCore}  newChart  the new chart that will be the finally displayed. 
  *   @param {DvtGraphAnimation} anim  the details of the animation.
  *   @returns {boolean} true if animation handled, or false if animation is not posssible.
  */
DvtDataChangeManager.prototype.handleDataChange = function(oldChart, newChart, anim)
{
   this._oldChart = oldChart ;
   this._newChart = newChart ;

   if (! this._canAnimate()) {     // check if we deem charts animatable.
     return false ;                // will not animate
   }

   if (this._animation) {          // stop any possible animation in progress
     this._animation.stop() ;
     this._animation = null ;
   }

   // Build arrays of old and new data change handlers.

   var aOldRisers = [];
   var aNewRisers = [];
   this._buildAnimLists(aOldRisers, oldChart, aNewRisers, newChart) ;

   // Prepare for animation

   var  ctx           = this._graph.getCtx() ;
   this._delContainer = new DvtContainer(ctx, 'DelTainer') ;
   var  ah            = new DvtDataAnimationHandler(ctx, this._delContainer) ;

   //  Walk the datachange handler arrays, and create animators for risers
   //  that need to be updated/deleted/inserted.

   ah.constructAnimation(aOldRisers, aNewRisers) ;
   var numPlayables = ah.getNumPlayables() ;

   if (numPlayables > 0) {
     this._animation = ah.getAnimation() ;
     this._animation.setOnEnd(this._onEnd, this) ;
   }

   //  At this point, any deleted objects have been removed from the old chart
   //  and moved to the riser delete container.  This will be rendered before 
   //  the first riser in the new chart to prevent deleted objects jumping to
   //  the front of the new chart at the start of animation.

   this._graph.addChild(newChart) ;
   newChart.render() ;                        // render the new chart

   if (numPlayables > 0) {

     if (this._delContainer.getNumChildren() > 0) {  // render delete container if not empty.

       var risers = this._newChart.getRisers().slice(0) ;
       var firstRiser;
       if (risers){
         while(!firstRiser)
           firstRiser = risers.shift();
         firstRiser = firstRiser[0] ;
         firstRiser = this._newChart.getChildIndex(firstRiser) ;
         newChart.addChildAt(this._delContainer, firstRiser) ;  
       }
       else {
         newChart.addChild(this._delContainer) ;  
       }
     }

     //  All grid lines to be faded out (i.e. grid lines in the old chart)
     //  have been moved to the delete container.  This will be rendered before any risers,
     //  so that the lines do not appear to move to the front as animation starts.

     if (this._Y1Animation && (this._Y1DelContainer.getNumChildren() > 0))  {

       var Y1AxisIndex = -1 ;        // Y1Axis container index in new chart
       Y1AxisIndex = this._newChart.getY1Axis().getAxisLine() ;
       Y1AxisIndex = this._newChart.getChildIndex(Y1AxisIndex) ;

       newChart.addChildAt(this._Y1DelContainer, Y1AxisIndex) ; //  Y1 axis delete container
     }
   }

   this._graph.removeChild(oldChart) ;
   oldChart.destroy();

   if (numPlayables > 0) {
     this._bAnimating = true ;        // note animation in progress.
                                      // see also this._onEnd()
     if (this._Y1Animation) {
       this._Y1Animation.play() ;     // start the Y1 axis animation
     }                                // if scale change.

     this._animation.play() ;         // start the datachange animation.
   }

   return true ;                      // return animation was started.
};


/*----------------------------------------------------------------------*/
/*   isY1AxisScaleChange()                                              */
/*----------------------------------------------------------------------*/
/**
  * Determines whether the Y1-axis underwent a scale change. This method
  * also updates  this._bY1Overflow and this._bY1Underflow.
  * @private
  * @returns {boolean}  true if the axis had a scale change, else false.
  */
DvtDataChangeManager.prototype._isY1AxisScaleChange = function()
{
   var ds = this._oldChart.getDataSummary() ;

   var oldY1MinVal = ds.y1MinVal ;
   var oldY1MaxVal = ds.y1MaxVal ;

   ds = this._newChart.getDataSummary() ;
   var newY1MinVal = ds.y1MinVal ;
   var newY1MaxVal = ds.y1MaxVal ;

   this._bY1Overflow  = (newY1MaxVal > oldY1MaxVal) ;
   this._bY1Underflow = (newY1MaxVal < oldY1MaxVal) ;
   
   return (this._bY1Overflow || this._bY1Underflow) ;
};


/*--------------------------------------------------------------------*/
/*   _Init()                                                          */
/*--------------------------------------------------------------------*/
/**
  *   Initializes the object instance.
  *   @private
  */
DvtDataChangeManager.prototype._Init = function(graph)
{
   // Define the vars used.

   this._clear() ;
   this._graph        = graph ;
   this._bY1Overflow  = false ;  // scale change because y axis has a higher max
   this._bY1Underflow = false ;  //   ..     ..    ..    .  ..   .. . lower  ..

};


/*--------------------------------------------------------------------*/
/*   isAnimating()                                                    */
/*--------------------------------------------------------------------*/
/**
  *   Returns true if animation is in progress.
  *   @returns {boolean} true if animation is in progress, else false.
  */
DvtDataChangeManager.prototype.isAnimating = function()
{
    return this._bAnimating ;
};

/*--------------------------------------------------------------------*/
/*   _onEnd()                                                         */
/*--------------------------------------------------------------------*/
/**
  *  End of datachange animation handling.
  *  @private
  */
DvtDataChangeManager.prototype._onEnd = function()
{
   //  Remove the container of deleted objects from the new chart.

   if (this._delContainer && (this._delContainer.getNumChildren() > 0))   {
     this._newChart.removeChild(this._delContainer) ;
     this._delContainer.destroy();
   }

   if (this._newChart._indicators != null){
      var indicators = this._newChart._indicators;
      for (var i=0; i < indicators.length; i++){
          if (indicators[i]){
              this._newChart.removeChild(indicators[i]);
              indicators[i].destroy();
          }
      }
      this._newChart._indicators = null;
   }
   this._delContainer = null ;
   this._bAnimating   = false ;
   this._animation    = null ;

   if (this._newChart.isBubbleChart())
     this._graph.resetXML();
};


/*--------------------------------------------------------------------*/
/*   stopAnimating()                                                  */
/*--------------------------------------------------------------------*/
/**
  *  End of datachange animation handling.
  *  @private
  */
DvtDataChangeManager.prototype.stopAnimating = function()
{
   if (this._bAnimating) {
     if (this._Y1Animation) {
       this._Y1Animation.stop() ;
     }

     this._animation.stop() ;
   }
};
// Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
/*-------------------------------------------------------------------------*/
/*  DvtDCUtils()                                                           */
/*-------------------------------------------------------------------------*/

// Utilities for Graph data change animations

/**
  *   @constructor
  */
var DvtDCUtils = new Object;

DvtObj.createSubclass(DvtDCUtils, DvtObj, "DvtDCUtils");

DvtDCUtils.DIR_UP       = 0;
DvtDCUtils.DIR_DOWN     = 1;
DvtDCUtils.DIR_NOCHANGE = 2 ;

/**
 * Returns the direction of data change for use with animation indicators
 * @param {DvtGraphData} oldData chart data for the old chart
 * @param {number} oldSIdx old series index
 * @param {number} oldGIdx old group index 
 * @param {DvtGraphData} newData chart data for the new chart
 * @param {number} newSIdx new series index
 * @param {number} newGIdx new group index
 * @param {number} type data type (x, y, or z) to compare for direction change
 */
DvtDCUtils.getDirection = function(oldData, oldSIdx, oldGIdx, newData, newSIdx, newGIdx, type)
{
   var newValue = newData.getValue(type, newSIdx, newGIdx);
   var oldValue = oldData.getValue(type, oldSIdx, oldGIdx);
   
   if (newValue < oldValue) {
     return DvtDCUtils.DIR_DOWN;
   }
   else if (newValue > oldValue) {
       return DvtDCUtils.DIR_UP;
   }
   else {
       return DvtDCUtils.DIR_NOCHANGE;
   }
};

/**
  *   Creates a DvtPath object that draws the arrow shape used for animation indicators
  *   @returns {DvtPath}  A DvtPath object for the indicator.
  */
DvtDCUtils.drawDirectionPointer = function(bDown, xPos, yPos, lw, fc, fa, context)
{
  var ptrOffset = lw + 12;

  if (bDown) {
    yPos += ptrOffset;
  }
  else {
    yPos -= ptrOffset;
  }
  
  var ptrCmds;

  if (bDown) {
    ptrCmds = DvtPathUtils.moveTo(-5, 0) +
              DvtPathUtils.lineTo(5, 0)  +
              DvtPathUtils.lineTo(0, 7)  +
              DvtPathUtils.lineTo(-5, 0);
  }
  else {
    ptrCmds = DvtPathUtils.moveTo(-5, 0) +
              DvtPathUtils.lineTo(5, 0)  +
              DvtPathUtils.lineTo(0, -7) +
              DvtPathUtils.lineTo(-5, 0);
  }
  DvtPathUtils.closePath();
    
  var pointer     = new DvtPath(context, ptrCmds);
  var pointerFill = new DvtSolidFill(fc, fa);

  pointer.setFill(pointerFill);
  pointer.setTranslateX(xPos);
  pointer.setTranslateY(yPos);

  return pointer;
};


/**
  *  Helper function for determining which groups were inserted into the
  *  new chart, or deleted from the old chart, or exist in both charts (updates).
  *  @param {Array} oldList  array of groups in the old chart
  *  @param {Array} newList  array of groups in the new chart
  *  @returns {Array} an array of arrays.  array[0] is an array of updated
  *                   objects, ar[1] is an array of inserted objects, and
  *                   ar[2] is an array of deleted objects.
  */
DvtDCUtils.compareGroups = function(oldList, newList, isLA)
{
  var updates = new Array();       // )  return
  var inserts = new Array();       // )  values
  var deletes = new Array();       // )
  var oldLen  = oldList.length ; 

  newList = newList.slice(0);      // Copy the new objects list, since we will modify it

  if (oldList) {
    for (var oldIndex = 0; oldIndex < oldLen; oldIndex++) {
       var oldItem = oldList[oldIndex];
    
       // Loop through the new list looking for a match
       var bMatchFound = false;
       for (var newIndex = 0; newIndex < newList.length; newIndex++) {
          var newItem = newList[newIndex];
      
          if (oldItem === newItem) {
            // Match found, remove the item from the new list since it's handled
            newList.splice(newIndex, 1);
            if (isLA) // if Line or Area, need to keep groups in order
              inserts = inserts.concat(newList.splice(0, newIndex)); // anything before the match is an insert
            bMatchFound = true;
            updates.push(oldItem);
            break;
          }
       }
    
      // If no match found, it was a delete.

      if (! bMatchFound) {
        deletes.push(oldItem);
      }
    }
  }

  // All remaining objects in newList are inserts

  for (var i = 0; i < newList.length; i++) {
     if (newList[i])                        // must be valid object for insert
       inserts.push(newList[i]);
  }
    
  return [updates, inserts, deletes];
};
// Copyright (c) 2008, 2011, Oracle and/or its affiliates. 
// All rights reserved. 
/*--------------------------------------------------------------------*/
/*   DvtDCAbstract             Base Data change Handler               */
/*--------------------------------------------------------------------*/
/*  Data Change Handlers must implement the following methods:        */
/*      animateUpdate()                                               */
/*      animateInsert()                                               */
/*      animateDelete()                                               */
/*      getId()                                                       */
/*--------------------------------------------------------------------*/

/**
  *  Base data change Handler.
  *  @extends DvtObj
  *  @class DvtDCAbstract  Base data change handler.
  *  @constructor  
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {DvtDisplayable} obj   The {@link DvtDisplayable} derivative to be
  *                                animated on datachange.
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *  <p>
  *  @returns {DvtDCAbstract} A new DvtDCabstract object.
  */
var  DvtDCAbstract = function(context, obj, id)
{
   this.Init(context, obj, id) ;
};

DvtObj.createSubclass(DvtDCAbstract, DvtObj, "DvtDCAbstract");

DvtDCAbstract.UPDATE = 0 ;
DvtDCAbstract.DELETE = 1 ;
DvtDCAbstract.INSERT = 2 ;


/*--------------------------------------------------------------------*/
/*  animateUpdate()                                                   */
/*--------------------------------------------------------------------*/
/**
 * Creates the update animation.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can
 *                                          be used to chain animations.
 * @param {DvtDCAbstract} oldDC  The old object's DC handler to animate from.
 */
DvtDCAbstract.prototype.animateUpdate = function(handler, oldDC)
{
};


/*--------------------------------------------------------------------*/
/*  animateInsert()                                                   */
/*--------------------------------------------------------------------*/
/**
 * Creates the insert animation.
 * @param {DvtDataAnimationHandler} handler The animation handler, which
 *                                  can be used to chain animations.
 * @return {DvtPlayable} The animation to perform for this insert.
 */
DvtDCAbstract.prototype.animateInsert = function(handler)
{
};


/*--------------------------------------------------------------------*/
/*  animateDelete()                                                   */
/*--------------------------------------------------------------------*/
/**
 * Creates the delete animation.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be used to
 *                                          chain animations.
 * @param {DvtContainer} delContainer   The container to which the deleted objects should
 *                                      be moved for animation.
 * @returns {DvtPlayable} The animation to perform for this delete.
 */
DvtDCAbstract.prototype.animateDelete = function(handler, delContainer)
{
};


/*--------------------------------------------------------------------*/
/*  destroy()                                                         */
/*--------------------------------------------------------------------*/
/**
  *   Removes references to any resources used.
  */
DvtDCAbstract.prototype.destroy = function()
{
   this._obj      = null ;
   this._Dlo      = null ;
   this._newChart = null ;
};


/*---------------------------------------------------------------------*/
/*  getChart()                                                         */
/*---------------------------------------------------------------------*/
/** 
  *  @returns {DvtGraphCore}  the owning chart for this obj/datachange handler.
  */
DvtDCAbstract.prototype.getChart = function()
{
   return  this._Dlo.getChart() ;
};


/*---------------------------------------------------------------------*/
/*  getId()                                                            */
/*---------------------------------------------------------------------*/
/** 
  *  @returns {String}  a unique Id for object comparison during
  *  datachange animation of two charts.
  */
DvtDCAbstract.prototype.getId = function()
{
   return  this._animId ;
};


/*--------------------------------------------------------------------*/
/*  getNewChart()                                                     */
/*--------------------------------------------------------------------*/
/**
  *   Returns the new chart for which this datachange handler is associated for animation.
  *   This is specifically for use by delete animation, so that the animation handler
  *   can refer to the new chart being animated.
  *   @returns {DvtGraphCore}  the new chart.
  */
DvtDCAbstract.prototype.getNewChart = function(chart)
{
   return this._newChart ;
};


/*---------------------------------------------------------------------*/
/*  Init()                                                             */
/*---------------------------------------------------------------------*/
/**
  *  Object initializer.
  */
DvtDCAbstract.prototype.Init = function(context, obj, id)
{
  this._obj    = obj ;
  this._Dlo    = obj._Dlo ;
  this._animId = null ;
} ;


/*--------------------------------------------------------------------*/
/*  setNewChart()                                                     */
/*--------------------------------------------------------------------*/
/**
  *   Updates this datachange handler with the "new" chart involved with the animation.
  *   @param {DvtGraphCore} chart  the new chart.
  */
DvtDCAbstract.prototype.setNewChart = function(chart)
{
   this._newChart = chart ;
};
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. 
// All rights reserved. 
/*--------------------------------------------------------------------*/
/*   DvtDC2DBar    Data change Handler for 2D Bar Riser component     */
/*--------------------------------------------------------------------*/
/*  Data Change Handlers must override the following base methods:    */
/*      animateUpdate()                                               */
/*      animateInsert()                                               */
/*      animateDelete()                                               */
/*      getId()     (optional)                                        */
/*--------------------------------------------------------------------*/

/**
  *  Data change Handler for 2D Bar Riser.
  *  @extends DvDCAbstract
  *  @class DvtDC2DBar  Data change Handler for 2D Bar Riser.
  *  @constructor  
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {Dvt2DBarRiser} obj  The Dvt2DBarRiser object to be animated on datachange.
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *  <p>
  *  @returns {DvtDC2DBar} A new DvtDC2DBar object.
  */
var  DvtDC2DBar = function(context, obj, id)
{
   this.Init(context, obj, id) ;
};

DvtObj.createSubclass(DvtDC2DBar, DvtDCAbstract, "DvtDC2DBar");



/*--------------------------------------------------------------------*/
/*  animateUpdate()    override of DvtDCAbstract.animateUpdate()      */
/*--------------------------------------------------------------------*/
/**
 * Creates the update animation for this 2D Bar.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can
 *                                          be used to chain animations.
 * @param {DvtDC2DBar} oldNode The old node state to animate from.
 */
DvtDC2DBar.prototype.animateUpdate = function(handler, oldNode) {

   var chart = this._Dlo.getChart() ;                // the new chart

   // Create the animator for this node

   var startState = oldNode.getAnimationParams();
   var endState   = this.getAnimationParams();

   if (DvtArrayUtils.equals(startState, endState))   // If no bar geometry change,
     return ;                                        // nothing to animate.

   var newData     = chart.getDataObj();
   var oldData     = oldNode._Dlo.getChart().getDataObj();
   var oldSIdx     = oldNode._Dlo.getSeriesIndex();
   var oldGIdx     = oldNode._Dlo.getGroupIndex();
   var newSIdx     = this._Dlo.getSeriesIndex();
   var newGIdx     = this._Dlo.getGroupIndex();
   
   var dur         = (chart.getAnimation().dataChangeDuration)/1000 ;
   var anim        = chart.getAnimation();
   var stacked     = chart.getType().indexOf('STACK') >= 0 ;
   
   //  Animate indicators if requested

   if (anim.indicators != DvtGraphAnimation.NONE) {

     var bVertical   = chart.isVertical();
     var uiDirection = bVertical? DvtDCUtils.getDirection(oldData, oldSIdx, oldGIdx, newData, newSIdx, newGIdx, DvtGraphData.DV_Y) :
                                  DvtDCUtils.getDirection(oldData, oldSIdx, oldGIdx, newData, newSIdx, newGIdx, DvtGraphData.DV_X);
     if (uiDirection != DvtDCUtils.DIR_NOCHANGE) {

       var bDown = (uiDirection == DvtDCUtils.DIR_DOWN);

       this._offset   = -10;
      
       var xpos = startState[0];
       var ypos = startState[1];
      
       if (stacked){
           xpos += startState[2]/2;    // minus triangle half width
           ypos += startState[3]/2;    // minus triangle half height
       }
       else{
           if (bVertical) {
               xpos += startState[2] / 2;
               this._offset = -10;
           }
           else {
               ypos += startState[3]/2;
               this._offset = 4;
           }
       }
      
       var fc, fa;

       if (bDown) {
         fc = anim.downColor;
         fa = anim.downAlpha;
       }
       else {
         fc = anim.upColor;
         fa = anim.upAlpha;
       }
      
       //  Create a path object that draws the indicator
       var dirptr = DvtDCUtils.drawDirectionPointer(bDown, xpos, ypos, this._offset,
                                                    fc, fa, this._obj.getCtx()) ;
       chart.addChild(dirptr); 
       chart.addUpdateIndicators(dirptr) ;  // add to chart's list for cleanup at end.
       this._pointer = dirptr ;
     }
   }
    
   //  Animate the bar update

   var nodePlayable = new DvtCustomAnimation(this._obj.getCtx(), this, dur);
   nodePlayable.getAnimator().addProp(DvtAnimator.TYPE_NUMBER_ARRAY, this,
                                      this.getAnimationParams, this.setAnimationParams, endState);
   
   handler.add(nodePlayable, 0);           // create the playable
   this.setAnimationParams(startState);    // initialize the start state

};

/*--------------------------------------------------------------------*/
/*  animateInsert()      override of DvtDCAbstract.animateInsert()    */
/*--------------------------------------------------------------------*/
/**
 * Creates the insert animation for this 2D Bar.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be used to chain animations.
 * @return {DvtPlayable} The animation to perform for this insert.
 */
DvtDC2DBar.prototype.animateInsert = function(handler)
{
   var dur        = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;
   var obj        = this._obj ;

   // Changed starting width to 1 to fix bug 12661350
   var startState = [obj.getX() + obj.getWidth()/2, obj.getY()+obj.getHeight(), 1, 1, 0] ; 
   var endState   = [obj.getX(), obj.getY(), obj.getWidth(), obj.getHeight(), obj.getAlpha()] ; 

   var nodePlayable = new DvtCustomAnimation(obj.getCtx(), this, dur);
   nodePlayable.getAnimator().addProp(DvtAnimator.TYPE_NUMBER_ARRAY, this,
                                      this.getAnimationParams, this.setAnimationParamsPlusAlpha, endState);
   
   handler.add(nodePlayable, 0);                    // create the playable
   this.setAnimationParamsPlusAlpha(startState);    // set the start state
};


/*--------------------------------------------------------------------*/
/*  animateDelete()      override of DvtDCAbstract.animateDelete()    */
/*--------------------------------------------------------------------*/
/**
 * Creates the delete animation for this 2D Bar.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be used to
 *                                          chain animations.
 * @param {DvtContainer} delContainer   The container to which the deleted objects should
 *                                      be moved for animation.
 * @returns {DvtPlayable} The animation to perform for this delete.
 */
DvtDC2DBar.prototype.animateDelete = function(handler, delContainer)
{
   delContainer.addChild(this._obj);   // Move from the old chart to the delete
                                       // container on top of the new chart.

   var dur      = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;
   var obj      = this._obj ;
   var endState = [obj.getX() + obj.getWidth()/2, obj.getY()+obj.getHeight(), 0, 0, 0] ; 

   var nodePlayable = new DvtCustomAnimation(obj.getCtx(), this, dur);
   nodePlayable.getAnimator().addProp(DvtAnimator.TYPE_NUMBER_ARRAY, this,
                                      this.getAnimationParams, this.setAnimationParamsPlusAlpha, endState);
   
   handler.add(nodePlayable, 0);     // create the playable
};



/*--------------------------------------------------------------------*/
/*  getAnimationParams()                                              */
/*--------------------------------------------------------------------*/
/**
  *   Returns the geometry of the bar.
  *   @returns {Array} an array containing the rectangle points of the
  *   bar, plus the alpha as the last element of the array.
  */
DvtDC2DBar.prototype.getAnimationParams = function()
{
    var ar  = [] ;
    var obj = this._obj ;

    ar[0]  = obj.getX() ;
    ar[1]  = obj.getY() ;
    ar[2]  = obj.getWidth() ;
    ar[3]  = obj.getHeight() ;
    ar[4]  = this._obj.getAlpha() ;

    return ar ;
};


/*---------------------------------------------------------------------*/
/*  Init()                                                             */
/*---------------------------------------------------------------------*/
/**
  *  Object initializer.
  */
DvtDC2DBar.prototype.Init = function(context, obj, id)
{
   DvtDC2DBar.superclass.Init.call(this, context, obj, id) ;

   var dlo       = this._Dlo ;
   this._animId   = dlo.getSeriesId()  + '/' + dlo.getGroupId() + '/' + dlo.getObjId() ;
   this._pointer  = null ;        // update indicator
} ;


/*--------------------------------------------------------------------*/
/*  setAnimationParams()                                              */
/*--------------------------------------------------------------------*/
/**
  *   Updates the geometry of the bar.
  *   @param {Array} ar  an array containing the rectangle points.
  */
DvtDC2DBar.prototype.setAnimationParams = function(ar)
{
   //  Note: to avoid having to figure out whether the bars go above or below the
   //  axis, or to the left or right of the axis, we simply interpolate all the 
   //  rectangle properties.
   //  However: while Flash renders smoothly, SVG's use of fractional pixel values
   //  causes (say for vertical bars) the bottom of the bar to flicker during
   //  animation as the y and height vary by a pixel independently, causing the base
   //  of the bar to move above and then back down to the axis for example.  To get
   //  round this (bug 12701757) the interpolated values are rounded

   ar[0] = Math.round(ar[0]) ;        // x
   ar[1] = Math.round(ar[1]) ;        // y
   ar[2] = Math.round(ar[2]) ;        // width
   ar[3] = Math.round(ar[3]) ;        // height

   this._obj.setRect(ar[0], ar[1], ar[2], ar[3]) ;  // does not use last element of alpha
   
   // Update the bounds of the gradient so it doesn't shift during animation
   var bounds = [ar[0], ar[1], ar[2], ar[3]];
   var fill = this._obj.getFill();
   var newFill;
   if (fill instanceof DvtLinearGradientFill) {
       newFill = new DvtLinearGradientFill(fill.getAngle(), fill.getColors(), fill.getAlphas(), fill.getStops(), bounds);
       this._obj.setFill(newFill);
   }
   
   // Check for update direction indicators.

   if (this._pointer != null) {
      var chart     = this._Dlo.getChart() ;
      var bVertical = chart.isVertical();
      var pointer   = this._pointer;

      chart.removeChild(pointer);               // keep indicator on top of z-order
      var stacked  = (chart.getType().indexOf('STACK') >= 0);
      var xpos, ypos;
        
      if (stacked) {
        xpos = ar[0] + (ar[2]/2);
        ypos = ar[1] + (ar[3]/2);
      }
      else {
        if (bVertical) {
          xpos = ar[0] + (ar[2]/2);
          ypos = ar[1] + this._offset;
        }
        else{
          xpos = ar[0] + ar[2] + this._offset;
          ypos = ar[1] + (ar[3]/2);
        }
      }
        
      pointer.setTranslateX(xpos);
      pointer.setTranslateY(ypos);
      chart.addChild(pointer);
   }

};


/*--------------------------------------------------------------------*/
/*  setAnimationParamsPlusAlpha()                                     */
/*--------------------------------------------------------------------*/
/**
  *   Updates the geometry of the bar and its alpha.
  *   @param {Array} ar  an array containing the rectangle points,
  *   with the alpha value as the last element of the array.
  */
DvtDC2DBar.prototype.setAnimationParamsPlusAlpha = function(ar)
{
   this._obj.setRect(ar[0], ar[1], ar[2], ar[3]) ;
   this._obj.setAlpha(ar[4]) ;
};

// Copyright (c) 2008, 2013, Oracle and/or its affiliates. 
// All rights reserved. 
/*--------------------------------------------------------------------*/
/*   DvtDC3DBar    Data change Handler for 3D Bar Riser component     */
/*--------------------------------------------------------------------*/
/*  Data Change Handlers must override the following base methods:    */
/*      animateUpdate()                                               */
/*      animateInsert()                                               */
/*      animateDelete()                                               */
/*      getId()     (optional)                                        */
/*--------------------------------------------------------------------*/
/*                                                                    */
/*                           +----+   +       Rectangular (and        */
/*                    BRB   /    /   /|       diamond) cross-section  */
/*                         +----+   / |                               */
/*                         +----+  +  |                               */
/*                         |    |  |  |                               */
/*                         |    |  |  |                               */
/*                         |    |  |  |                               */
/*                         |    |  |  |                               */
/*                         | BR |  |  +                               */
/*                         |    |  | /                                */
/*                         |    |  |/  BRS                            */
/*                         +----+  +                                  */
/*                                                                    */
/*  p="botLeftX, botLeftY, botRightX, botRightY, topRightX, topRightY,*/
/*       [0]        [1]       [2]        [3]        [4]        [5]    */
/*                                                                    */
/*     topLeftX, topLeftY, botLeftX, botLeftY"                        */
/*       [6]        [7]      [8]       [9]                            */
/*--------------------------------------------------------------------*/
/*                       +   + - - +   +                              */
/*                       |\   \   /   / |    Triangular               */
/*                       | \   \ /   /  |    cross-section            */
/*                       |  +   +   +   |                             */
/*                       |  | BRB   |   |    Note that front and      */
/*                       |  |       |   |    side could be consid-    */
/*                       |  |       |   |    ered interchanged        */
/*                       |  |       |   |    compared to the rect-    */
/*                       |  |       |   |    angular c/s above.       */
/*                       +  |       |   +                             */
/*                        \ |       |  /                              */
/*                     BR  \|       |/  BRS                           */
/*                          +       +                                 */
/*                                                                    */
/*                        BR        BRS                               */
/*  For BR and BRS                                                    */
/*  p="botLeftX, botLeftY, botRightX, botRightY, topRightX, topRightY,*/
/*       [0]        [1]       [2]        [3]        [4]        [5]    */
/*                                                                    */
/*     topLeftX, topLeftY, botLeftX, botLeftY"                        */
/*       [6]        [7]      [8]       [9]                            */
/*                                                                    */
/*                                                                    */
/*  For BRB:                                                          */
/*  p="botX, botY, topRightX, topRightY, topLeftX, topLeftY           */
/*     [0]   [1]      [2]        [3]        [4]      [5]              */
/*                                                                    */
/*     botX, botY                                                     */
/*     [6]   [7]                                                      */
/*--------------------------------------------------------------------*/

/**
  *  Data change Handler for 3D Bar Riser.
  *  @extends DvtDCAbstract
  *  @class DvtDC3DBar  Data change Handler for 2D Bar Riser.
  *  @constructor  
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {Dvt3DBarRiser} obj  The Dvt2DBarRiser object to be animated on datachange.
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *  <p>
  *  @returns {DvtDC3DBar} A new DvtDC3DBar object.
  */
var   DvtDC3DBar = function(context, obj, id)
{
   this.Init(context, obj, id) ;
}  

DvtObj.createSubclass(DvtDC3DBar, DvtDCAbstract, "DvtDC3DBar");


/*--------------------------------------------------------------------*/
/*  animateUpdate()    override of DvtDCAbstract.animateUpdate()      */
/*--------------------------------------------------------------------*/
/**
 * Creates the update animation for this bar.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be
 *                                          used to chain animations.
 * @param {Dvt2DBarRiser} oldNode The old node state to animate from.
 */
DvtDC3DBar.prototype.animateUpdate = function(handler, oldNode) {

   // Create the animator for this node

   var startState = oldNode.getAnimationParams();
   var endState   = this.getAnimationParams();

   if (DvtArrayUtils.equals(startState, endState))   // If no bar geometry change,
     return ;                                        // nothing to animate.

   var newData = this._Dlo.getChart().getDataObj();
   var oldData = oldNode._Dlo.getChart().getDataObj();
   var oldSIdx = oldNode._Dlo.getSeriesIndex();
   var oldGIdx = oldNode._Dlo.getGroupIndex();
   var newSIdx = this._Dlo.getSeriesIndex();
   var newGIdx = this._Dlo.getGroupIndex();
   
   var dur      = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;
   var anim     = this._Dlo.getChart().getAnimation();
   var stacked  = (this._Dlo.getChart().getType().indexOf('STACK') >= 0);


   //  Check for animation indicators

   if (anim.indicators != DvtGraphAnimation.NONE) {

     var bVertical   = this._Dlo.getChart().isVertical();
     var uiDirection = bVertical?  DvtDCUtils.getDirection(oldData, oldSIdx, oldGIdx, newData, newSIdx, newGIdx, DvtGraphData.DV_Y) :
                                   DvtDCUtils.getDirection(oldData, oldSIdx, oldGIdx, newData, newSIdx, newGIdx, DvtGraphData.DV_X) ;
    
     if (uiDirection != DvtDCUtils.DIR_NOCHANGE)  {

       var bDown = (uiDirection == DvtDCUtils.DIR_DOWN);

       this._offset = -10;
       var disp = 20; 
       var xpos = startState[disp + 0];             // top face, bottom left x
       var ypos = startState[disp + 7];             // top face, top left y
       
       if (stacked) {
         xpos = startState[0];                      // front face, bottom left x
         ypos = startState[7];                      // front face, top left y
         xpos += (startState[2]-startState[0])/2;   // minus triangle half width
         ypos += (startState[1]-startState[7])/2;   // minus triangle half height
       }
       else {
         if (bVertical){
           xpos += (startState[disp+2]-startState[disp+0])/2;
           this._offset = -10;
         }
         else {
           ypos += (startState[disp+1]-startState[disp+7])/2;
           this._offset = 4;
         }
       }
       
       var fc, fa;

       if (bDown) {
         fc = anim.downColor;
         fa = anim.downAlpha;
       }
       else {
         fc = anim.upColor;
         fa = anim.upAlpha;
       }
       
       //  Create a direction pointer object (DvtPath)_
       var dirptr = DvtDCUtils.drawDirectionPointer(bDown, xpos, ypos, this._offset,
                                                    fc, fa, this._obj.getCtx()) ;
       this._Dlo.getChart().addChild(dirptr); 
       this._Dlo.getChart().addUpdateIndicators(dirptr) ;
       this._pointer = dirptr ;
     }
   }         // end of indicator support



   var nodePlayable = new DvtCustomAnimation(this._obj.getCtx(), this, dur);
   nodePlayable.getAnimator().addProp(DvtAnimator.TYPE_NUMBER_ARRAY, this,
                                       this.getAnimationParams, this.setAnimationParams, endState);
   
   handler.add(nodePlayable, 0);           // create the playable
   this.setAnimationParams(startState);    // initialize the start state
};


/*--------------------------------------------------------------------*/
/*  animateInsert()      override of DvtDCAbstract.animateInsert()    */
/*--------------------------------------------------------------------*/
/**
 * Creates the insert animation for this bar.
 * @param {DvtDataAnimationHandler} handler The animation handler, which
 *                                          can be used to chain animations.
 * @return {DvtPlayable} The animation to perform for this insert.
 */
DvtDC3DBar.prototype.animateInsert = function(handler)
{
   //  Refer to the diagram of the polygon points used, at the top of this file.

   var i ;
   var obj = this._obj ;
   var dur = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;

   //  Get start state

   var startState = [] ;
   var disp       = 0 ;         // disp into startState
   
   var bTri =  (obj.getTop().getPoints().length  === 8);  // check top for shape

   var ar   = obj.getFront().getPoints() ;
   var len  = ar.length ;
   for (i = 0; i < len; i++) {
      startState.push(ar[i]) ;
   }

   var mid = bTri? ar[0] : (ar[0] + (ar[2] - ar[0])/ 2);     // mid x frontface if rectangular.
                                                             // mid vertical between
                                                             // front/side if triangle.
   startState[0] = mid ;           // bottom left x 
   startState[2] = mid ;           // bottom right x
   startState[4] = mid ;           // top right x
   startState[6] = mid ;           // top left x
   startState[8] = mid ;           // bottom left x

   disp += len ;
   ar = obj.getSide().getPoints() ;
   len  = ar.length ;
   for (i = 0; i < len; i++) {
      startState.push(ar[i]) ;
   }

   startState[disp+0] =  mid ;     // bottom left x
   startState[disp+2] =  mid ;     // bottom right x
   startState[disp+4] =  mid ;     // top right x
   startState[disp+6] =  mid ;     // top left x
   startState[disp+8] =  mid ;     // bottom left x
   disp += len ;

   ar = obj.getTop().getPoints() ;
   len  = ar.length ;
   for (i = 0; i < len; i++) {
      startState.push(ar[i]) ;
   }

   if (bTri) {                           // triangle c/s
     startState[disp+2] = mid ;             // top right x
     startState[disp+4] = mid ;             // top left x
     startState[disp+3] = ar[1] ;           // top right y
     startState[disp+5] = ar[1] ;           // top left y
   }
   else {                                // rectangular/diamond c/s
     var halfWidth = (ar[2] - ar[0])/ 2;    // half bar width
     mid = ar[0] + halfWidth ;              // mid x frontface

     startState[disp+0] +=  halfWidth ;     // bottom left x
     startState[disp+2] -=  halfWidth ;     // bottom right x
     startState[disp+4] -=  halfWidth;      // top right x
     startState[disp+6] +=  halfWidth ;     // top left x
     startState[disp+8] +=  halfWidth ;     // bottom left x
   }
   startState.push(0) ;                     // start alpha


   //  Now get end state

   var endState = [] ;

   ar  = obj.getFront().getPoints() ;
   for (var i = 0; i < 3; i++) {

      len = ar.length ;

      for (j = 0; j < len; j++) {
         endState.push(ar[j]) ;
      }

      if (i === 0) {
        ar = obj.getSide().getPoints() ;
      }
      else {
        ar = obj.getTop().getPoints() ;
      }
   }

   endState.push(obj.getAlpha()) ;   // end alpha


   var nodePlayable = new DvtCustomAnimation(obj.getCtx(), this, dur);
   nodePlayable.getAnimator().addProp(DvtAnimator.TYPE_NUMBER_ARRAY, this,
                                      this.getAnimationParams, this.setAnimationParamsPlusAlpha, endState);
   
   handler.add(nodePlayable, 0);                    // create the playable
   this.setAnimationParamsPlusAlpha(startState);    // set the start state
};


/*--------------------------------------------------------------------*/
/*  animateDelete()      override of DvtDCAbstract.animateDelete()    */
/*--------------------------------------------------------------------*/
/**
 * Creates the delete animation for this bar.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be used to
 *                                          chain animations.
 * @param {DvtContainer} delContainer   The container to which the deleted objects should
 *                                      be moved for animation.
 * @return {DvtPlayable} The animation to perform for this delete.
 */
DvtDC3DBar.prototype.animateDelete = function(handler, delContainer)
{
   //  Refer to the diagram of the polygon points used, at the top of this file.

   var obj = this._obj ;

   delContainer.addChild(obj);     // Move from the old chart to the delete
                                   // container on top of the new chart.

   var dur  = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;
   dur *= 0.8 ;

   //  Get end state

   var endState = [] ;
   var disp     = 0 ;         // disp into startState
   var bTri     =  (obj.getTop().getPoints().length  === 8);  // check top for shape

   var ar  = obj.getFront().getPoints() ;
   var len = ar.length ;
   for (i = 0; i < len; i++) {
      endState.push(ar[i]) ;
   }

   var mid = bTri? ar[0] : (ar[0] + (ar[2] - ar[0])/ 2);     // mid x frontface if rectangular
                                                             // mid vertical between
                                                             // front/side if triangle.
   var frontTopY   = ar[5] ;         //    ..     top right Y.
   var frontBotY   = ar[3] ;         //    ..     bottom right Y.

   endState[0] = mid ;     // bottom left x
   endState[2] = mid ;     // bottom right x
   endState[4] = mid ;     // top right x
   endState[6] = mid ;     // top left x
   endState[8] = mid ;     // bottom left x
   if (bTri) {
     endState[7] = frontTopY ;   // top left y
     endState[9] = frontBotY ;   // top left y
   }
   disp += len ;

   ar   = obj.getSide().getPoints() ;
   len  = ar.length ;
   for (i = 0; i < len; i++) {
      endState.push(ar[i]) ;
   }

   endState[disp+0] =  mid ;         // bottom left x
   endState[disp+2] =  mid ;         // bottom right x
   endState[disp+4] =  mid ;         // top right x
   endState[disp+6] =  mid ;         // top left x
   endState[disp+8] =  mid ;         // bottom left x
   endState[disp+5] =  frontTopY ;   // top right y
   endState[disp+3] =  frontBotY ;   // bottom right y
   disp += len ;

   ar = obj.getTop().getPoints() ;
   len  = ar.length ;
   for (i = 0; i < len; i++) {
      endState.push(ar[i]) ;
   }

   if (bTri) {                     // triangle c/s
     endState[disp+2] =                // top right x
     endState[disp+4] =                // top left x
     endState[disp+3] =  frontTopY ;   // top right y
     endState[disp+5] =  frontTopY ;   // top left y
   }
   else {                          // rectangular/diamond c/s
     endState[disp+0] =  mid ;         // bottom left x
     endState[disp+2] =  mid ;         // bottom right x
     endState[disp+4] =  mid;          // top right x
     endState[disp+6] =  mid ;         // top left x
     endState[disp+8] =  mid ;         // bottom left x
     endState[disp+5] =  frontTopY ;   // top right y
     endState[disp+7] =  frontBotY ;   // bottom right y
   }
   endState.push(0) ;                // end alpha


   //  Get start state

   var startState = [] ;

   ar  = obj.getFront().getPoints() ;
   for (var i = 0; i < 3; i++) {

      len  = ar.length ;
      for (j = 0; j < len; j++) {
         startState.push(ar[j]) ;
      }

      if (i === 0) {
        ar = obj.getSide().getPoints() ;
      }
      else {
        ar = obj.getTop().getPoints() ;
      }
   }

   startState.push(obj.getAlpha()) ;   // start alpha

   var nodePlayable = new DvtCustomAnimation(obj.getCtx(), this, dur);
   nodePlayable.getAnimator().addProp(DvtAnimator.TYPE_NUMBER_ARRAY, this,
                                      this.getAnimationParams, this.setAnimationParamsPlusAlpha, endState);
    
   handler.add(nodePlayable, 0);     // create the playable
};


/*--------------------------------------------------------------------*/
/*  getAnimationParams()                                              */
/*--------------------------------------------------------------------*/
/**
  *   Get the geometry of the bar faces.
  *   @returns {Array} an array containing the polygon points for each face.
  *   in the order front, side, top.
  */
DvtDC3DBar.prototype.getAnimationParams = function()
{
   var ar     = [] ;
   var disp   = 0 ;
   var obj    = this._obj ;
   var arFace = obj.getFront().getPoints() ;
   var len ;

   for (var i = 0; i < 3; i++) {

     len  = arFace.length

     for (j = 0; j < len; j++) {
        ar[disp +j] = arFace[j] ;
     }

     if (i === 0) {
       arFace = obj.getSide().getPoints() ;
     }
     else {
       arFace = obj.getTop().getPoints() ;
     }

     disp += len ;
   }

   ar[disp] = this._obj.getAlpha() ;              // last in the sequence is the alpha

   return ar ;
};


/*---------------------------------------------------------------------*/
/*  Init()                                                             */
/*---------------------------------------------------------------------*/
/**
  *  Object initializer.
  */
DvtDC3DBar.prototype.Init = function(context, obj, id)
{
   DvtDC3DBar.superclass.Init.call(this, context, obj, id) ;

   var dlo       = this._Dlo ;
   this._animId  = dlo.getSeriesId()  + '/' + dlo.getGroupId() + '/' + dlo.getObjId() ;
   this._pointer = null ;        // update indicator
} ;


/*--------------------------------------------------------------------*/
/*  setAnimationParams()                                              */
/*--------------------------------------------------------------------*/
/**
  *   Sets the geometry of the bar faces.
  *   @param {Array} ar  an array containing the polygon points
  *   for each face in the order front, side, top.
  */
DvtDC3DBar.prototype.setAnimationParams = function(ar)
{

   var i, j ;
   var disp   = 0 ;
   var obj    = this._obj ;
   var arFace = obj.getFront().getPoints() ;
   var len  ;

   for (i = 0; i < 3; i++) {

      len  = arFace.length ;

      for (j = 0; j < len; j++) {
         arFace[j] = ar[disp +j] ;
      }
      if (i === 0) {
        obj.getFront().setPoints(arFace) ;
        arFace = obj.getSide().getPoints() ;
      }
      else if (i === 1) {
        obj.getSide().setPoints(arFace) ;
        arFace = obj.getTop().getPoints() ;
      }
      else {
        obj.getTop().setPoints(arFace) ;
      }
      disp += len ;
   }
   
   if (this._pointer != null) {
     this._Dlo.getChart().removeChild(this._pointer);    // keep indicator on top of z-order
     var stacked = (this._Dlo.getChart().getType().indexOf('STACK') >= 0);
     var bVertical = this._Dlo.getChart().isVertical();
     var xpos, ypos;
     var disp = 20;
     
     if (stacked){
       xpos = ar[6] + ((ar[4] - ar[6])/2);
       ypos = ar[7] + ((ar[1]-ar[7])/2);
     }
     else {
       if (bVertical) {
           xpos = ar[disp+0] + ((ar[disp+2] - ar[disp+0])/2);
           ypos = ar[disp+7] + this._offset;
       }
       else {
           xpos = ar[disp+0] + (ar[disp+2] -ar[disp+0]) + this._offset;
           ypos = ar[disp+7] + ((ar[disp+1]-ar[disp+7])/2);
       }
     }
     
     this._pointer.setTranslateX(xpos);
     this._pointer.setTranslateY(ypos);
     this._Dlo.getChart().addChild(this._pointer);
   }

};


/*--------------------------------------------------------------------*/
/*  setAnimationParamsPlusAlpha()                                     */
/*--------------------------------------------------------------------*/
/**
  *   Sets the geometry of the bar faces and the alpha.
  *   @param {Array} ar  an array containing the polygon points for each face
  *   in the order front, side, top.  The last array entry is the alpha.
  */
DvtDC3DBar.prototype.setAnimationParamsPlusAlpha = function(ar)
{
   this.setAnimationParams(ar) ;
   this._obj.setAlpha(ar[ar.length-1]) ;
};

// Copyright (c) 2008, 2013, Oracle and/or its affiliates. 
// All rights reserved. 
/*--------------------------------------------------------------------*/
/*   DvtDCMarker           Data change Handler for Markers            */
/*--------------------------------------------------------------------*/
/*  Data Change Handlers must override the following base methods:    */
/*      animateUpdate()                                               */
/*      animateInsert()                                               */
/*      animateDelete()                                               */
/*      getId()     (optional)                                        */
/*--------------------------------------------------------------------*/

/**
  *  Data change Handler for markers.
  *  @extends DvtDCAbstract
  *  @class DvtDCMarker  Data change Handler for legend markers.
  *  @constructor  
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {DvtMarker} obj        The DvtMarker object to be animated on datachange.
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *  <p>
  *  @returns {DvtDCMarker} A new DvtDCMarker object.
  */
var  DvtDCMarker = function(context, obj, id)
{
   this.Init(context, obj, id) ;
};

DvtObj.createSubclass(DvtDCMarker, DvtDCAbstract, "DvtDCMarker");



/*--------------------------------------------------------------------*/
/*  animateUpdate()    override of DvtDCAbstract.animateUpdate()      */
/*--------------------------------------------------------------------*/
/**
 * Creates the update animation for this marker.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be
 *                                          used to chain animations.
 * @param {DvtDCMarker} oldNode The old node state to animate from.
 */
DvtDCMarker.prototype.animateUpdate = function(handler, oldNode) {

   // Create the animator for this node
   var oldObj = oldNode._obj;
   var newObj = this._obj;

   // Transform to the old state and animate to the new one
   var endMatrix = newObj.getMatrix();
   var startMatrix = new DvtMatrix();
   startMatrix.translate(-newObj.getX(), -newObj.getY());
   startMatrix.scale(oldObj.getWidth()/newObj.getWidth(), oldObj.getHeight()/newObj.getHeight());
   startMatrix.translate(oldObj.getX(), oldObj.getY());
   
   // If the text's geometry has changed, animate the change.
   if (! endMatrix.equals(startMatrix)) {
   
     var anim = this._Dlo.getChart().getAnimation();
     var nodePlayable;
     var dur = (anim.dataChangeDuration)/1000 ;
     
     nodePlayable = new DvtCustomAnimation(this._obj.getCtx(), this, dur);
     newObj.setMatrix(startMatrix);    // initialize the start state

     nodePlayable.getAnimator().addProp(DvtAnimator.TYPE_MATRIX, this, this.getAnimationParams, this.setAnimationParams, endMatrix);
                                       
     if(this.isUpdateTrigger(oldNode) && anim.indicators != DvtGraphAnimation.NONE) {  
       // Reuse the old shape as the yellow overlay 
       oldObj.setSolidFill("#FFFF00"); 
       oldObj.removeAllDrawEffects();
       this._Dlo.getChart().addChild(oldObj);
     
       // To create the color changing effect when the marker moves
       nodePlayable.getAnimator().addProp(DvtAnimator.TYPE_NUMBER, oldObj, oldObj.getAlpha, oldObj.setAlpha, 0);
       
       // Move the overlay
       var overlayEndMatrix = new DvtMatrix();
       overlayEndMatrix.translate(-oldObj.getX(), -oldObj.getY());
       overlayEndMatrix.scale(newObj.getWidth()/oldObj.getWidth(), newObj.getHeight()/oldObj.getHeight());
       overlayEndMatrix.translate(newObj.getX(), newObj.getY());
       
       
       nodePlayable.getAnimator().addProp(DvtAnimator.TYPE_MATRIX, oldObj, oldObj.getMatrix, oldObj.setMatrix, overlayEndMatrix);

       // setting the overLay as indicators to reuse the clean up code
       // when animation ends, the overLay object will be removed
       var arOverLay = new Array();
       arOverLay.push(oldObj);
       this._Dlo.getChart()._indicators = arOverLay;  
       
       oldObj.setAlpha(1);                  // set overLay initial Alpha to 1
       
       // Keep a reference so that it can be parented after the real shape
       this._overlay = oldObj;
     }
    
     handler.add(nodePlayable, 0);
   }
};

/*--------------------------------------------------------------------*/
/*  animateInsert()      override of DvtDCAbstract.animateInsert()    */
/*--------------------------------------------------------------------*/
/**
 * Creates the insert animation for this marker.
 * @param {DvtDataAnimationHandler} handler The animation handler, which
 *                                          can be used to chain animations.
 * @return {DvtPlayable} The animation to perform for this insert.
 */
DvtDCMarker.prototype.animateInsert = function(handler)
{
   var dur        = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;
   var obj        = this._obj ;

   var nodePlayable = new DvtAnimFadeIn(obj.getCtx(), obj, dur);
   
   handler.add(nodePlayable, 0);
};


/*--------------------------------------------------------------------*/
/*  animateDelete()      override of DvtDCAbstract.animateDelete()    */
/*--------------------------------------------------------------------*/
/**
 * Creates the delete animation for this marker.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be used to
 *                                          chain animations.
 * @param {DvtContainer} delContainer   The container to which the deleted objects should
 *                                      be moved for animation.
 * @returns {DvtPlayable} The animation to perform for this delete.
 */
DvtDCMarker.prototype.animateDelete = function(handler, delContainer)
{
   delContainer.addChild(this._obj);   // Move from the old chart to the delete
                                       // container on top of the new chart.

   var dur  = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;
   var obj  = this._obj ;

   var nodePlayable = new DvtAnimFadeOut(obj.getCtx(), obj, dur);
   
   handler.add(nodePlayable, 0);
};

/**
  *   Returns the geometry of the marker.
  *   @returns {DvtMatrix} a Matrix containing the position of the text.
  */
DvtDCMarker.prototype.getAnimationParams = function()
{
    var mtx = this._obj.getMatrix().clone();
    return mtx ;
};


/*---------------------------------------------------------------------*/
/*  Init()                                                             */
/*---------------------------------------------------------------------*/
/**
  *  Object initializer.
  */
DvtDCMarker.prototype.Init = function(context, obj, id)
{
   DvtDCMarker.superclass.Init.call(this, context, obj, id) ;

   var dlo      = this._Dlo ;
   var data     = dlo.getChart().getDataObj() ;
   var gi       = dlo.getGroupIndex() ;
   var ar       = data.getGroupLabels() ;
   var sGroup ;

   if (ar) {
     sGroup = ar[gi] ;         // group labels take precedence
   }                           // over group Id
   else {
     sGroup = dlo.getGroupId() ;
   }
  
   this._animId = dlo.getSeriesId()  + '/' + dlo.getGroupId() + '/' + dlo.getObjId() ;
} ;

/*--------------------------------------------------------------------*/
/*  setAnimationParams()                                              */
/*--------------------------------------------------------------------*/
/**
  *   Updates the geometry of the marker.
  *   @param {DvtMatrix} mtx  a matrix containing the rectangle points.
  */
DvtDCMarker.prototype.setAnimationParams = function(mtx)
{   
    this._obj.setMatrix(mtx);
    
    // Reparent so it appears above the real shape
    if(this._overlay)
      this._Dlo.getChart().addChild(this._overlay);
};

/*--------------------------------------------------------------------*/
/*  isUpdateTrigger()                                                 */
/*--------------------------------------------------------------------*/
/**
 * Check if there is data change.
 * @param {DvtDCMarker} oldNode The old node state to animate from.
 */
DvtDCMarker.prototype.isUpdateTrigger = function(oldNode)
{
  var oldSIdx = oldNode._Dlo.getSeriesIndex();
  var oldGIdx = oldNode._Dlo.getGroupIndex();
  var newSIdx = this._Dlo.getSeriesIndex();
  var newGIdx = this._Dlo.getGroupIndex();
  var newData = this._Dlo.getChart().getDataObj();
  var oldData = oldNode._Dlo.getChart().getDataObj();

  if (! oldNode) {
    return false;
  }
  else {
    return !(newData.getValue(DvtGraphData.DV_X, newSIdx, newGIdx) == oldData.getValue(DvtGraphData.DV_X, oldSIdx, oldGIdx) &&
             newData.getValue(DvtGraphData.DV_Y, newSIdx, newGIdx) == oldData.getValue(DvtGraphData.DV_Y, oldSIdx, oldGIdx) &&
             newData.getValue(DvtGraphData.DV_Z, newSIdx, newGIdx) == oldData.getValue(DvtGraphData.DV_Z, oldSIdx, oldGIdx));
  }
};
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. 
// All rights reserved. 
/*--------------------------------------------------------------------*/
/*   DvtDCText           Data change Handler for Text                 */
/*--------------------------------------------------------------------*/
/*  Data Change Handlers must override the following base methods:    */
/*      animateUpdate()                                               */
/*      animateInsert()                                               */
/*      animateDelete()                                               */
/*      getId()     (optional)                                        */
/*--------------------------------------------------------------------*/

/**
  *  Data change Handler for text.
  *  @extends DvtObj
  *  @class DvtDCText  Data change Handler for 2D Bar Riser.
  *  @constructor  
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {DvtText} obj          The DvtText object to be animated on datachange.
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *  <p>
  *  @returns {DvtDCText} A new DvtDCText object.
  */
var  DvtDCText = function(context, obj, id)
{
   this.Init(context, obj, id) ;
};

DvtObj.createSubclass(DvtDCText, DvtDCAbstract, "DvtDCText");



/*--------------------------------------------------------------------*/
/*  animateUpdate()    override of DvtDCAbstract.animateUpdate()      */
/*--------------------------------------------------------------------*/
/**
 * Creates the update animation for this 2D Bar.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be
 *                                          used to chain animations.
 * @param {DvtDCText} oldNode The old node state to animate from.
 */
DvtDCText.prototype.animateUpdate = function(handler, oldNode) {

   // Create the animator for this node

   var startState = oldNode.getAnimationParams();
   var endState   = this.getAnimationParams();

   // If the text's geometry has changed, animate the change.

   if (! DvtArrayUtils.equals(startState, endState)) {

     var nodePlayable;
     var dur = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;

     nodePlayable = new DvtCustomAnimation(this._obj.getCtx(), this, dur);
     nodePlayable.getAnimator().addProp(DvtAnimator.TYPE_NUMBER_ARRAY, this,
                                        this.getAnimationParams, this.setAnimationParams, endState);
    
     handler.add(nodePlayable, 0);
     this.setAnimationParams(startState);    // initialize the start state
  }
};

/*--------------------------------------------------------------------*/
/*  animateInsert()      override of DvtDCAbstract.animateInsert()    */
/*--------------------------------------------------------------------*/
/**
 * Creates the insert animation for this 2D Bar.
 * @param {DvtDataAnimationHandler} handler The animation handler, which
 *                                          can be used to chain animations.
 * @return {DvtPlayable} The animation to perform for this insert.
 */
DvtDCText.prototype.animateInsert = function(handler)
{
   var dur        = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;
   var obj        = this._obj ;

   var nodePlayable = new DvtAnimFadeIn(obj.getCtx(), obj, dur);
   
   handler.add(nodePlayable, 0);
};


/*--------------------------------------------------------------------*/
/*  animateDelete()      override of DvtDCAbstract.animateDelete()    */
/*--------------------------------------------------------------------*/
/**
 * Creates the delete animation for this 2D Bar.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be used to
 *                                          chain animations.
 * @param {DvtContainer} delContainer   The container to which the deleted objects should
 *                                      be moved for animation.
 * @returns {DvtPlayable} The animation to perform for this delete.
 */
DvtDCText.prototype.animateDelete = function(handler, delContainer)
{
   delContainer.addChild(this._obj);   // Move from the old chart to the delete
                                       // container on top of the new chart.

   var dur  = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;
   var obj  = this._obj ;

   var nodePlayable = new DvtAnimFadeOut(obj.getCtx(), obj, dur);
   
   handler.add(nodePlayable, 0);
};


/*--------------------------------------------------------------------*/
/*  getAnimationParams()                                              */
/*--------------------------------------------------------------------*/
/**
  *   Returns the geometry of the bar.
  *   @returns {Array} an array containing the position of the text.
  */
DvtDCText.prototype.getAnimationParams = function()
{
    var ar  = [] ;
    var obj = this._obj ;

    ar[0]  = obj.getX() ;
    ar[1]  = obj.getY() ;

    return ar ;
};



/*---------------------------------------------------------------------*/
/*  Init()                                                             */
/*---------------------------------------------------------------------*/
/**
  *  Object initializer.
  */
DvtDCText.prototype.Init = function(context, obj, id)
{
  DvtDCText.superclass.Init.call(this, context, obj, id) ;

  var dlo = this._Dlo ;
  this._animId = dlo.getSeriesId()  + '/' + dlo.getGroupId() + '/' + dlo.getMiscId() + '/' + dlo.getObjId() ;
} ;


/*--------------------------------------------------------------------*/
/*  setAnimationParams()                                              */
/*--------------------------------------------------------------------*/
/**
  *   Updates the geometry of the bar.
  *   @param {Array} ar  an array containing the rectangle points.
  */
DvtDCText.prototype.setAnimationParams = function(ar)
{
    this._obj.setX(ar[0]) ;
    this._obj.setY(ar[1]) ;   
};
// Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
/*--------------------------------------------------------------------*/
/*   DvtDCLineArea      Data change Handler for Line/Area Riser       */
/*--------------------------------------------------------------------*/
/*  Data Change Handlers must override the following base methods:    */
/*      animateUpdate()                                               */
/*      animateInsert()                                               */
/*      animateDelete()                                               */
/*      getId()     (optional)                                        */
/*--------------------------------------------------------------------*/

/**
  *  Data change Handler for Line and Area risers.
  *  @extends DvtObj
  *  @class DvtDCLineArea  Data change Handler for Line and Area risers.
  *  @constructor  
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {DvtDisplayable} obj   The DvtLineRiser or DvtAreaRiser object to be
  *                                animated on datachange.
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *  <p>
  *  @returns {DvtDCLineArea} A new DvtDCLineArea object.
  */
var  DvtDCLineArea = function(context, obj, id)
{
   this.Init(context, obj, id) ;
};

DvtObj.createSubclass(DvtDCLineArea, DvtDCAbstract, "DvtDCLineArea");


/*--------------------------------------------------------------------*/
/*  animateUpdate()    override of DvtDCAbstract.animateUpdate()      */
/*--------------------------------------------------------------------*/
/**
 * Creates the update animation for this Line or Area. Insert/delete of
 * groups within an existing series is treated as a special case of animateUpdate.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can
 *                                          be used to chain animations.
 * @param {DvtDCLineArea} oldDC  The old object's DC handler to animate from.
 */
DvtDCLineArea.prototype.animateUpdate = function(handler, oldDC) {

   //-----------------------------------------------------------------------------//
   //  Animation summary :                                                        //
   //                                                                             //
   //  Updates  - just perform regular animation from oldState to newState        //
   //                                                                             //
   //  Inserts  - initially position the inserted point into the old point list   //
   //             with an x,y position proportionally between, and on the line    //
   //             connecting, the two non-inserted points on either side in the   //
   //             new initial line.  It can then be tweened to its final position //
   //             in the new list.                                                //
   //                                                                             //
   //  Deletes  - perform the reverse of an insert. The deleted point is inserted //
   //             into the new point list in its original position in the old     //
   //             list. It can then be tweened to an x,y position proportionally  //
   //             between, and on the line connecting, the final position of the  //
   //             two non-deleted points on either side in the new line.          //
   //             It can then be tweened from its original position to its final  //
   //             position on the connecting line.                                //         
   //-----------------------------------------------------------------------------//

   var startState   = oldDC.getAnimationParams();
   var endState     = this.getAnimationParams();
   this._finalState = null;

   if (DvtArrayUtils.equals(startState, endState)) { // any geometry changes ?
     return ;                                        // no, nothing to animate.
   }

   var newData = this._Dlo.getChart().getDataObj();     // new chart's data
   var oldData = oldDC._Dlo.getChart().getDataObj();    // old chart's data

   //  Summarize the group changes as inserts/deletes/updates.

   var arNewGroups   = newData.getGroups();  // groups from new chart
   var arOldGroups   = oldData.getGroups();  // groups from old chart
   var newGroupLen   = arNewGroups.length;
   var oldGroupLen   = arOldGroups.length;

   var arCompare     = DvtDCUtils.compareGroups(arOldGroups, arNewGroups, true);
   var arUpdates     = arCompare[0];
   var arInserts     = arCompare[1];
   var arDeletes     = arCompare[2];
   var bInsertDelete = ((arInserts.length > 0) || (arDeletes.length > 0)) ;

   //  Create an array that represents the type of point (e.g. DvtDCAbstract.INSERT,
   //  DvtDCAbstract.DELETE, or DvtDCAbstract.UPDATE in the initial new polyline/polygon.

   var arPointType = [];         // holds the point type for each line vertex.
   var iPT = 0;                  // index into arPointType

   //  Create an array of logical direction indicators if indicator support requested. 

   this._arPointers  = null;
   this._arDirection = null;

   var anim = this._Dlo.getChart().getAnimation();

   if ((anim.indicators != DvtGraphAnimation.NONE) && (arUpdates.length > 0)) {
       this._arPointers  = [] ;
       this._arDirection = [] ;
   }
    
   // Will create 2 arrays for old and new points of the same length for animation.
   // Inserted and deleted points will exist in both (but with different coords).
   // At the end of the animation, the original new points will be applied to 
   // the polygon to remove the redundant deleted points.

   var arOldPt = new Array();                          // note: only updated
   var arNewPt = new Array();                          // if inserts or deletes.
     
   var aptsOld = oldDC.getAnimationParams();            // the old and new points to loop
   var aptsNew = this.getAnimationParams();             // over while populating arOldPt
   var aptsOldLen = aptsOld.length;                 // and arNewPt.
   var aptsNewLen = aptsNew.length;   

   var newSIdx = this._Dlo.getSeriesIndex();
   var oldSIdx = oldDC._Dlo.getSeriesIndex();
    
   var ixOld    = 0;   // index into the old group list
   var ixNew    = 0;   // index into the new group list
   var ixOldPts = 0;   // index into aptsOld
   var ixNewPts = 0;   // index into aptsNew
    
   //  Loop over both sets of groups.  If inserts/deletes, also build 2 new arrays of
   //  start and ends states for the polyline/polygon vertices in arOldPt and arNewPt.

   while ((ixOld < oldGroupLen) || (ixNew < newGroupLen)) {
        if (ixOld < oldGroupLen && ixNew < newGroupLen  && ixOldPts < aptsOldLen && ixNewPts < aptsNewLen)    {
          if (arOldGroups[ixOld] === arNewGroups[ixNew])    { // not an insert or delete
            arPointType[iPT] = DvtDCAbstract.UPDATE;
               
            var xPos = aptsOld[ixOldPts++];             // get vertice old (x,y)
            var yPos = aptsOld[ixOldPts++];
               
            if (this._arPointers != null){
              this._makeDirPointer(anim, xPos, yPos, oldData, oldSIdx, ixOld,
                                                     newData, newSIdx, ixNew, iPT);
            }
            ixOld++;
            ixNew++;
            iPT++;

            if (! bInsertDelete) {
              continue
            }

            arOldPt.push(xPos);                 // old x
            arOldPt.push(yPos);                 // old y
            arNewPt.push(aptsNew[ixNewPts++]);  // new x
            arNewPt.push(aptsNew[ixNewPts++]);    // new y
           }
           else if (DvtArrayUtils.getIndex(arDeletes, arOldGroups[ixOld]) >= 0) {
              // its a delete
              arPointType[iPT] = DvtDCAbstract.DELETE ;
               
              arOldPt.push(aptsOld[ixOldPts++]);  // old x
              arOldPt.push(aptsOld[ixOldPts++]);    // old y
              arNewPt.push(0);                    // placeholder new x
              arNewPt.push(0);                    // placeholder new y
               
              ixOld++;
               
              if (this._arPointers != null) {
                this._arPointers[iPT]  = null;    // dummy
                this._arDirection[iPT] = null;    // entries
              }
              iPT++;
           }
           else if (DvtArrayUtils.getIndex(arInserts, arNewGroups[ixNew]) > -1) {
              // its an insert
              arPointType[iPT] = DvtDCAbstract.INSERT;
               
              arOldPt.push(0);                    // placeholder old x
              arOldPt.push(0);                    // placeholder old y
              arNewPt.push(aptsNew[ixNewPts++]);  // new x
              arNewPt.push(aptsNew[ixNewPts++]);    // new y
               
              ixNew++;
               
              if (this._arPointers != null) {
                this._arPointers[iPT]  = null;    // dummy
                this._arDirection[iPT] = null;    // entries
              }
              iPT++;
           }
           else {
              ixOld++;
              ixNew++;
           }
       }
       else if (ixOld < oldGroupLen && ixOldPts < aptsOldLen) {             // if we've run out of new entries
           while (ixOld < oldGroupLen  && ixOldPts < aptsOldLen){
              arPointType[iPT] = DvtDCAbstract.DELETE; // the remaining old entries must be deletes
              
              arOldPt.push(aptsOld[ixOldPts++]) ;  // old x
              arOldPt.push(aptsOld[ixOldPts++]) ;    // old y
              arNewPt.push(0) ;                    // placeholder new x
              arNewPt.push(0) ;                    // placeholder new y
               
              ixOld++;
               
              if (this._arPointers != null) {
                this._arPointers[iPT]  = null;     // dummy
                this._arDirection[iPT] = null;     // entries
              }
              iPT++;
           }
           break;
       }
       else {                                      // we must have run out of old entries
           while (ixNew < newGroupLen && ixNewPts < aptsNewLen) {
              arPointType[iPT] = DvtDCAbstract.INSERT; // the remaining new entries must be inserts
              
              arOldPt.push(0) ;                    // placeholder old x
              arOldPt.push(0) ;                    // placeholder old y
              arNewPt.push(aptsNew[ixNewPts++]) ;  // new x
              arNewPt.push(aptsNew[ixNewPts++]) ;    // new y
               
              ixNew++;
               
              if (this._arPointers != null) {
                this._arPointers[iPT]  = null;     // dummy
                this._arDirection[iPT] = null;     // entries
              }
              iPT++;
           }
           break;
       }
   } // end while
    

   //  If we have inserts and/or deletes, we now need to calculate and fill in
   //  the null placeholder values set above in arOldPt and arNewPt.

   //---------------------------------------------------------------------------//
   //  At this point  arPointType contains an entry for each point in the line  //
   //  on initial display (i.e. before animations start).                       //
   //  Each entry contains :                                                    //
   //    DvtDCAbstract.UPDATE  = entry is unchanged or updated                  //
   //    DvtDCAbstract.DELETE  = entry is deleted and will be animated out      //
   //    DvtDCAbstract.INSERT  = entry is inserted and will be animated in      //
   //                                                                           //
   //  Also the initial and final tween positions have been generated for the   //
   //  non-insert/delete entries, the initial postions only have been generated //
   //  for the delete entries, and the final positions only for the insert      //
   //  entries.                                                                 //
   //---------------------------------------------------------------------------//

   if (bInsertDelete) {
      var pt1x   = 0;                   // for computation of a line segment's middle point.
      var pt1y   = 0;
      var pt2x   = 0;
      var pt2y   = 0;
      var xInc   = 0;                   // x and y increments for
      var yInc   = 0;                   // proportional placement
      var iPTLen = arPointType.length;
       
      for (var i = 0; i < iPTLen; i++) {               // loop over arPointType
         if (arPointType[i] == DvtDCAbstract.UPDATE)    // ignore non-insert/delete entries
           continue;
           
         //  Count the # of consecutive entries of the same type
         var n = arPointType[i];          // note the type
         var iConsec = 1;                 // # of consecutive entries
         var ix;                          // temp index
         for (ix = i+1; ix < iPTLen; ix++) {
            if (arPointType[ix] != n)
                break;
            iConsec++;
         }
           
         //  The iConsec's worth of points might be the last (rightmost) points
         //  in the line.
         var bFinalGroup = (ix ===iPTLen);   // note if so
           
         //  If this is the first or last entry, need to do edge case special handling

                                                //-----------------------//
         if (i === 0)                           // is it the first entry //
         {                                      //-----------------------//

            if (n === DvtDCAbstract.INSERT)     // is it an insert
            {                                   // yes, fill-in its initial position
               //  Use the (x,y) coords of the first non-inserted point on the initial
               //  display line position as the initial inserted point(s) position.

               n = (i + iConsec) * 2 ;         // get 1st non-inserted index

               pt1x  =  arOldPt[n] ;
               pt1y  =  arOldPt[n +1] ;

               for (ix = 0; ix < iConsec; ix++)
               {
                  if (ix > 0)
                    i++ ;
                  n = i * 2 ;

                  arOldPt[n++] =  pt1x ;
                  arOldPt[n]   =  pt1y ;
               }
            }
            else                                // its a delete
            {
               //  Use the (x,y) coords of the first non-deleted point on the final
               //  display line position as the final delete point(s) positions.

               n = (i + iConsec) * 2 ;        // get 1st non-deleted index

               pt1x  =  arNewPt[n] ;
               pt1y  =  arNewPt[n +1] ;

               for (ix = 0; ix < iConsec; ix++)
               {
                  if (ix > 0)
                    i++ ;
                  n = i * 2 ;

                  arNewPt[n++] =  pt1x ;
                  arNewPt[n]   =  pt1y ;
               }
            }
         }                                                   //---------------------------//
         else  if ( (i === (iPTLen -1) )  || bFinalGroup)    // is it the right-end entry //
         {                                                   // or final right hand group //
                                                             //---------------------------//

            if (n === DvtDCAbstract.INSERT)     // is it an insert
            {                                   // yes, fill-in its initial position
               //  Use the (x,y) coords of the last non-inserted point on the initial
               //  display line position as the initial inserted position.

               n = (i -1) * 2 ;               // get index of previous entry

               pt1x  =  arOldPt[n] ;
               pt1y  =  arOldPt[n +1] ;

               n = i * 2 ;                   // get index of last entry
               arOldPt[n++] =  pt1x ;
               arOldPt[n]   =  pt1y ;
            }
            else
            {
               //  Use the (x,y) coords of the first non-deleted point on the final
               //  display line position as the final delete point(s) positions.

               n = (i -1) * 2 ;              // get previous non-deleted index
               pt1x  =  arNewPt[n] ;         // and the x and
               pt1y  =  arNewPt[n +1] ;      // y pos.

               for (ix = 0; ix < iConsec; ix++)
               {
                  if (ix > 0)
                    i++ ;
                  n = i * 2 ;

                  arNewPt[n++] =  pt1x ;
                  arNewPt[n]   =  pt1y ;
               }
            }
         }                                      //---------------------------//
         else                                   // must be an internal entry //
         {                                      //---------------------------//

            if (n === DvtDCAbstract.INSERT)     // is it an insert
            {                                   // yes, fill-in its initial position

               //  Get the (x,y) coords of the points on either side of this
               //  group (in their initial display positions) and place the
               //  insert initial positions proportionately on the line joing them.

               //  Have a group of inserts that are not all on the right hand end

               ix = (i -1) * 2 ;              // get previous entry of any type

               pt1x  =  arOldPt[ix++] ;
               pt1y  =  arOldPt[ix] ;

               ix    = (i + iConsec) * 2 ;

               pt2x  =  arOldPt[ix++] ;
               pt2y  =  arOldPt[ix] ;

               xInc  = (pt2x - pt1x) / (iConsec + 1) ;
               yInc  = (pt2y - pt1y) / (iConsec + 1) ;      // (note can be -ve)

               for (ix = 0; ix < iConsec; ix++)
               {
                  if (ix > 0)
                    i++ ;
                  n = i * 2 ;        // get index into old tween points array

                  pt1x += xInc ;
                  pt1y += yInc ;

                  arOldPt[n++] =  pt1x ;
                  arOldPt[n]   =  pt1y ;
               }
            }
            else                                // its a delete
            {                                   // yes, fill-in its final position

               //  Have a group of deletes that are not all on the right hand end

               //  Get the (x,y) coords of the points on either side of this
               //  group (in their final display positions) and place the
               //  deleted initial positions proportionately on the line joing them.

               ix = (i -1) * 2 ;              // get previous entry of any type

               pt1x  =  arNewPt[ix++] ;
               pt1y  =  arNewPt[ix] ;

               ix    = (i + iConsec) * 2 ;

               pt2x  =  arNewPt[ix++] ;
               pt2y  =  arNewPt[ix] ;

               xInc  = (pt2x - pt1x) / (iConsec + 1) ;
               yInc  = (pt2y - pt1y) / (iConsec + 1) ;      // (note can be -ve)

               for (ix = 0; ix < iConsec; ix++)
               {
                  if (ix > 0)
                    i++ ;
                  n = i * 2 ;        // get index into old tween points array

                  pt1x += xInc ;
                  pt1y += yInc ;

                  arNewPt[n++] =  pt1x ;
                  arNewPt[n]   =  pt1y ;
               }
            }
         }
       } // end loop over arPointType for inserts/deletes
       

       //  If this is an area chart or a combo area series, need to add 
       //  the polygon's closing coords to the  starting and ending points.

       if (this._obj instanceof DvtPolygon) {
         ixOldPts = aptsOld.length - 6 ;
         ixNewPts = aptsNew.length - 6 ;
         arOldPt.push(aptsOld[ixOldPts]) ;
         arOldPt.push(aptsOld[++ixOldPts]) ;
         arOldPt.push(aptsOld[++ixOldPts]) ;
         arOldPt.push(aptsOld[++ixOldPts]) ;
         arOldPt.push(aptsOld[++ixOldPts]) ;
         arOldPt.push(aptsOld[++ixOldPts]) ;
         arNewPt.push(aptsNew[ixNewPts]) ;
         arNewPt.push(aptsNew[++ixNewPts]) ;
         arNewPt.push(aptsNew[++ixNewPts]) ;
         arNewPt.push(aptsNew[++ixNewPts]) ;
         arNewPt.push(aptsNew[++ixNewPts]) ;
         arNewPt.push(aptsNew[++ixNewPts]) ;
       }

       // Start and end points array generation complete.

       startState       = arOldPt;
       this._finalState = endState; // save original end state so we can reset points after animating
       endState         = arNewPt;

   }   // end if insert/delete


   // Final processing

   this.setAnimationParams(startState);    // initialize the start state

   if (this._arPointers) {
     this._Dlo.getChart().addUpdateIndicators(this._arPointers) ;
   }
     
   //  Create the animator

   var dur = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;

   var nodePlayable = new DvtCustomAnimation(this._obj.getCtx(), this, dur);
   nodePlayable.getAnimator().addProp(DvtAnimator.TYPE_NUMBER_ARRAY, this,
                                          this.getAnimationParams, this.setAnimationParams, endState);
    
   // Post-processing if the end state has been altered (insert/delete)

   if (this._finalState) {
     nodePlayable.setOnEnd(this._resetPoints, this);
   }
   handler.add(nodePlayable, 0);           // create the playable
};


/**
 * Function to be added on end of animation to reset the line's points
 */
DvtDCLineArea.prototype._resetPoints = function(){
    this._obj.setPoints(this._finalState);
};

/*--------------------------------------------------------------------*/
/*  animateInsert()      override of DvtDCAbstract.animateInsert()    */
/*--------------------------------------------------------------------*/
/**
 * Creates the insert animation for this Line.
 * @param {DvtDataAnimationHandler} handler The animation handler, which
 *                                  can be used to chain animations.
 * @return {DvtPlayable} The animation to perform for this insert.
 */
DvtDCLineArea.prototype.animateInsert = function(handler)
{
   var dur   = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;
   var obj   = this._obj ;

    var nodePlayable = new DvtAnimFadeIn(obj.getCtx(), obj, dur);
 
   handler.add(nodePlayable, 0);                    // create the playable
};


/*--------------------------------------------------------------------*/
/*  animateDelete()      override of DvtDCAbstract.animateDelete()    */
/*--------------------------------------------------------------------*/
/**
 * Creates the delete animation for this Line/Area
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be used to
 *                                          chain animations.
 * @param {DvtContainer} delContainer   The container to which the deleted objects should
 *                                      be moved for animation.
 * @returns {DvtPlayable} The animation to perform for this delete.
 */
DvtDCLineArea.prototype.animateDelete = function(handler, delContainer)
{
//   delContainer.addChild(this._obj);   // Move from the old chart to the delete
                                         // container.

  // Move deleted area displayable to new chart. Note: new chart has
  // not been rendered at this point so need to add obj to new charts's
  // initial object list

    var oldIdx =  this._obj._Dlo.getChart().getChildIndex(this._obj) ;
    if (oldIdx >= 0) {
      this._Dlo.getChart().removeChild(this._obj);                   // Remove from the old chart.
      this.getNewChart().getObjList().splice(oldIdx, 0, this._obj);  // insert in new chart.

      var dur  = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;
      var obj  = this._obj ;

      var nodePlayable = new DvtAnimFadeOut(obj.getCtx(), obj, dur);

      nodePlayable.setOnEnd(this._deleteSeries, this);   // will delete "old" object from
                                                         // the new chart at the end of the animation
      handler.add(nodePlayable, 0);     // create the playable
    }
};

/**
 *   Function to be added on end of animation to remove the deleted series obj from the chart.
 */
DvtDCLineArea.prototype._deleteSeries = function(){
    this.getNewChart().removeChild(this._obj) ;
};


/*--------------------------------------------------------------------*/
/*  getAnimationParams()                                              */
/*--------------------------------------------------------------------*/
/**
  *   Returns the geometry of the line/area.
  *   @returns {Array} an array containing the points of the line
  */
DvtDCLineArea.prototype.getAnimationParams = function()
{
   var ar  = [] ;
   var obj = this._obj ;

   ar = obj.getPoints();
   return ar ;
};


/*---------------------------------------------------------------------*/
/*  Init()                                                             */
/*---------------------------------------------------------------------*/
/**
  *  Object initializer.
  */
DvtDCLineArea.prototype.Init = function(context, obj, id)
{
   DvtDCLineArea.superclass.Init.call(this, context, obj, id) ;

   //  We ignore the group for a polyline, only interested in seeing if the
   //  series matches. All changes to a line are considered to be an update.

   this._animId = this._Dlo.getSeriesId();
} ;


/*---------------------------------------------------------------------*/
/*   _makeDirPointer                                                   */
/*---------------------------------------------------------------------*/

DvtDCLineArea.prototype._makeDirPointer = function(anim, xPos, yPos, oldData, oldSIdx, oldGIdx,
                                                                     newData, newSIdx, newGIdx, iPT)
{
    var dirptr = null;              // return value

    var bDown  = false;
    var iDirection ;
    var fc, fa ;

    iDirection = DvtDCUtils.getDirection(oldData, oldSIdx, oldGIdx,
                                         newData, newSIdx, newGIdx, DvtGraphData.DV_Y) ;
    if (iDirection !== DvtDCUtils.DIR_NOCHANGE) {
      bDown = (iDirection == DvtDCUtils.DIR_DOWN);
      
      if (bDown) {
        fc = anim.downColor;
        fa = anim.downAlpha;
      }
      else {
        fc = anim.upColor;
        fa = anim.upAlpha;
      }

      var  stroke = this._obj.getStroke() ;
      var  lw = stroke ? stroke.getWidth() : 5 ;

      //  Create a direction pointer object
      dirptr = DvtDCUtils.drawDirectionPointer(bDown, xPos, yPos, lw,
                                               fc, fa, this._obj.getCtx());
      this._Dlo.getChart().addChild(dirptr);
    }

    this._arPointers[iPT]  = dirptr ;
    this._arDirection[iPT] = bDown;
};



/*--------------------------------------------------------------------*/
/*  setAnimationParams()                                              */
/*--------------------------------------------------------------------*/
/**
  *   Updates the geometry of the line/area.
  *   @param {Array} ar  an array containing the line/area's points.
  */
DvtDCLineArea.prototype.setAnimationParams = function(ar)
{
   this._obj.setPoints(ar);
   
   if (this._arPointers != null) {
     for (var i = 0; i < this._arPointers.length; i++) {
        var pointer = this._arPointers[i];
        if (pointer) {
          this._Dlo.getChart().removeChild(pointer);

          var offset = (this._arDirection[i] == true)? 12 : -12 ;
          pointer.setTranslateX(ar[i*2]);
          pointer.setTranslateY(ar[i*2+1]+offset);

          this._Dlo.getChart().addChild(pointer);
        }
     }
   }
};


// Copyright (c) 2008, 2013, Oracle and/or its affiliates. 
// All rights reserved. 
/*--------------------------------------------------------------------*/
/*   DvtDCStatusIndicator    Data change Handler for Dial Indicator   */
/*--------------------------------------------------------------------*/
/*  Data Change Handlers must override the following base methods:    */
/*      animateUpdate()                                               */
/*      animateInsert()                                               */
/*      animateDelete()                                               */
/*      getId()     (optional)                                        */
/*--------------------------------------------------------------------*/

/**
  *  Data change Handler for Dial Indicator.
  *  @extends DvtDCAbstract
  *  @class DvtDCDialIndicator  Data change Handler for Dial Indicator.
  *  @constructor  
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {DvtDialIndicator} obj The DvtDialIndicator object to be animated on datachange.
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *  <p>
  *  @returns {DvtDCDialIndicator} A new DvtDCDialIndicator object.
  */
var  DvtDCDialIndicator = function(context, obj, id)
{
   this.Init(context, obj, id) ;
};

DvtObj.createSubclass(DvtDCDialIndicator, DvtDCAbstract, "DvtDCDialIndicator");



/*--------------------------------------------------------------------*/
/*  animateUpdate()    override of DvtDCAbstract.animateUpdate()      */
/*--------------------------------------------------------------------*/
/**
 * Creates the update animation for this dial indicator.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be
 *                                          used to chain animations.
 * @param {DvtDCDialIndicator} oldNode The old node state to animate from.
 */
DvtDCDialIndicator.prototype.animateUpdate = function(handler, oldNode) {
  // Get the indicator value
  var oldVal = oldNode._obj._Dlo.getChart().getGaugeData()._curVal;
  var newVal   = this._obj._Dlo.getChart().getGaugeData()._curVal;
  
  var minVal = this._Dlo._Chart.getGaugeData()._minVal;
  var maxVal = this._Dlo._Chart.getGaugeData()._maxVal;
  
  // If the value is out of range, peg the value to the min or max
  if (oldVal < minVal) oldVal = minVal;
  else if (oldVal > maxVal) oldVal = maxVal;
  if (newVal < minVal) newVal = minVal;
  else if (newVal > maxVal) newVal = maxVal;
   
  var dialAngleStart = this._Dlo._Chart.getGaugeData()._dialAngleStart;
  var dialAngleExtent = this._Dlo._Chart.getGaugeData()._dialAngleExtent;
  
  // old and new Angles in Radians
  var oldAngle = ((Math.abs(oldVal-minVal) / Math.abs(maxVal - minVal)) * dialAngleExtent) * (Math.PI / 180);
  var newAngle = ((Math.abs(newVal-minVal) / Math.abs(maxVal - minVal)) * dialAngleExtent) * (Math.PI / 180);
   
  // If the indicator's angle has changed, animate the change.
  if (oldAngle != newAngle) {
    // First set the angle to the old angle
    var mat = this._obj.getMatrix().clone();
    // translate the matrix so the center of rotation will be at _xPor, _yPor
    mat.translate(this._obj._xPor * -1, this._obj._yPor * -1);
    // Rotate to old angle
    mat.rotate(oldAngle-newAngle);
    // translate back
    mat.translate(this._obj._xPor, this._obj._yPor);
    this._obj.setMatrix(mat);
    this._ang = oldAngle;
    
    var nodePlayable;
    var dur = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;
    
    nodePlayable = new DvtCustomAnimation(this._obj.getCtx(), this, dur);
    nodePlayable.getAnimator().addProp(DvtAnimator.TYPE_NUMBER, this,
                                        this.getAnimationParams, this.setAnimationParams, newAngle);
    
    handler.add(nodePlayable, 0);
    this.setAnimationParams(oldAngle);    // initialize the start state
  }
};

/*--------------------------------------------------------------------*/
/*  animateInsert()      override of DvtDCAbstract.animateInsert()    */
/*--------------------------------------------------------------------*/
/**
 * Creates the insert animation for this Area.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be used to chain animations.
 * @return {DvtPlayable} The animation to perform for this insert.
 */
DvtDCDialIndicator.prototype.animateInsert = function(handler)
{
   var dur   = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;
   var obj   = this._obj ;

   var nodePlayable = new DvtAnimFadeIn(obj.getCtx(), obj, dur);
 
   handler.add(nodePlayable, 0);                    // create the playable
};

/*--------------------------------------------------------------------*/
/*  animateDelete()      override of DvtDCAbstract.animateDelete()    */
/*--------------------------------------------------------------------*/
/**
 * Creates the delete animation for this Area
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be used to
 *                                          chain animations.
 * @param {DvtContainer} delContainer   The container to which the deleted objects should
 *                                      be moved for animation.
 * @returns {DvtPlayable} The animation to perform for this delete.
 */
DvtDCDialIndicator.prototype.animateDelete = function(handler, delContainer)
{
   delContainer.addChild(this._obj);   // Move from the old chart to the delete
                                       // container on top of the new chart.

   var dur  = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;
   var obj  = this._obj ;

   var nodePlayable = new DvtAnimFadeOut(obj.getCtx(), obj, dur);
   handler.add(nodePlayable, 0);     // create the playable
};




/*--------------------------------------------------------------------*/
/*  getAnimationParams()                                              */
/*--------------------------------------------------------------------*/
/**
  *   Returns the tval.
  *   @returns {Number} a number of the value difference between the old and new indicators.
  */
DvtDCDialIndicator.prototype.getAnimationParams = function()
{
   return this._ang;
};


/*---------------------------------------------------------------------*/
/*  Init()                                                             */
/*---------------------------------------------------------------------*/
/**
  *  Object initializer.
  */
DvtDCDialIndicator.prototype.Init = function(context, obj, id)
{
  DvtDCDialIndicator.superclass.Init.call(this, context, obj, id) ;

  var dlo       = this._Dlo ;
  // For indicator, only the objId is needed
  this._animId = dlo.getSeriesId()  + '/' + dlo.getGroupId() + '/' + dlo.getMiscId() + '/' + dlo.getObjId() ;
  this._ang    = 0;
} ;


/*--------------------------------------------------------------------*/
/*  setAnimationParams()                                              */
/*--------------------------------------------------------------------*/
/**
  *   Updates the geometry of the indicator.
  *   @param {Number} tval  a number for the x/y value need to be adjusted.
  */
DvtDCDialIndicator.prototype.setAnimationParams = function(ang)
{ 
  var mat = this._obj.getMatrix().clone();
  // translate the matrix so the center of rotation will be at _xPor, _yPor 
  mat.translate(this._obj._xPor * -1, this._obj._yPor * -1);
  mat.rotate(ang-this._ang);
  // translate back
  mat.translate(this._obj._xPor, this._obj._yPor);
  this._obj.setMatrix(mat);
  this._ang = ang;
};
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. 
// All rights reserved. 
/*--------------------------------------------------------------------*/
/*   DvtDCStatusIndicator    Data change Handler for Status Indicator */
/*--------------------------------------------------------------------*/
/*  Data Change Handlers must override the following base methods:    */
/*      animateUpdate()                                               */
/*      animateInsert()                                               */
/*      animateDelete()                                               */
/*      getId()     (optional)                                        */
/*--------------------------------------------------------------------*/

/**
  *  Data change Handler for StatusMeter.
  *  @extends DvtDCAbstract
  *  @class DvtDCStatusIndicator  Data change Handler for Status Indicator.
  *  @constructor  
  *  @param {DvtContext} context   The platform dependent context object (such
  *                                as {@link DvtSvgContext}).
  *  @param {DvtStatusIndicator} obj The DvtStatusIndicator object to be animated on datachange.
  *  @param {String} id  Optional ID for the shape (see {@link  DvtDisplayable#setId}).
  *  <p>
  *  @returns {DvtDCStatusIndicator} A new DvtDCStatusIndicator object.
  */
var  DvtDCStatusIndicator = function(context, obj, id)
{
   this.Init(context, obj, id) ;
};

DvtObj.createSubclass(DvtDCStatusIndicator, DvtDCAbstract, "DvtDCStatusIndicator");



/*--------------------------------------------------------------------*/
/*  animateUpdate()    override of DvtDCAbstract.animateUpdate()      */
/*--------------------------------------------------------------------*/
/**
 * Creates the update animation for this status indicator.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be
 *                                          used to chain animations.
 * @param {DvtDCStatusIndicator} oldNode The old node state to animate from.
 */
DvtDCStatusIndicator.prototype.animateUpdate = function(handler, oldNode) {

   // Create the animator for this node

  var arStart = oldNode._obj.getCommands();
  var arEnd   = this._obj.getCommands();
  
  if (this._obj._Dlo.getChart().isVertical()) {
      // the value of the y difference between the old and new indicators
      this._tval = arStart[9] - arEnd[9];     
  } else {
      // the value of the x difference between the old and new indicators 
      this._tval = arEnd[7] - arStart[7];   
  }
  
   // If the indicator's value has changed, animate the change.
   if (this._tval) {
     var nodePlayable;
     var dur = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;
    
     nodePlayable = new DvtCustomAnimation(this._obj.getCtx(), this, dur);
     nodePlayable.getAnimator().addProp(DvtAnimator.TYPE_NUMBER, this,
                                        this.getAnimationParams, this.setAnimationParams, 0);
    
     handler.add(nodePlayable, 0);
    
     this.setAnimationParams(this._tval);    // initialize the start state
  }
};

/*--------------------------------------------------------------------*/
/*  animateInsert()      override of DvtDCAbstract.animateInsert()    */
/*--------------------------------------------------------------------*/
/**
 * Creates the insert animation for this Area.
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be used to chain animations.
 * @return {DvtPlayable} The animation to perform for this insert.
 */
DvtDCStatusIndicator.prototype.animateInsert = function(handler)
{
   var dur          = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;
   var obj          = this._obj ;
   var nodePlayable = new DvtAnimFadeIn(obj.getCtx(), obj, dur);
 
   handler.add(nodePlayable, 0);                    // create the playable
};

/*--------------------------------------------------------------------*/
/*  animateDelete()      override of DvtDCAbstract.animateDelete()    */
/*--------------------------------------------------------------------*/
/**
 * Creates the delete animation for this Area
 * @param {DvtDataAnimationHandler} handler The animation handler, which can be used to
 *                                          chain animations.
 * @param {DvtContainer} delContainer   The container to which the deleted objects should
 *                                      be moved for animation.
 * @returns {DvtPlayable} The animation to perform for this delete.
 */
DvtDCStatusIndicator.prototype.animateDelete = function(handler, delContainer)
{
   delContainer.addChild(this._obj);   // Move from the old chart to the delete
                                       // container on top of the new chart.

   var dur  = (this._Dlo.getChart().getAnimation().dataChangeDuration)/1000 ;
   var obj  = this._obj ;

   var nodePlayable = new DvtAnimFadeOut(obj.getCtx(), obj, dur);
   handler.add(nodePlayable, 0);     // create the playable
};


/*--------------------------------------------------------------------*/
/*  getAnimationParams()                                              */
/*--------------------------------------------------------------------*/
/**
  *   Returns the tval.
  *   @returns {Number} a number of the value difference between the old and new indicators.
  */
DvtDCStatusIndicator.prototype.getAnimationParams = function()
{
   return this._tval;
};

/*---------------------------------------------------------------------*/
/*  Init()                                                             */
/*---------------------------------------------------------------------*/
/**
  *  Object initializer.
  */
DvtDCStatusIndicator.prototype.Init = function(context, obj, id)
{
  DvtDCMarker.superclass.Init.call(this, context, obj, id) ;

  var dlo      = this._Dlo ;

  // For indicator, only the objId is needed
  this._animId = dlo.getSeriesId()  + '/' + dlo.getGroupId() + '/' + dlo.getMiscId() + '/' + dlo.getObjId() ;
  this._tval = 0;
  
  var ar          = this._obj.getCommands() ;  
  var aPath       = DvtArrayUtils.copy(ar) ;
  
  this._aAnimCmds = aPath ;                  // for tweening.
  this._aCmds     = ar ;                     // the original cmds.
} ;


/*--------------------------------------------------------------------*/
/*  setAnimationParams()                                              */
/*--------------------------------------------------------------------*/
/**
  *   Updates the geometry of the indicator.
  *   @param {Number} tval  a number for the x/y value need to be adjusted.
  */
DvtDCStatusIndicator.prototype.setAnimationParams = function(tval)
{
   var ar    = this._aAnimCmds ;
   var aCmds = this._aCmds ;

   if (this._obj._Dlo.getChart().isVertical()) {
     ar[2]  = aCmds[2]  + tval ;  // y in M1
     ar[5]  = aCmds[5]  + tval ;  // y's
     ar[7]  = aCmds[7]  + tval ;  // in
     ar[9]  = aCmds[9]  + tval ;  // C2
     ar[18] = aCmds[18] + tval ;  // y in L5
     ar[21] = aCmds[21] + tval ;  // y's
     ar[23] = aCmds[23] + tval ;  // in
     ar[25] = aCmds[25] + tval ;  // C6
   }
   else {
     ar[7]  = aCmds[7]  - tval ;  // x in L3
     ar[10] = aCmds[10] - tval ;  // x's
     ar[12] = aCmds[12] - tval ;  // in
     ar[14] = aCmds[14] - tval ;  // C4
     ar[17] = aCmds[17] - tval ;  // x in L5
     ar[20] = aCmds[20] - tval ;  // x's
     ar[22] = aCmds[22] - tval ;  // in
     ar[24] = aCmds[24] - tval ;  // C6
   }
   this._obj.setCommands(ar) ;
};
// Copyright (c) 2008, 2011, Oracle and/or its affiliates. 
// All rights reserved. 

/*---------------------------------------------------------------------*/
/*  DvtGraphEH            Base class for Graph event handlers          */
/*---------------------------------------------------------------------*/
//  All event handlers must implement :
//  
//  1)  A constant property .TYPE that returns an identifying string for
//      for type of the event handler.
//  2)  method newKbdEvent()
//  2)  method newMouseEvent()
//  3)  method destroy()

/**
  *  @class  DvtGraphEH    Base class for Graph event handlers.
  *  @extends DvtObj
  *  @constructor
  */
var  DvtGraphEH = function()
{
  this._Init()
};

DvtObj.createSubclass(DvtGraphEH, DvtObj, "DvtGraphEH") ;


DvtGraphEH.prototype._Init = function(chart)
{
   this._type = null ; 

};


  


/*---------------------------------------------------------------------*/
/*   getType() / setType()                                             */
/*---------------------------------------------------------------------*/
/**
  *  Returns the event handler type (e.g. {@link DvtTTEH#TYPE}).
  *  @type String
  */
DvtGraphEH.prototype.getType = function()
{
  return this._type ;
};

/**
  *  Sets the event handler type (e.g. {@link DvtTTEH#TYPE}).
  *  @param {String} type  the event handler type (Id).
  */
DvtGraphEH.prototype.setType = function(type)
{
   this._type = type ;
};


/*---------------------------------------------------------------------*/
/*   newKbdEvent()                                                     */
/*---------------------------------------------------------------------*/
/**
  *  @param  {Object}  evt   A platform specific event object such as a
  *                          derivative of {@link DvtSvgBaseEvent}.
  *  @returns {boolean}   Returns false if the event is not to be propagated
  *                       to the next event handler in the chain.  Returns
  *                       true if the event is to continue proagation to the
  *                       following event handler.
  */
DvtGraphEH.prototype.newKbdEvent = function(evt)
{
  return true ;      // no termination of event distribution
};


/*---------------------------------------------------------------------*/
/*   newMouseEvent()                                                   */
/*---------------------------------------------------------------------*/
/**
  *
  *  @param  {Object}  evt   A platform specific event object such as a
  *                          derivative of {@link DvtSvgBaseEvent}.
  *  @returns {boolean}   Returns false if the event is not to be propagated
  *                       to the next event handler in the chain.  Returns
  *                       true if the event is to continue proagation to the
  *                       following event handler.
  */
DvtGraphEH.prototype.newMouseEvent = function(evt)
{
  return true ;      // no termination of event distribution
};

    
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. 
// All rights reserved. 
/*---------------------------------------------------------------------*/
/*  DvtHSSEH              Hide/Show Series Event Handler               */
/*---------------------------------------------------------------------*/
/*  All event handlers must implement :                                */
/*                                                                     */
/*  1)  A constant property .TYPE that returns an identifying string   */
/*      for type of the event handler.                                 */
/*  2)  method newKbdEvent()                                           */
/*  3)  method newMouseEvent()                                         */
/*  4)  method destroy()                                               */
/*                                                                     */
/*  Event handlers must return false if they want events to not be     */
/*  propagated to the following handlers, else they should return true.*/
/*---------------------------------------------------------------------*/

/**
  *  @class  DvtHSSEH 
  *  @extends DvtGraphEH
  *  @constructor
  */
var  DvtHSSEH = function(chart)
{
  this._Init(chart)
};

DvtObj.createSubclass(DvtHSSEH, DvtGraphEH, "DvtHSSEH") ;


DvtHSSEH.TYPE = 'HSSEH' ;

DvtHSSEH.prototype._Init = function(chart)
{
   this.setType(DvtHSSEH.TYPE) ;
   this._Chart = chart ;
};

   

/*---------------------------------------------------------------------*/
/*   newKbdEvent()        override of DvtGraphEH.newKbdEvent()         */
/*---------------------------------------------------------------------*/

DvtHSSEH.prototype.newKbdEvent = function(evt)
{
  // TDO JRM
  return true ;      // no termination of event distribution
};


/*---------------------------------------------------------------------*/
/*   newMouseEvent()       override of DvtGraphEH.newMouseEvent()      */
/*---------------------------------------------------------------------*/

DvtHSSEH.prototype.newMouseEvent = function(evt)
{
   var obj   = evt.target ;
   var dlo   = obj._Dlo ;             // get Dvt det obj
   if (! dlo) {                       // not a det obj,
     return true ;                    // ignore.
   }
   
   if ((evt.type !== DvtMouseEvent.CLICK) || dlo.isInitFxInProgress()) {
     return true ;
   }

   this.handleShowHide(dlo);
   return true;
};

/**
* Tells the chart to set the series visibility and emits an HSS event
* @private
*/

DvtHSSEH.prototype._performShowHideSeries = function(sidx)
{
    var bHidden = this._Chart.updateHSLegendState(sidx, true);
    this._Chart.updateHSSeriesState(bHidden, sidx, true) ;
};

/**
* Tells the chart to set the series visibility of split series objects and emits an HSS event
* @private
*/
DvtHSSEH.prototype._performShowHideSplitSeries = function(aSeriesIndicies, midx)
{
    var bHidden = this._Chart.updateHSMiscLegendState(midx, true);
    for (var i = 0; i< aSeriesIndicies.length; i++) {
        this._Chart.updateHSSeriesState(bHidden, aSeriesIndicies[i], true) ;
    }
};

/**
* Tells the chart to set the visibility of underlays of objects with underlays matching
* the given shape or color and emits an HSS event
* @private
*/
DvtHSSEH.prototype._performShowHideUnderlay = function(midx, state)
{
    var bHidden = this._Chart.updateHSMiscLegendState(midx, true);
    this._Chart.updateHSSeriesUnderlayState(bHidden, state, true);
};

/*---------------------------------------------------------------------*/
/*  handleShowHide()                                                  */
/*---------------------------------------------------------------------*/

DvtHSSEH.prototype.handleShowHide = function(dlo) {
var isLegendMarker = DvtGraphEventUtils.isLegendMarker(dlo);
    if (isLegendMarker) {
        var sidx = dlo.getSeriesIndex();
        var miscId = dlo.getMiscId();
        
        //Case 1: Legend marker is split series
        //Case 2: Legend marker is undleray color
        //Case 3: Legend marker is underlay shape
        //Case 4: Legend marker is series
        if (sidx === -1) {
            //  layerList="0,3~;0,1,2~;1,4~;2,5~;3,4,5"          // split series
            //  layerList="c~;#8f993366~;d~;#8f99cc33~;td~;t"    // analytic layers
            var layerList = dlo.getChart().getLayers();
            var layerSeries = layerList[miscId];
            var len = layerSeries.length;
            
            if (len > 0) {
                // Take first element and determine case
                var val = layerSeries[0];
                sidx = parseInt(val, 10);
                if (!isNaN(sidx)) { // Case 1
                    this._performShowHideSplitSeries(layerSeries, miscId)
                }
                else if (val.charAt(0) === '#') { // Case 2
                    this._performShowHideUnderlay(miscId, val)
                } else { // Case 3
                    type = (val ===  'c' ? DvtMarker.CIRCLE :
                            (val === 's' ? DvtMarker.SQUARE :
                            (val === 't' ? DvtMarker.TRIANGLE_UP :
                            (val === 'td' ? DvtMarker.TRIANGLE_DOWN : DvtMarker.DIAMOND))));
                    this._performShowHideUnderlay(miscId, type)
                }
            }
        } else { // Case 4
            this._performShowHideSeries(sidx);
        }
        return true;

    }
    return false;
};

/**
 * Reference object hover behavior event handler.
 * @class DvtRefObjHandler
 * @constructor
 */
var  DvtRefObjHandler = function() {};

DvtObj.createSubclass(DvtRefObjHandler, DvtObj, "DvtRefObjHandler");

/**
 * Processes a mouse over on the specified target.
 * @param {object} target The target of the mouse event.
 */
DvtRefObjHandler.processMouseOver = function(target) {
  DvtRefObjHandler._setVisible(target, true);
}

/**
 * Processes a mouse out on the specified target.
 * @param {object} target The target of the mouse event.
 */
DvtRefObjHandler.processMouseOut = function(target) {
  DvtRefObjHandler._setVisible(target, false);
}

/**
 * Sets the visibility of all reference objects associated with the target.
 * @param {object} target The target of the mouse event.
 * @param {boolean} bVisible
 */
DvtRefObjHandler._setVisible = function(target, bVisible) {
  if(!target || !target.getRefObjs)
    return;
  
  var refObjs = target.getRefObjs();
  if(refObjs) {
    for(var i=0; i<refObjs.length; i++) {
      refObjs[i].setVisible(bVisible);
    }
  }
}
/**
 *  Series rollover event handler performs rollover effects on the object's
 *  behalf.
 *  @param {DvtGraphCore}  the owning chart object.
 *  @class  DvtSeriesRolloverHandler
 *  @constructor
 */
var DvtSeriesRolloverHandler = function (chart) {
  this.Init(chart)
};

DvtObj.createSubclass(DvtSeriesRolloverHandler, DvtObj, "DvtSeriesRolloverHandler");

DvtSeriesRolloverHandler.prototype.Init = function () {
};

/**
 * Processes a mouse over event on the specified object.
 * @param {object} obj The logical object.
 */
DvtSeriesRolloverHandler.prototype.processMouseOver = function(obj) {
  this.ProcessEvent(obj, true);
}

/**
 * Processes a mouse out event on the specified object.
 * @param {object} obj The logical object.
 */
DvtSeriesRolloverHandler.prototype.processMouseOut = function(obj) {
  this.ProcessEvent(obj, false);
}

/*
 * Subclasses override
 */
DvtSeriesRolloverHandler.prototype.ProcessEvent = function(obj, bOver) {
}
/**
 * @constructor
 */
var DvtGraphSeriesRolloverHandler = function (chart) {
  this.Init(chart)
};

DvtObj.createSubclass(DvtGraphSeriesRolloverHandler, DvtSeriesRolloverHandler, "DvtGraphSeriesRolloverHandler");

DvtGraphSeriesRolloverHandler.prototype.Init = function (chart) {
  DvtGraphSeriesRolloverHandler.superclass.Init.call(this);
  this._chart = chart;
  this._bDim = (chart.getSROEffect() === DvtGraphCore.RB_DIM);
  this._bHilite = (chart.getSROEffect() === DvtGraphCore.RB_HILITE);
};

/**
 * @param {object} obj The logical object.
 * @param {boolean} bOver True if this is a rollOver.
 * @private
 */
DvtGraphSeriesRolloverHandler.prototype.ProcessEvent = function(obj, bOver) {
    //Rollover cases:
    //Case 1: Line marker
    //Case 2: Legend marker, series
    //Case 3: Legend marker, split series
    //Case 4: Legend marker, undleray color
    //Case 5: Legend marker, underlay shape
    
    if (!obj)
        return;

    var chart = this._chart;
    var focusSeries = chart.getCurrentSeries();
    if (focusSeries != null && focusSeries >= 0) {    
        return;
    }

    var showRolloverEffect = (this._bDim || this._bHilite); // Temp until highlight effect is implemented

    //Case 1 & 2
    var layerList = this._chart.getLayers();
    var categories = obj.getCategories(); // can return an array with null obj
    if(categories[0] !== null) {
        var series = categories[0];
        var sidx = this._chart.getDataObj().getIndexFromSeries(series);
        if(sidx >= 0) {
            DvtGraphEventUtils.HighlightSeries(this._chart, sidx, showRolloverEffect, bOver);
            if (this._chart._scrollableLegend){
                //Fix for bug 14004059. Reverse index to scroll to correct data point in legend.
                var type = this._chart.getType();
                if(type == DvtGraphType.BAR_VERT_PERCENT || type == DvtGraphType.BAR_VERT_STACK || type == DvtGraphType.BAR_VERT_STACK2Y || 
                type == DvtGraphType.LINE_VERT_PERCENT || type == DvtGraphType.LINE_VERT_STACK || 
                type == DvtGraphType.LINE_VERT_STACK2Y || type == DvtGraphType.AREA_VERT_PERCENT || type == DvtGraphType.AREA_VERT_STACK)
                    this._chart._scrollableLegend.scrollIntoView(this._chart.getDataObj()._arSeries.length-1-sidx);
                else
                    this._chart._scrollableLegend.scrollIntoView(sidx);
            }
        }
        if (layerList) {
            DvtGraphEventUtils.HighlightSplitSeriesLegend(this._chart, sidx, showRolloverEffect, bOver);
        }
    }
    else if (DvtGraphEventUtils.isLegendMarker(obj)) {
        var midx = obj.getMiscId();
        //  layerList="0,3~;0,1,2~;1,4~;2,5~;3,4,5"          // split series
        //  layerList="c~;#8f993366~;d~;#8f99cc33~;td~;t"    // analytic layers
        var layerSeries = layerList[midx];
        var len = layerSeries.length;
        
        if (len > 0) {
            var val = layerSeries[0];
            sidx = parseInt(val, 10);
            if (!isNaN(sidx)) { // Case 3
                DvtGraphEventUtils.HighlightMiscLegend(this._chart, midx, showRolloverEffect, bOver);
                DvtGraphEventUtils.HighlightSplitSeries(this._chart, layerSeries, showRolloverEffect, bOver);
                if (this._chart._scrollableLegend)
                    this._chart._scrollableLegend.scrollIntoView(sidx);
            }
            else if (val.charAt(0) === '#') { // Case 4
                DvtGraphEventUtils.HighlightMiscLegend(this._chart, midx, showRolloverEffect, bOver);
                DvtGraphEventUtils.HighlightUnderlay(this._chart, showRolloverEffect, bOver, val);
            } else { // Case 5
                type = (val ===  'c' ? DvtMarker.CIRCLE :
                        (val === 's' ? DvtMarker.SQUARE :
                        (val === 't' ? DvtMarker.TRIANGLE_UP :
                        (val === 'td' ? DvtMarker.TRIANGLE_DOWN : DvtMarker.DIAMOND))));
                DvtGraphEventUtils.HighlightMiscLegend(this._chart, midx, showRolloverEffect, bOver);
                DvtGraphEventUtils.HighlightUnderlay(this._chart, showRolloverEffect, bOver, null, type);
            }
        }
    }
}
/**
 * @constructor
 * Abstract series focus handler
 */
var DvtSeriesFocusHandler = function() {
    this.Init();
};

DvtObj.createSubclass(DvtSeriesFocusHandler, DvtObj, "DvtSeriesFocusHandler");

DvtSeriesFocusHandler.prototype.Init = function() {
  this._seriesFocusHandled = false;
}

DvtSeriesFocusHandler.prototype.processSeriesFocus = function(pos, obj) {
  this._seriesFocusHandled = false;
  if (this.isSeriesFocusable(obj)) {
      var seriesCategory = this.getSeriesCategory(obj);
      this._renderSeriesFocus(seriesCategory);
      this._seriesFocusHandled = true;
  } else {
      var currentSeries = this.getCurrentSeriesCategory();
      if (currentSeries != null) {   
          if (this.isClearSeriesFocus(pos)) {
              this._renderSeriesFocus(null);
              this._seriesFocusHandled = true;
          }
      }
  }
}

DvtSeriesFocusHandler.prototype._renderSeriesFocus = function(seriesCategory) {
  var oldSeriesCategory = this.getCurrentSeriesCategory();
  if (oldSeriesCategory != seriesCategory) {   
      if (seriesCategory == null) {   
        var currentSeries = this.getCurrentSeriesCategory(); 
        this.SeriesFocusCleared(currentSeries);
        this.setCurrentSeriesCategory(null);
      } else {
        this.SeriesFocusChanged(oldSeriesCategory, seriesCategory);
        this.setCurrentSeriesCategory(seriesCategory);
      }
  }
}

DvtSeriesFocusHandler.prototype.SeriesFocusCleared = function(currentSeries) {
}

DvtSeriesFocusHandler.prototype.SeriesFocusChanged = function(oldSeries, newSeries) {
}

DvtSeriesFocusHandler.prototype.getSeriesCategory = function(obj) {

}

DvtSeriesFocusHandler.prototype.getCurrentSeriesCategory = function() {
}

DvtSeriesFocusHandler.prototype.setCurrentSeriesCategory = function(sidx) {
}

DvtSeriesFocusHandler.prototype.isClearSeriesFocus = function(pos) {
    return false;
}

DvtSeriesFocusHandler.prototype.isSeriesFocusHandled = function() {
    return this._seriesFocusHandled;
}

DvtSeriesFocusHandler.prototype.isSeriesFocusable = function(dlo) {
    return false;
}
/**
 * @constructor
 * Graph series focus handler
 */
var DvtGraphSeriesFocusHandler = function(chart) {
    this.Init(chart);
};

DvtObj.createSubclass(DvtGraphSeriesFocusHandler, DvtSeriesFocusHandler, "DvtGraphSeriesFocusHandler");

DvtGraphSeriesFocusHandler.prototype.Init = function(chart) {
  DvtGraphSeriesFocusHandler.superclass.Init.call(this);
  this._Chart = chart;
}

DvtGraphSeriesFocusHandler.prototype.isSeriesFocusable = function(dlo) {
    var chart = this._Chart; 
    if (chart.isSeriesFocusEnabled()) {
        var seriesCount = chart.getSeriesCount();
        if (seriesCount > 1) {
          if (DvtGraphEventUtils.isLegendMarker(dlo)) {
            return true;
          }
        }
    }
    return false;
}

DvtGraphSeriesFocusHandler.prototype.SeriesFocusCleared = function(currentSeries) {
    var chart = this._Chart; 
    DvtGraphEventUtils.UnhighlightAllSeries(chart);
}

DvtGraphSeriesFocusHandler.prototype.SeriesFocusChanged = function(oldSeries, newSeries) {
    var chart = this._Chart; 
    DvtGraphEventUtils.HighlightSeries(chart, oldSeries, true, false);
    DvtGraphEventUtils.HighlightSeries(chart, newSeries, true, true);
}

DvtGraphSeriesFocusHandler.prototype.getSeriesCategory = function(obj) {
    return obj.getSeriesIndex();
}

DvtGraphSeriesFocusHandler.prototype.getCurrentSeriesCategory = function() {
  var chart = this._Chart;
  return chart.getCurrentSeries();
}

DvtGraphSeriesFocusHandler.prototype.setCurrentSeriesCategory = function(sidx) {
  var chart = this._Chart;
  chart.setCurrentSeries(sidx);
}

DvtGraphSeriesFocusHandler.prototype.isClearSeriesFocus = function(pos) {
    var chart = this._Chart;
    return !this._getLegendBounds(chart).containsPoint(pos.x, pos.y) && !chart.getFrameRect().containsPoint(pos.x, pos.y);
}

DvtGraphSeriesFocusHandler.prototype._getLegendBounds = function(chart)
{  
    var legendRect = new DvtRectangle(chart.getLegendRect().getX(), chart.getLegendRect().getY(), chart.getLegendRect().getWidth(), chart.getLegendRect().getHeight());
    return legendRect;
}
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. 
// All rights reserved. 

/*---------------------------------------------------------------------*/
/*  DvtTTEH                 Tooltip/Datatip event behavior             */
/*---------------------------------------------------------------------*/
//  All event handlers must implement :
//  
//  1)  A constant property .TYPE that returns an identifying string for
//      for type of the event handler.
//  2)  method newKbdEvent()
//  3)  method newMouseEvent()
//  4)  method destroy()
//
//  Event handlers must return false if they want events to not be
//  propagated to following handlers, else they should return true.

/**
  *  @class  DvtTTEH 
  *  @extends DvtGraphEH
  *  @constructor
  */
var  DvtTTEH = function(chart)
{
  this._Init(chart)
};

DvtObj.createSubclass(DvtTTEH, DvtGraphEH, "DvtTTEH") ;


DvtTTEH.TYPE = 'TTEH' ;


/*---------------------------------------------------------------------*/
/*   _Init()                                                           */
/*---------------------------------------------------------------------*/
/**
 *   @private
 */
DvtTTEH.prototype._Init = function(chart)
{
   this.setType(DvtTTEH.TYPE) ;
   this._TTM         = new DvtHtmlTooltipManager() ;
   this._Chart       = chart ;
   this._Ctx         = chart.getCtx() ;
   this._bTipShowing = false ;

   DvtTTEH.superclass._Init.call(this) ;
};
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*  DvtDCEH                 Data Cursor Event Handler                  */
/*---------------------------------------------------------------------*/
/**
 *  @class  DvtDCEH
 *  @extends DvtObj
 *  @constructor
 */
var DvtDCEH = function (context, dataCursor) {
  this._Init(context, dataCursor)
};

DvtObj.createSubclass(DvtDCEH, DvtObj, "DvtDCEH");

DvtDCEH.prototype._Init = function (context, dataCursor) {
  this._context = context;
  this._dataCursorShown = false;
  this._horizontal = false;
  this._useAllInGroup = false;
  this._isNumericMainAxis = false;
  this._dataCursor = dataCursor;
  this._threeDHorizontalOffset = 0;
};

// Show/hide the data cursor based on the global page coordinates of the action
// Returns whether or not data cursor is shown
DvtDCEH.prototype.processMove = function (pageX, pageY, targetObj, logicalObj) {
  if (!targetObj) {
    this._removeDataCursor();
    return;
  }

  var pos = this._context.getRelativePosition(pageX, pageY);
  var x = pos.x;
  var y = pos.y;

  var blockEventsRect = this.getActionablePlotRect(x, y, logicalObj);
  if (blockEventsRect) {
    // Show the data cursor only if the current point is within the plot area
    this._showDataCursor(blockEventsRect, x, y, targetObj);
    this._dataCursorShown = true;
    return true;
  }
  else {
    this._removeDataCursor();
    this._dataCursorShown = false;
  }
  return false;

}

DvtDCEH.prototype.processEnd = function () {
  this._removeDataCursor();
}

DvtDCEH.prototype.processOut = function (pos) {
  var plotRect = this.getPlotRect();
  if (!plotRect.containsPoint(pos.x, pos.y)) {
    this._removeDataCursor();
  }
}

/**
 * Displays the data cursor.
 * @param {DvtRectangle} plotRect The bounds of the plot area
 * @param {number} x
 * @param {number} y
 * @param {object} targetObj
 * @private
 */
DvtDCEH.prototype._showDataCursor = function (plotRect, x, y, targetObj) {
  var dataCursor = this._dataCursor;

  // Find the closest data point
  var closestMatch = this.getClosestMatch(x, y, targetObj, plotRect);
  if (closestMatch == null) {
    this._removeDataCursor();
    return;
  }
  
  // Find the center of the data item
  var centerPoint = DvtGeomUtils.getCenterPoint(closestMatch.matchRegion);

  var dcX = x;
  var dcY = y;
  // Adjust for snap behavior
  if (dataCursor.getBehavior() == DvtDataCursor.BEHAVIOR_SNAP) {
    if (dataCursor.isHorizontal())
      dcY = Math.min(Math.max(centerPoint.y, plotRect.y), plotRect.y + plotRect.h);
    else
      dcX = Math.min(Math.max(centerPoint.x, plotRect.x), plotRect.x + plotRect.w);
  }

  // Regardless of behavior, the data cursor itself should always be within the front plane of the plot area (in 3d graphs)
  if (this._threeDHorizontalOffset) {
    var xExtent = plotRect.x + plotRect.w - this._threeDHorizontalOffset;
    if (dcX > xExtent)
      dcX = xExtent;
  }

  // Don't show the data cursor if no tooltip text is present
  var tooltipText = this.getTooltipText(closestMatch);
  if (!tooltipText || tooltipText == "") {
    dataCursor.setVisible(false);
    return;
  }
  else 
    dataCursor.setVisible(true);

  var seriesColor = this.getSeriesColor(closestMatch.sidx, closestMatch.gidx);
  var lineCoord = dataCursor.isHorizontal() ? dcY : dcX;
  dataCursor.render(plotRect, centerPoint.x, centerPoint.y, lineCoord, tooltipText, seriesColor);
}

// Remove the data cursor
DvtDCEH.prototype._removeDataCursor = function () {
  if (this._dataCursor.getVisible())
    this._dataCursor.setVisible(false);
  
  this._context.getTooltipManager().hideTooltip();
}

DvtDCEH.prototype.isDataCursorShown = function () {
  return this._dataCursorShown;
}

DvtDCEH.AddPotentialMatch = function (matches, matchObj, plotRect, isHorizontal) {
  var region = matchObj.matchRegion;
  if (isHorizontal) {
    if (region.y + region.h < plotRect.y || region.y > plotRect.y + plotRect.h) {
      return false;
    }
  }
  else {
    if (region.x + region.w < plotRect.x || region.x > plotRect.x + plotRect.w) {
      return false;
    }
  }
  matches.push(matchObj);
  return true;
}

DvtDCEH.GetClosestMatchSecondDirection = function (matchesInBounds, horizontal, x, y) {
  var closestMatch = null;
  var minDiff = 100000000;
  for (var i = 0;i < matchesInBounds.length;i++) {
    var match = matchesInBounds[i];
    var lowerBound = (horizontal) ? match.matchRegion.x : match.matchRegion.y;
    var higherBound = (horizontal) ? match.matchRegion.x + match.matchRegion.w : match.matchRegion.y + match.matchRegion.h;
    var value = (horizontal) ? x : y;
    var midPoint = (lowerBound + higherBound) / 2;
    var diffValue = Math.abs(midPoint - value);
    if (diffValue < minDiff) {
      minDiff = diffValue;
      closestMatch = match;
    }
  }
  return closestMatch;
}

DvtDCEH.GetClosestMatchesFirstDirection = function (matches, horizontal, x, y) {
  var minDiff = 10000000;
  var closestFirstDirectionMatches = new Array();
  // Get closest matches
  for (var i = 0;i < matches.length;i++) {
    var matchObj = matches[i];
    var lowerBound = (horizontal) ? matchObj.matchRegion.y : matchObj.matchRegion.x;
    var higherBound = (horizontal) ? matchObj.matchRegion.y + matchObj.matchRegion.h : matchObj.matchRegion.x + matchObj.matchRegion.w;
    var value = (horizontal) ? y : x;

    var midPoint = (lowerBound + higherBound) / 2;
    var diffValue = Math.abs(midPoint - value);
    if (diffValue <= minDiff) {
      if (diffValue < minDiff) {
        closestFirstDirectionMatches = new Array();
      }
      closestFirstDirectionMatches.push(matchObj);
      minDiff = diffValue;
    }
  }
  return closestFirstDirectionMatches;
}

DvtDCEH.prototype.getActionablePlotRect = function (x, y, logicalObj) {
  return null;
}

DvtDCEH.prototype.getPlotRect = function () {
  return null;
}

DvtDCEH.prototype.getSeriesColor = function (series) {
  return "black";
}

DvtDCEH.prototype.getTooltipText = function (closestMatch) {
  return "Base class should override";
}

DvtDCEH.prototype.getTargetSeriesIndex = function (targetObj) {
  return  - 1;
}

DvtDCEH.prototype.findMatches = function (x, y, targetObj, matches, plotRect, targetSeriesIndex) {
  return null;
}

DvtDCEH.prototype.getClosestMatch = function (x, y, targetObj, plotRect) {
  var horizontal = this._horizontal;
  var useAllInGroup = this._useAllInGroup;
  var isNumericMainAxis = this._isNumericMainAxis;

  var targetSeriesIndex = this.getTargetSeriesIndex(targetObj);

  var matches = new Array();
  var immediateMatch = this.findMatches(x, y, targetObj, matches, plotRect, targetSeriesIndex)
  if (immediateMatch)
    return immediateMatch;

  var closestFirstDirectionMatches = DvtDCEH.GetClosestMatchesFirstDirection(matches, horizontal, x, y);

  var matchesInBounds = closestFirstDirectionMatches;

  // Non-numerical x axis
  if (!isNumericMainAxis) {
    var closestLowerBound = 1000000;
    var closestHigherBound =  - 1000000;
    var closestGroup = null;

    for (var i = 0;i < closestFirstDirectionMatches.length;i++) {
      var closestFirstDirectionMatch = closestFirstDirectionMatches[i];
      closestLowerBound = Math.min(closestLowerBound, (horizontal) ? closestFirstDirectionMatch.matchRegion.y : closestFirstDirectionMatch.matchRegion.x);
      closestHigherBound = Math.max(closestHigherBound, (horizontal) ? closestFirstDirectionMatch.matchRegion.y + closestFirstDirectionMatch.matchRegion.h : closestFirstDirectionMatch.matchRegion.x + closestFirstDirectionMatch.matchRegion.w);
      closestGroup = closestFirstDirectionMatch.gidx;
    }

    for (var i = 0;i < matches.length;i++) {
      var match = matches[i];
      var itemGroup = match.gidx;
      if (useAllInGroup) {
        if (closestGroup == itemGroup) {
          matchesInBounds.push(match);
        }
      }
      else {
        var lowerBound = (horizontal) ? match.matchRegion.y : match.matchRegion.x;
        var higherBound = (horizontal) ? match.matchRegion.y + match.matchRegion.h : match.matchRegion.x + match.matchRegion.w;
        var midPoint = (lowerBound + higherBound) / 2;
        if (closestHigherBound >= midPoint && closestLowerBound <= midPoint) {
          matchesInBounds.push(match);
        }

      }
    }
  }
  return DvtDCEH.GetClosestMatchSecondDirection(matchesInBounds, horizontal, x, y);
}

DvtDCEH.getGlobalBounds = function (obj) {
  var dim = obj.getDimensions(obj.getParent());
  var stagePoint = obj.getParent().localToStage(new DvtPoint(dim.x, dim.y));
  dim.x = stagePoint.x;
  dim.y = stagePoint.y;
  return dim;
}
/**
 * @constructor
 */
var  DvtGraphDCEH = function(chart)
{
  this._Init(chart)
};

DvtObj.createSubclass(DvtGraphDCEH, DvtDCEH, "DvtGraphDCEH") ;

DvtGraphDCEH.prototype._Init = function(chart)
{
   DvtGraphDCEH.superclass._Init.call(this, chart.getCtx(), chart.getDataCursor()) ;
   this._Chart  = chart ;
   this._horizontal = chart.isHorizontal();
   this._useAllInGroup = chart.isArea() || chart.isLineChart() || chart.isStockChart();
   this._isNumericMainAxis = chart.isBubbleChart() || chart.isScatterChart();
   this._threeDHorizontalOffset = chart._threeDHorizontalOffset;
   this._isStockChart = chart.isStockChart();
};

DvtGraphDCEH.prototype.getPlotRect = function() {
   var chart = this._Chart;
   return chart.getFrameRect();
}

DvtGraphDCEH.prototype.getActionablePlotRect = function(x, y, logicalObj) {
    var chart = this._Chart;
    return DvtGraphEventUtils.getActionablePlotRect(chart, x, y, logicalObj);
}

DvtGraphDCEH.prototype.getSeriesColor = function(series, group) {
    var chart = this._Chart;
    return chart.getSeriesColor(series);
}

DvtGraphDCEH.prototype.getTooltipText = function(closestMatch) {
    var chart = this._Chart;
    var ttipText = DvtGraphEventUtils.GetDataCursorTooltip(chart, closestMatch);
    return DvtGraphParser.convertLineBreaks(ttipText);
}

DvtGraphDCEH.prototype.getTargetSeriesIndex = function(targetObj) {
   var targetSeriesIndex = -1;
   if (targetObj && targetObj._Dlo) {
       targetSeriesIndex = targetObj._Dlo.getSeriesIndex();
   }
   return targetSeriesIndex;
}

DvtGraphDCEH.prototype.findMatches = function(x, y, targetObj, matches, plotRect, targetSeriesIndex) {
  var chart = this._Chart;
  var immediateMatch = null;
  // Special case for stock
  if (this._isStockChart) {
      immediateMatch = DvtGraphDCEH.ProcessClosestStockMatches(chart, targetObj, plotRect, matches, this._isNumericMainAxis, this._horizontal);
  } else {
      immediateMatch = DvtGraphDCEH.ProcessClosestMatches(chart, targetObj, plotRect, matches, this._isNumericMainAxis, this._horizontal, x, y, targetSeriesIndex);
  }
  return immediateMatch;
}


DvtGraphDCEH.ProcessClosestStockMatches = function(chart, targetObj, plotBounds, matches, isNumericMainAxis, horizontal) {
  for (key in chart._stockMarkersMap)
  {
    var stockMarkerArray = chart._stockMarkersMap[key];
    for (var iMarker = 0; iMarker < stockMarkerArray.length; iMarker++)
    {
      var currIMarker = stockMarkerArray[iMarker];
      var dlo = currIMarker._Dlo;
      if (dlo.getObjId() == DvtGraphObjType.STOCK_RISER ||
          dlo.getObjId() == DvtGraphObjType.STOCK_RISING_RISER ||
          dlo.getObjId() == DvtGraphObjType.STOCK_FALLING_RISER ||
          dlo.getObjId() == DvtGraphObjType.STOCK_VOLUME_RISER) {
          var dim = DvtDCEH.getGlobalBounds(currIMarker);
          var matchObj = { obj: currIMarker, matchRegion: dim, gidx: dlo.getGroupIndex(), sidx: dlo.getSeriesIndex(), marker: null };
          var added = DvtDCEH.AddPotentialMatch(matches, matchObj, plotBounds, horizontal);

          // Immediate if target obj found already, except with numeric axes
          if (added && !isNumericMainAxis && currIMarker == targetObj) {
              return matchObj;
          }
      }
    }
  }
}

DvtGraphDCEH.ProcessClosestMatches = function(chart, targetObj, plotBounds, matches, isNumericMainAxis, horizontal, x, y, targetSeriesIndex) {
   var riserArray = DvtGraphDCEH.GetSearchableRisers(chart);
   if (riserArray == null) {
      return;
   }
   for (var i=0; i<riserArray.length; i++) {
       var risers = riserArray[i];
       if (risers == null)
        continue;
       for (var j=0; j<risers.length;j++) {
            var riser = risers[j];
            if (riser) {
                var dlo = riser._Dlo;
                var seriesIndex = dlo.getSeriesIndex();
                // Don't consider if hidden
                if (chart.isHSSeriesSupported() && !chart.isSeriesVisible(seriesIndex)) {
                    continue;
                }

                var matchRegion = null;
                var groupIndex = null;
                var marker = null;
                var matchRiser = riser;
                if (dlo && dlo.isMultiRiser()) {
                    var markerArray = new Array();
                    if (riser._aLMarkers) {
                        markerArray = markerArray.concat(riser._aLMarkers);
                    }
                    var hiddenMarkers = chart.getHiddenMarkers();
                    if (hiddenMarkers) {
                        var hiddenSeriesMarkers = hiddenMarkers[seriesIndex];
                        if (hiddenSeriesMarkers) {
                            markerArray = markerArray.concat(hiddenSeriesMarkers);
                        }
                    }
                    if (markerArray.length > 0) {
                        var minMarkerDiff = 1000000;
                        for (var k = 0; k < markerArray.length;k++) {
                            var m = markerArray[k];
                            var dimensions = DvtDCEH.getGlobalBounds(m);
                            var mX = dimensions.x;
                            var mY = dimensions.y;
                            var mHeight = dimensions.h;
                            var mWidth = dimensions.w;
                            var tempXPos = mX+mWidth/2;
                            var tempYPos = mY+mHeight/2;
                            var markerDiff = Math.abs(x - tempXPos);
                            if (markerDiff < minMarkerDiff) {
                                groupIndex = m._Dlo.getGroupIndex();
                                // This special case will return the marker as the matched riser
                                matchRiser = m;
                                matchRegion = dimensions;

                                minMarkerDiff = markerDiff;
                            }
                        }
                    } else {
                        var closestPoint = DvtGraphEventUtils.GetClosestLinePoint(chart, x, y, riser, seriesIndex);
                        if (closestPoint != null) {
                            var invisiblePointBuffer = 6;
                            matchRegion = new DvtRectangle(closestPoint.x - invisiblePointBuffer/2, closestPoint.y - invisiblePointBuffer/2, invisiblePointBuffer, invisiblePointBuffer);
                            groupIndex = closestPoint.gidx;
                            marker = closestPoint.marker;
                        }
                    }
                } 
                var matchSeriesIndex = -1;
                if (matchRegion != null) {
                    matchSeriesIndex = seriesIndex;
                } else {
                    // If special case of line/area graphs not triggered
                    // 3D graphs
                    if (riser instanceof Dvt3DBar) {
                        matchRiser = riser.getFront();    
                    }

                    var dimensions = DvtDCEH.getGlobalBounds(matchRiser);
                    matchRegion = dimensions;
                    groupIndex = matchRiser._Dlo.getGroupIndex();
                }
                var matchObj = { obj: matchRiser, matchRegion: matchRegion, gidx: groupIndex, sidx: seriesIndex, marker: marker };
                var added = DvtDCEH.AddPotentialMatch(matches, matchObj, plotBounds, horizontal);
                if (added && !isNumericMainAxis) {
                    // Immediate if target obj found already, except with numeric axes
                    if (targetSeriesIndex != -1 && matchSeriesIndex != -1 && matchSeriesIndex == targetSeriesIndex) {
                        return matchObj;
                    } else {
                        var returnImmediateTargets = new Array();
                        returnImmediateTargets.push(riser);
                        returnImmediateTargets.push(matchRiser);
                        if (riser instanceof Dvt3DBar) {
                            returnImmediateTargets.push(riser.getSide());
                            returnImmediateTargets.push(riser.getTop());
                        }
                                                    
                        for (var k=0;k<returnImmediateTargets.length;k++) {
                            var immediateTarget = returnImmediateTargets[k];
                            if (immediateTarget == targetObj) {
                                return matchObj;
                            }
                        }
                    }
                }

            }
       }
   }
}

DvtGraphDCEH.GetSearchableRisers = function(chart) {
   var riserArray = chart.getRisers();
   // Filter searchable risers to the current series
   var series = chart.getCurrentSeries();
   if (series != null && series >= 0) {
      riserArray = new Array();
      riserArray.push(chart.getSeriesRisers(series));
   }
   // TODO: if mapping between risers and split y axis is available, filter on this as well
   return riserArray;
}
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*  DvtTooltipHelper       Tooltip helper for line markers             */
/*---------------------------------------------------------------------*/
  
//  Helper class that finds the closest line marker to show based on mouse coords. 
/**
 * @constructor
 */
var  DvtTooltipHelper = function(chart)
{
   this._Init(chart) ;
};

DvtObj.createSubclass(DvtTooltipHelper, DvtObj, "DvtTooltipHelper");

DvtTooltipHelper.MAX_DISTANCE = 3;     // max distance from the center that a tooltip will be shown.

/**
  *   Adds a LineMarker object to this helper.
  *   @param {DvtLineMarker} lm  The DvtLineMarker object.
  */
DvtTooltipHelper.prototype.addMarker = function(lm)
{
   this._arMarkers.push(lm);
} ;


/**
  *  Returns an Array containing all LineMarkers that belong to the given series. 
  *  @param {number} sidx  The series index.
  *  @type Array
  */
DvtTooltipHelper.prototype.getMarkers = function(sidx)
{
  var arRet ;
 
  if (this._arMarkers) {
    arRet = [] ;

    var len = this._arMarkers.length ;

    for (var i = 0; i < len; i++) {
      var lm = this._arMarkers[i] ;
      if (lm.getSeriesIndex() === sidx) {
        arRet.push(lm);
      }
    }
  }

  return arRet;
} ;
    

/**
  *  Finds the closest line marker based on the current mouse position.
  *  @param {number} x  The mouse x position in stage coordinates
  *  @param {number} y the mouse y position in stage coordinates
  *  @returns An Object with the following params:<br><br>
  *  marker  (the line marker)<br>
  *  sidx  (the series index)<br>
  *  gidx  (the group index)<br>
  *  @type Object
  */
DvtTooltipHelper.prototype.findMatch = function(x, y)
{
   var  retObj = null ;

   if (this._arMarkers && this._arMarkers.length > 0) {

     // Search for the closest marker

     if (this._findGlobalMatch(x, y)) {
       this._obj.marker  = this._marker;
       this._obj.sidx = this._sidx ;
       this._obj.gidx = this._gidx ;
       retObj = this._obj;
     }
   }

   return retObj;        
} ;
    


/**
  *  Returns true if members _sidx, _gidx, _marker, _tooltip are updated for the closest match,
  *  or false if no match is found.
  *  @type Boolean  
  */
DvtTooltipHelper.prototype._findGlobalMatch = function(x, y)
{
   // Do a search of each set of line markers until a match is found.
   // We use the one with closest x-coord and from the last possible set
   // of markers (which has greatest z-order), so search in reverse.

    var  bFound = false ;
    for (var i = this._arMarkers.length -1 ; i >=0 ; i--) { 
        var lm = this._arMarkers[i];        // get a LineMarker obj
        // Find the scroll offset for this marker
        var offsets = this._Chart.getScrollOffset(lm.getHandleId());
        bFound = this._findLocalMatch(x - offsets.x, y - offsets.y, lm) ;
        if (bFound) {
            this._marker = lm ;  // Match found, update the marker property
            break ;
        }
    
    }

return bFound ;
} ;
    

/**
  *  Returns the closest match for x and y in the linemarker array of coords.
  *  @param {number} x the x mouse coord
  *  @param {number} y the y mouse coord
  *  @param {DvtLineMarker} lm the DvtLineMarker containing coords to search
  *  @type Boolean
  *  @returns true if this._lmA has been updated iwth a match, else false.
  */
DvtTooltipHelper.prototype._findLocalMatch = function(x, y, lm)
{
   // The algorithm is a modified binary search.  Once a valid x coord match
   // is found, a linear search is performed in both directions to find
   // the best match.
   
   var arCoords = lm.getCoords() ;
   var startOffset = 0;
   var endOffset   = arCoords.length;

   if (DvtAgent.isRightToLeft() && arCoords.length >2){
      arCoords = this._bidiFlip(arCoords.slice(0));
    }
            
   while (endOffset >= startOffset) {

     // Check if the midpoint is a x coord match

     var mid = (startOffset + endOffset)/2;

     if (mid % 2 === 1) {                    // make sure we're on the x coord
       mid = mid - 1;
     }

     var xDist  = Math.abs(x - arCoords[mid]);
     var yDist  = Math.abs(y - arCoords[mid+1]);

     if (xDist <= DvtTooltipHelper.MAX_DISTANCE) {

         // x-match found, linear search to find the exact match

         return this._linearSearch(x, y, lm, mid);
     }
     
     // If we've searched the entire list, break

     if (mid === startOffset || mid === endOffset) {
       break;
     }
     else {
       // Otherwise keep going in the correct direction

       if (x > arCoords[mid]) {
         startOffset = mid + 2;
       }
       else  {
         endOffset = mid;
       }
     }
   }
   
   // If we reach here, no match is found

   return null;
} ;
    

/**
 *   @private
 */
DvtTooltipHelper.prototype._Init = function(chart)
{
   this._Chart = chart ;
   this._arMarkers  = [] ;    // All line markers belonging to this chart. 

   this._sidx = -1 ;          // )  these will be
   this._gidx = -1 ;          // )  updated if
   this._marker = null ;      // )  a match is
   this._tooltip = null ;     // )  found.
   this._obj = {
                 marker : null,    // the 
                 sidx: null,       // returned object.
                 gidx: null};
};


/**
  *  Returns the closest match within the given marker by using a linear search.
  *  This search is performed in either direction around the offset.
  *  @param {number} x The x mouse coord.
  *  @param {number} y The y mouse coord.
  *  @param {DvtLineMarker} lm The LineMarker containing the coords to search.
  *  @param {number} offset the offset to search around
  *  @type Boolean
  *  @returns An otrue if this._lm has been updated with group and series index, else false.
  */
DvtTooltipHelper.prototype._linearSearch = function(x, y, lm, offset)
{
    // Search in both directions for a match
    
    var arCoords  = lm.getCoords() ;
    var i ;
    
    
   if (DvtAgent.isRightToLeft() && arCoords.length >2){
      arCoords = this._bidiFlip(arCoords.slice(0));
    }
    
    // Stores best match so far

    var bMatchFound  = false;
    var bestXDist    = 16777215;               // start with a huge number
    var sidx         = lm.getSeriesIndex();
    var gidx         = 0 ;
    
    // Search left first

    var xDist ;
    var yDist ;

    for (i = offset; i >= 0; i -= 2) {
       xDist = Math.abs(x - arCoords[i]);
       yDist = Math.abs(y - arCoords[i+1]);

       if (xDist > DvtTooltipHelper.MAX_DISTANCE || xDist > bestXDist) {
           break;
       }
       else if (yDist <= DvtTooltipHelper.MAX_DISTANCE) {
           bestXDist   = arCoords[i];
           gidx        = lm.getGroupIndex() + (i/2);
           bMatchFound = true;
           break;
       }
    } 

    // Then search right

    var  len = arCoords.length -1 ;

    for (i = offset+2; i <= len; i += 2) {
       xDist = Math.abs(x - arCoords[i]);
       yDist = Math.abs(y - arCoords[i+1]);

       if (xDist > DvtTooltipHelper.MAX_DISTANCE || xDist > bestXDist) {
         break;
       }
       else if (yDist <= DvtTooltipHelper.MAX_DISTANCE) {
           bestXDist   = arCoords[i];
           gidx        = lm.getGroupIndex() + (i/2);
           bMatchFound = true;
           break;
       }
    } 
    
    // Return the series and group of the best match


    if (bMatchFound && DvtAgent.isRightToLeft() && arCoords.length>2) {
      this._sidx = sidx ;
      this._gidx = arCoords.length/2- gidx-1 ;
    }
    else if (bMatchFound) {
      this._sidx = sidx ;
      this._gidx =  gidx ;
    }

    return bMatchFound ;
} ;

DvtTooltipHelper.prototype._bidiFlip = function(arCoords)
{
  var i, tempX, tempY;
  var len = arCoords.length;
  for(i=0; i<len/2; i+=2){
     tempX = arCoords[i];
     tempY = arCoords[i+1];
     arCoords[i] = arCoords[len-i-2];
     arCoords[i+1] = arCoords[len-i-1];
        
     arCoords[len-i-2] = tempX;
     arCoords[len-i-1] = tempY;
   }
   return arCoords;
};
/**
 * Reference object hover behavior event handler.
 * @class DvtGraphKeyboardHandler
 * @constructor
 * @param {DvtGraphCore} the owning chart
 */
var  DvtGraphKeyboardHandler = function(chart)
{
  this.Init(chart)
};

DvtObj.createSubclass(DvtGraphKeyboardHandler, DvtObj, "DvtGraphKeyboardHandler");

/**
 * Initializer
 * @param {DvtGraphCore} chart
 */
DvtGraphKeyboardHandler.prototype.Init = function (chart) {
  this._chart = chart;
};

/**
 * Process key down events
 * @param {DvtKeyboardEvent} event
 */
DvtGraphKeyboardHandler.prototype.processKeyDown = function(event)
{
  var key = event.keyCode;

  var zoomButton = this._chart.getZoomButton();
  if(zoomButton && zoomButton.getButtonState() == DvtZoomButton.BUTTON_STATE_UP)
  {
    // if graph first gets focus via mouse, the zoom button is shown but focus effect is not applied
    // apply keyboard focus effect to zoom button now
    event.preventDefault();
    zoomButton.drawOverState();
    return;
  }

  if(key == DvtKeyboardEvent.CONTROL || key == DvtKeyboardEvent.SHIFT || 
     key == DvtKeyboardEvent.PAGE_UP || key == DvtKeyboardEvent.PAGE_DOWN)
  {
    // make sure only the graph pans
    // eat these events, so that the entire page doesn't scroll when we get a pageUp/pageDown
    event.preventDefault();
  
    this._handlePanKeyboard(event, true);
  }
  else if(key == DvtKeyboardEvent.ENTER || key == DvtKeyboardEvent.UP_ARROW || key == DvtKeyboardEvent.DOWN_ARROW ||
          key == DvtKeyboardEvent.LEFT_ARROW || key == DvtKeyboardEvent.RIGHT_ARROW || key == DvtKeyboardEvent.ESCAPE) 
  {
    event.preventDefault();
    
    this._handleZoomKeyboard(event);
  }
}


/**
 * Handle pan-related keystrokes
 * @param {Object} event Keystroke event
 * @param {boolean} bKeyDown Whether or not this method is called by this.processKeyDown
 * @private
 */
DvtGraphKeyboardHandler.prototype._handlePanKeyboard = function(event, bKeyDown)
{
  if (this._chart.getPanMode() != DvtGraphPanHandler.PAN_OFF && bKeyDown)
  {
    // On keyDown for pageUp and pageDown, pan the graph
    this._handlePanKeyboardHelper(event);
  }
}
 
 
/**
  * Handles keyboard panning events. 
  * @param {Object} event
  * @private
  */ 
DvtGraphKeyboardHandler.prototype._handlePanKeyboardHelper = function(event)
{
  var xDiff = 0;
  var yDiff = 0;

  var frameRect = this._chart.getFrameRect();

  var bBiDi = DvtAgent.isRightToLeft();

  if (event.keyCode == DvtKeyboardEvent.PAGE_UP)
  {
    if (event.shiftKey)// SHIFT + PAGE_UP
      xDiff =  (!bBiDi) ? -frameRect.w : frameRect.w;
    else // PAGE_UP
      yDiff =  - frameRect.h;
  }
  else if (event.keyCode == DvtKeyboardEvent.PAGE_DOWN)
  {
    if (event.shiftKey)// SHIFT + PAGE_DOWN
      xDiff = (!bBiDi) ? frameRect.w : -frameRect.w;
    else // PAGE_DOWN
      yDiff = frameRect.h;
  }

  // Scroll based on the diff
  if (this._chart.isVertical())// must scroll the correct axis based on orientation
    this._chart.getHandleManager().scrollPlotAreaOffset(xDiff, xDiff, yDiff, yDiff, null, true, true);
  else 
    this._chart.getHandleManager().scrollPlotAreaOffset(yDiff, yDiff, xDiff, xDiff, null, true, true);
}


/**
 * Handle zoom-related keystrokes
 * @param {Object} event Keystroke event
 * @private
 */
DvtGraphKeyboardHandler.prototype._handleZoomKeyboard = function(event)
{
  var zoomButton = this._chart.getZoomButton();
  if (zoomButton) 
  {
    zoomButton.processKeyboardEvent(event);    
  }
}
/**
 * Graph event utils.
 * @class DvtGraphEventUtils
 * @constructor Graph event utils.
 */
var DvtGraphEventUtils = function(){};

DvtObj.createSubclass(DvtGraphEventUtils, DvtObj, "DvtGraphEventUtils");

/**
 * Constructs and returns the id with the specified series and group.
 * @param {string} series The series id.
 * @param {string} group The group id.
 */
DvtGraphEventUtils.getId = function(series, group) {
  return series + "~:" + group;
}

/**
  * 
  */
DvtGraphEventUtils.GetMarkerTooltip = function(chart, x, y)
{

  // Support for showing invisible line marker tooltips.  These tooltips are calculated
  // based on mouse position, and do not have corresponding display objects.
  var mth = chart.getMarkerTooltipHelper();

  if (! mth) {
    return null ;
  }

  // Find the closest line marker within the max distance
  var objMatch = mth.findMatch(x, y);
  var sTip     = (objMatch ? DvtGraphEventUtils.GetMarkerTooltipSeriesGroup(chart, objMatch.marker, objMatch.sidx, objMatch.gidx) : null);
  return sTip ;
};

DvtGraphEventUtils.GetMarkerTooltipSeriesGroup = function(chart, marker, sidx, gidx)
{
  var sTip     = (marker ? marker.getTooltip(sidx, gidx) : null);
  return sTip ;
};

// Only call for lines
DvtGraphEventUtils.GetClosestLinePoint = function(chart, x, y, riser, seriesIndex) 
{
    var minDiffX = 10000;
    var closestGroup = null;
    var mth = chart.getMarkerTooltipHelper();
    if (!mth)
        return null;
    var markers = mth.getMarkers(seriesIndex);
    var currentX = null;
    var currentY = null;
    var markerMatch = null; 
    for (var i=0;i<markers.length;i++) {
        var lm = markers[i];
        var offsets = chart.getScrollOffset(lm.getHandleId());
        var arCoords    = lm.getCoords() ;
        for (var j=0;j<arCoords.length;j+=2) {
            var pointX = arCoords[j] + offsets.x;
            var pointY = arCoords[j+1] + offsets.y;
            var diffX = Math.abs(x-pointX);
            if (diffX < minDiffX) {
                minDiffX = diffX;
                closestGroup = lm.getGroupIndex() + j/2;
                currentX = pointX;
                currentY = pointY;
                markerMatch = lm;
            }
        }
    }
    return {x: currentX, y: currentY, gidx: closestGroup, marker: markerMatch};
}

DvtGraphEventUtils.GetTooltipText = function(chart, dlo, offsetX, offsetY)
{
    if (!dlo || !chart)
        return null;
    
    var tts = null;
    if (dlo && dlo.isMultiRiser()) {
      tts = DvtGraphEventUtils.GetMarkerTooltip(chart, offsetX, offsetY);

      if (! tts) {
        return null;
      }
    }
    if (!tts)
        tts = chart.makeTooltip(dlo) ;

    return tts;
}

DvtGraphEventUtils.GetDataCursorTooltip = function(chart, closestMatch)
{
    var obj = closestMatch.obj;
    if (!obj)
        return null;
    
    var tts = null;
    var dlo   = obj._Dlo ;
    if (dlo && dlo.isMultiRiser()) {
      tts = DvtGraphEventUtils.GetMarkerTooltipSeriesGroup(chart, closestMatch.marker, closestMatch.sidx, closestMatch.gidx);

      if (! tts) {
        return null;
      }
    }
    if (!tts)
        tts = chart.makeTooltip(dlo) ;
 
    return tts;
}

DvtGraphEventUtils.isLegendMarker = function(dlo) {
   if (!dlo || !dlo.getObjId)
     return false;
   var objId  = dlo.getObjId() ;
   var bLegend = (objId === DvtGraphObjType.LEGEND_MARKER) ;
   if (bLegend || objId === DvtGraphObjType.LEGEND_TEXT) {
     return true ;
   }
   return false;
}

DvtGraphEventUtils.isBar = function(dlo) {
   if (!dlo)
     return false;
   var objId  = dlo.getObjId() ;
   return (objId === DvtGraphObjType.BAR_RISER);
}

/**
 * @this {DvtGraphEventUtils}
 */
DvtGraphEventUtils.HighlightSeries = function (chart, sidx, bDim, bOver) {
  // Process the chart objects
  var alpha = (bOver ? 0.35 : 1);
  var aDLOs = chart.getDLOs();
  var len = aDLOs.length;

  if (bDim) {
    for (var i = 0;i < len; i++) {
      var dlo = aDLOs[i];
      if(dlo.getSeriesIndex() >= 0 && dlo.getSeriesIndex() != sidx) {
        var displayable = dlo.getDetObj();
        if(displayable) {
          // On rollover operations, store the old alpha so that we can restore it later
          if(bOver) {
            // If you move the cursor from an underlay to its marker, the _oldAlpha has been defined and it shouldn't
            // be replaced by the current alpha, which is alreadly dimmed during the underlay hover. However, this will 
            // break if someone changes the displayable alpha since the _oldAlpha won't be updated.
            if (displayable._oldAlpha == null)
              displayable._oldAlpha = displayable.getAlpha();
            if(displayable.getAlpha() > alpha)
              displayable.setAlpha(alpha);
          }
          else if(!bOver) {
            // Restore the alpha back to the old value
            var oldAlpha = displayable._oldAlpha != null ? displayable._oldAlpha : alpha;
            if(displayable.getAlpha() < oldAlpha)
              displayable.setAlpha(oldAlpha);
          }
        }
      }
    }
  }
  
  // Notify the chart about the event, allowing it to pass it on to subcomponents like the pie.
  var eventType = bOver ? DvtCategoryRolloverEvent.TYPE_OVER : DvtCategoryRolloverEvent.TYPE_OUT;
  var category = chart.getDataObj().getSeriesFromIndex(sidx);
  var event = new DvtCategoryRolloverEvent(eventType, category);
  chart.processEvent(event, this);
};

DvtGraphEventUtils.HighlightSplitSeries = function (chart, indicies, bDim, bOver) {
    var alpha = (bOver ? 0.35 : 1);
    var aDLOs = chart.getDLOs();
    var len = aDLOs.length;

    if (bDim) {
        for (var i = 0; i < len; i++) {
            var dlo = aDLOs[i];
            var sidx = dlo.getSeriesIndex();
            if(sidx >= 0) {
                var bSetAlpha = true;
                for (var j = 0; j < indicies.length; j++) {
                    if (indicies[j] == sidx)
                        bSetAlpha = false;
                }
                if (bSetAlpha) {
                    var displayable = dlo.getDetObj();
                        if(displayable)
                            displayable.setAlpha(alpha);
                }
                bSetAlpha = true;
            }
        }
    }
    else {
        //  Highlight  TODO
    }
};

DvtGraphEventUtils.UnhighlightAllSeries = function(chart) {
  DvtGraphEventUtils.HighlightSeries(chart, -1, true, false);
}

DvtGraphEventUtils.HighlightMiscLegend = function(chart, midx, bDim, bOver) {
    // Process the chart objects
    var alpha = (bOver ? 0.35 : 1);
    
    if (bDim) {
        var aLeg  = chart.getMiscLegends();         
        if (aLeg) {
            for (i = 0; i < aLeg.length; i++) {
                var obj = aLeg[i];
                var objMidx = obj._Dlo.getMiscId() ;
                if (midx !== objMidx)
                   obj.setAlpha(alpha);
            }
        }
        aLeg  = chart.getLegends();         
        if (aLeg) {
            for (i = 0; i < aLeg.length; i++) {
                for (var j=0; j<aLeg[i].length; j++) {
                    aLeg[i][j].setAlpha(alpha);
                }
            }
        }
    } 
    else {
        //Highlight TODO
    }
}

DvtGraphEventUtils.HighlightSplitSeriesLegend = function(chart, sidx, bDim, bOver) {
    // Process the chart objects
    var alpha = (bOver ? 0.35 : 1);
    var layerList = chart.getLayers();
    var bSetAlpha = true;    

    if (bDim) {
        var aLeg  = chart.getMiscLegends();         
        for (i = 0; i < aLeg.length; i++) {
            var obj = aLeg[i];
            var midx = obj._Dlo.getMiscId() ;
            var layerSeries = layerList[midx];
            
            for (var j=0; j< layerSeries.length; j++) {
                if (sidx == layerSeries[j])
                     bSetAlpha = false;
            }
            if (bSetAlpha)
               obj.setAlpha(alpha);
            bSetAlpha = true;
        }
    }
    else {
        //Highlight TODO
    }
}



DvtGraphEventUtils.HighlightUnderlay = function(chart, bDim, bOver, color, shape) {
    var alpha = (bOver ? 0.35 : 1);
    var aDLOs = chart.getDLOs();
    var len = aDLOs.length;

    if (bDim) {
        for (var i = 0; i < len; i++) {
            var dlo = aDLOs[i];
            if(dlo.getSeriesIndex() >= 0) {
                var displayable = dlo.getDetObj();
                if (shape !== null && shape !== undefined) {
                    if (dlo.getUnderlayType() !== shape) {
                        if(displayable)
                            displayable.setAlpha(alpha);
                    }
                }
                else if (color) {
                    if (dlo.getUnderlayFC() !== color) {
                        if(displayable)
                            displayable.setAlpha(alpha);
                    }
                }
            }
        }
    }
    else {
        //  Highlight  TODO
    }
}

DvtGraphEventUtils.getFirstLegendItem = function(chart)
{
    var aLegends = chart.getLegends() ;
    if (aLegends) {
        var len = aLegends.length;
        if (len > 0) {
            var aSL = aLegends[0] ; 
            var marker = aSL[0] ; 
            return marker;
        }
    }
    return null;
}

DvtGraphEventUtils.getLegendFromPosition = function(chart, x, y)
{
    var closestText = null;
    var txt = null;
    var minDiff = 100000;
     var aLegends = chart.getLegends() ;
     if (aLegends) {
       var len = aLegends.length;
       for (var i = 0; i < len; i++) {
          var aSL = aLegends[i] ;         // get a legend and text array
          if (aSL === undefined) {
            continue ;
          }
          var marker = aSL[0] ;        // the marker
          var textY = marker.getDimensions().y;
          var textHeight = marker.getDimensions().h;
          var midpoint = textY + textHeight/2;
          var diffValue = midpoint - y;
          var diff = Math.floor(Math.abs(diffValue));
          if (diff < minDiff) {
            closestText = marker;
            txt = aSL[1];
            minDiff = diff;
          }
       }
     }
     return {marker: closestText, text: txt};
}

DvtGraphEventUtils.isActionablePlotPosition = function(chart, x, y, logicalObj) {
    return DvtGraphEventUtils.getActionablePlotRect(chart, x, y, logicalObj) != null;
}

DvtGraphEventUtils.getActionablePlotRect = function(chart, x, y, logicalObj) {
   
   if (chart._topPlotRect.containsPoint(x, y)) {
       return chart._topPlotRect;
   } else if (chart._bottomPlotRect.containsPoint(x, y)) {
       return chart._bottomPlotRect;
   }
   return null;
}

DvtGraphEventUtils.isNonLegendData = function(obj) {
   if (obj) {
        var categories = obj.getCategories(); // can return an array with null obj
        if(categories[0] !== null && !DvtGraphEventUtils.isLegendMarker(obj)) {
            return true;
        }
   }
   return false;
}
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. 
// All rights reserved. 

/*--------------------------------------------------------------------------*/
/*   Class DataZoom      Encapsulates the zooming interaction of the        */
/*                       combined zooming/scrolling interactivity feature.  */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  DataZoom is a transparent sprite placed over the chart so that the user */
/*  can drag a rubberband rect over the chart to select an area to zoom     */
/*  in/out. When in zoom mode, the scrollbars are not available, and the    */
/*  zoom cursor will show.                                                  */
/*--------------------------------------------------------------------------*/
/**
 * @constructor
 */
var DvtGraphPanHandler = function(context, chart, bounds, panMode)
{
    this.m_bIsPanning = false;   // false == default mode,  true == pan mode 
    this.m_ctrlDown = false;     // is the CTRL key pressed
    
    this.m_chart = chart;
    
    // Store and update the position
    this.x   = bounds.x ;
    this.y   = bounds.y ;

    // Height and width of the plotArea
    this.m_w = bounds.w ;
    this.m_h = bounds.h ;
      
    this.m_panMode  = panMode; 
    if(this.m_panMode != DvtGraphPanHandler.PAN_OFF) {
       this.m_bIsPanning = true;
    }
   
}

// Zoom and Pan Constants
DvtGraphPanHandler.PAN_OFF   = 0;
DvtGraphPanHandler.PAN_AUTO  = 1;  // on if the user clicks a non-selectable area inside the plotArea
  
DvtGraphPanHandler.PAN_TOUCH_KEY  = "panTouch";

DvtObj.createSubclass(DvtGraphPanHandler, DvtObj, "DvtGraphPanHandler");

/**
 * Enable or disable panning
 * @param {boolean} panningEnabled
 */
DvtGraphPanHandler.prototype.enablePanning = function(panningEnabled)
{
  this.m_bIsPanning = panningEnabled;
}

/*----------------------------------------------------------------------*/
/*    removeCursor()                                               */
/*----------------------------------------------------------------------*/
DvtGraphPanHandler.prototype.removeCursor = function() {
    this.m_chart.setCursor("default");
}

/**
* Returns true if the cursor is positioned within the plotArea.  
* When panning, also checks to make sure that a marker is not beneath the cursor.
*/ 

DvtGraphPanHandler.prototype.isCursorInPlotArea = function(eventTarget) {
    // TODO: marker finish
  // We don't want to pan when over a DetMarker, esp due to drag and drop
 // if(event != null && this.m_bIsPanning && (event.target is DetMarker))
  //  return false;
  if(this.m_mouseX >= 0 && this.m_mouseX <= this.m_w && this.m_mouseY >= 0 && this.m_mouseY <= this.m_h)
    return true;
  else
  {     
    return false;
  }
}

DvtGraphPanHandler.prototype.processDragStart = function(relPos, startObj) {
   // for interactive testing - redundant as MouseMove would 
   // have set these, but under testing, a mouse move prior to this
   // may not have been called!
   this.m_mouseX = relPos.x - this.x;
   this.m_mouseY = relPos.y - this.y;
  
   // If not zoom mode, pan mode, or within plotArea, return
   if(!(this.m_bIsPanning) || !this.isCursorInPlotArea(startObj))
     return false;
     
   this.m_bMouseDown = true ;
   this.m_iStartPosX = this.m_mouseX ;  // else mouseX
   this.m_iStartPosY = this.m_mouseY ;  // else mouseY
   this.m_prevPosX = this.m_mouseX;  // else mouseX
   this.m_prevPosY = this.m_mouseY;  // else mouseY
   
   // Show pan cursor if panning
   if(this.m_bIsPanning) {
     this.updateCursor();
   }
   return true;

}

DvtGraphPanHandler.prototype.processDragMove = function(relPos) {
    this.m_mouseX = relPos.x - this.x;
    this.m_mouseY = relPos.y - this.y;
   // handle zoom or pan Modes
    if(this.m_bIsPanning)
      return this.handlePanMouseMove();  
    return false;
}

DvtGraphPanHandler.prototype.processDragMoveDelta = function(deltas) {
   var point = new DvtPoint(this.m_mouseX + this.x + deltas.deltaX, this.m_mouseY +this.y + deltas.deltaY);
   this.processDragMove(point);
}

/**
 * Handles mouse moves while in pan mode. 
 */ 
DvtGraphPanHandler.prototype.handlePanMouseMove = function() {
   // Nothing to do if mouse is up
   if(!this.m_bMouseDown) {
     return false;
   }
 
   // move our cursor to track the mouse
   this.updateCursor();

   // Update the plotArea rect
   // First figure out the new bounded x and y mouse positions

   // for interative testing
   var mX = this.m_mouseX; // mouseX ;           
   var mY = this.m_mouseY; // mouseY ;           

   //  The mouse move events come from the stage, and are received while
   //  mouse is down, so must keep the panning within the graph frame.

   // for interative testing
   if (this.m_mouseX > this.m_w)
     mX = this.m_w;
   if (this.m_mouseX < 0)
     mX = 0;
   if (this.m_mouseY > this.m_h)
     mY = this.m_h;
   if (this.m_mouseY < 0)
     mY = 0;


   // Change in x and y since last mouseMove
   var xDiff = mX - this.m_prevPosX;
   var yDiff = mY - this.m_prevPosY;         
   
   // Update the old mouse positions
   this.m_prevPosX = mX;
   this.m_prevPosY = mY;
   
   // Finally scroll the graph
   // We want to scroll in the opposite direction of the mouse movement
   // as if the user is grabbing the canvas and moving it
   if(this.m_chart.isVertical()) // must scroll the correct axis based on orientation
     this.m_chart.getHandleManager().scrollPlotAreaOffset(-xDiff, -xDiff, -yDiff, -yDiff, new DvtPoint(this.x + mX, this.y + mY));
   else
     this.m_chart.getHandleManager().scrollPlotAreaOffset(-yDiff, -yDiff, -xDiff, -xDiff, new DvtPoint(this.x + mX, this.y + mY)); 
   return true;
    
}

DvtGraphPanHandler.prototype.processDragEnd = function(relPos) {

   // for interactive testing - redundant as MouseMove would 
   // have set these, but under testing, a mouse move prior to this
   // may not have been called!
   this.m_mouseX = relPos.x - this.x;
   this.m_mouseY = relPos.y - this.y;
  
//trace("onMouseUp()         m_mouseX = " + m_mouseX + ", m_mouseY = " + m_mouseY);        
   
   if (! this.m_bMouseDown)
     return ;
     
   this.m_bMouseDown = false ;
   
   if(this.m_bIsPanning)
     this.handlePanMouseUp();
}

/**
 * Handles mouse up while in pan mode.
 */ 
DvtGraphPanHandler.prototype.handlePanMouseUp = function() {
   // Hide pan cursor
   this.updateCursor();
   // Fire a pan event to JS
   this.m_chart.getHandleManager().dispatchJSEvents();
}

/**
 * Initializes and creates the zoom or pan cursors.
 */ 
DvtGraphPanHandler.prototype.updateCursor = function(event) {
   if(this.isCursorInPlotArea(null))
   {
     if(this.m_bIsPanning && this.m_bMouseDown) {
        var selectCursor = DvtSelectionEffectUtils.getSelectingCursor();
        this.m_chart.setCursor(selectCursor);
     } else {
        // No cursor should be shown
        this.removeCursor();
        return;
     }
   }
   else {
     // Cursor is not in plotArea, hide it
     this.removeCursor();
   }
}  

/*---------------------------------------------------------------------*/
/*   removeListeners()                                                 */
/*---------------------------------------------------------------------*/
/*
DvtGraphPanHandler.prototype.removeListeners = function() {
   if (this.m_bListenersActive)
   {
    var stage = this.m_chart;
   //  Set up mouse event handlers
   stage.removeEvtListener(DvtMouseEvent.MOUSEDOWN, this.onMouseDown, false, this) ;
   stage.removeEvtListener(DvtMouseEvent.MOUSEMOVE, this.onMouseMove, false, this) ;
   stage.removeEvtListener(DvtMouseEvent.MOUSEUP,   this.onMouseUp,   false, this) ;
//     stage.removeEvtListener(KeyboardEvent.KEY_DOWN, onKeyDown) ;
  //   stage.removeEvtListener(KeyboardEvent.KEY_UP,   onKeyUp) ;

     this.m_bListenersActive = false ;
   }
}
*/


/**
 * Returns a boolean indicating whether or not we are currently panning
 * @return {boolean}
 */
DvtGraphPanHandler.prototype.isCurrentlyPanning = function()
{
  return this.m_bMouseDown;
}
// Handler used for marquee operations
/**
 * @constructor
 */
var   DvtGraphMarqueeHandler = function(context, rootDisplayable, bounds, panelBehavior)
{
    this.Init(context, rootDisplayable, bounds, panelBehavior);
}  

DvtGraphMarqueeHandler.PANEL_BEHAVIOR_BOTH  = 0;
DvtGraphMarqueeHandler.PANEL_BEHAVIOR_HORIZ   = 1;
DvtGraphMarqueeHandler.PANEL_BEHAVIOR_VERT   = 1;

DvtObj.createSubclass(DvtGraphMarqueeHandler, DvtObj, "DvtGraphMarqueeHandler");

DvtGraphMarqueeHandler.prototype.Init = function(context, rootDisplayable, bounds, panelBehavior) {
    this._context = context;
    this._rootDisplayable = rootDisplayable;
    
    this._panelBehavior = DvtGraphMarqueeHandler.PANEL_BEHAVIOR_BOTH;
    if (panelBehavior)
        this._panelBehavior = panelBehavior;
    
    this.m_iBandWidth = 1;          // rubber band line width
    this.m_iBandColor = "#c0c0c0";  // rubber band color

    // Store and update the position
    this.x   = bounds.x ;
    this.y   = bounds.y ;

    // Height and width of the plotArea
    this.m_w = bounds.w ;
    this.m_h = bounds.h ;
    
    // Enabled by default
    this._isMarqueeEnabled = true;

    this.InitMarqueePanel();    
}

DvtGraphMarqueeHandler.prototype.isMarqueeEnabled = function()
{
  return this._isMarqueeEnabled;
}

DvtGraphMarqueeHandler.prototype.enableMarquee = function(marqueeEnabled)
{
  this._isMarqueeEnabled = marqueeEnabled;
}

DvtGraphMarqueeHandler.prototype.InitMarqueePanel = function() {
    var panel = new DvtGraphMarqueePanel(this._context);
    panel.setVisible(false);
    this._panel = panel;
    this._rootDisplayable.addChild(panel);
}
  
/**
* Returns true if the cursor is positioned within the plotArea.  
* When panning, also checks to make sure that a marker is not beneath the cursor.
*/ 
DvtGraphMarqueeHandler.prototype.isInBounds = function(eventTarget) {
  if(this.m_mouseX >= 0 && this.m_mouseX <= this.m_w && this.m_mouseY >= 0 && this.m_mouseY <= this.m_h)
    return true;
  else
  {     
    return false;
  }
}

DvtGraphMarqueeHandler.prototype.cancelMarqueeZoom = function()
{
  this.removeCursor();
  this.m_bMouseDown = false;
  var panel = this._panel;
  if (panel)
     panel.setVisible(false);
}

DvtGraphMarqueeHandler.prototype.processDragStart = function(relPos, startObj) {
   // for interactive testing - redundant as MouseMove would 
   // have set these, but under testing, a mouse move prior to this
   // may not have been called!
   this.m_mouseX = relPos.x - this.x;
   this.m_mouseY = relPos.y - this.y;
   // If not zoom mode, pan mode, or within plotArea, return
   if(!(this._isMarqueeEnabled) || !this.isInBounds(startObj))
     return false;
     
   this.m_bMouseDown = true ;
   this.m_iStartPosX = this.m_mouseX ;  // else mouseX
   this.m_iStartPosY = this.m_mouseY ;  // else mouseY
   this.m_prevPosX = this.m_mouseX;  // else mouseX
   this.m_prevPosY = this.m_mouseY;  // else mouseY
   return true;
   
}

DvtGraphMarqueeHandler.prototype.processDragMove = function(relPos) {
    this.m_mouseX = relPos.x - this.x;
    this.m_mouseY = relPos.y - this.y;
    
   // handle zoom or pan Modes
   if(this._isMarqueeEnabled)
     return this.handleMarqueeMove();  
   return false;
}

/**
 * Handles mouse moves while in zoom mode. 
 */ 
DvtGraphMarqueeHandler.prototype.handleMarqueeMove = function() {
   // move our cursor to track the mouse
   this.updateCursor();
   //  If mouse is down, update the rubber band rect.
   if (this.m_bMouseDown)
   {
      var panel = this._panel;
      this.calcMarqueeCoords();  // Calculate the rectangle positions
      panel.setPanelSize(this._panelWidth, this._panelHeight);
      panel.Render();
      panel.setTranslateX(this.x + this._panelX);
      panel.setTranslateY(this.y + this._panelY);
      panel.setBandAttributes(this.m_iBandWidth, this.m_iBandColor);
      panel.setVisible(true);
      return true;
   }
   return false;
}

DvtGraphMarqueeHandler.prototype.processDragEnd = function(relPos) {

   // for interactive testing - redundant as MouseMove would 
   // have set these, but under testing, a mouse move prior to this
   // may not have been called!
   this.m_mouseX = relPos.x - this.x;
   this.m_mouseY = relPos.y - this.y;
   
   if (! this.m_bMouseDown)
     return ;
   this.m_bMouseDown = false ;
   
   if(this._isMarqueeEnabled)
     this.handleMarqueeEnd();
}

/**
 * Handles mouse up while in zoom mode.
 */ 
DvtGraphMarqueeHandler.prototype.handleMarqueeEnd = function() {
   // Hide the custom cursor
   this.removeCursor();
   // Calculate the rectangle position based on current mouse coords
   this.calcMarqueeCoords();

   this.HandleMarqueeDone();

}

DvtGraphMarqueeHandler.prototype.HandleMarqueeDone = function() {
}

/**
* Using the current mouse position and a set of starting coordinates, calculates
* the location of the zoom rectangle.
*/ 
DvtGraphMarqueeHandler.prototype.calcMarqueeCoords = function() {
  var mX = this.m_mouseX; // mouseX ;           
  var mY = this.m_mouseY; // mouseY ;           

  if (this.m_mouseX > this.m_w)
    mX = this.m_w;
  if (this.m_mouseX < 0)
    mX = 0;
  if (this.m_mouseY > this.m_h)
    mY = this.m_h;
  if (this.m_mouseY < 0)
    mY = 0;

  if (this._panelBehavior == (DvtGraphMarqueeHandler.PANEL_BEHAVIOR_BOTH))
  {
     //  No contraints on rubber band rect (within Frame)
     this._panelX  =  Math.min(this.m_iStartPosX, mX - 1);
     this._panelY  =  Math.min(this.m_iStartPosY, mY - 1);
     this._panelWidth  =  Math.abs(mX - this.m_iStartPosX);
     this._panelHeight  =  Math.abs(mY - this.m_iStartPosY);
  }
  else if (this._panelBehavior == DvtGraphMarqueeHandler.PANEL_BEHAVIOR_HORIZ)
  {
     //  Vertical sides extend to Frame height
     this._panelX  =  Math.min(this.m_iStartPosX, mX - 1);
     this._panelY  =  0;
     this._panelWidth  =  Math.abs(mX - this.m_iStartPosX);
     this._panelHeight  =  this.m_h;
  }
  else if (this._panelBehavior == DvtGraphMarqueeHandler.PANEL_BEHAVIOR_VERT)
  {
     //  Horizontal sides extend to Frame width
     this._panelX  =  0;
     this._panelY  =  Math.min(this.m_iStartPosY, mY - 1);
     this._panelWidth  =  this.m_w;
     this._panelHeight  =  Math.abs(mY - this.m_iStartPosY);
  }
}


/**
 * Initializes and creates the zoom or pan cursors.
 */ 
DvtGraphMarqueeHandler.prototype.updateCursor = function() {
    // Impl overrides
}

/*----------------------------------------------------------------------*/
/*    removeCursor()                                               */
/*----------------------------------------------------------------------*/
DvtGraphMarqueeHandler.prototype.removeCursor = function() {
    // Impl overrides
}

/**
 * Returns a boolean indicating whether or not we are currently in the process of drawing a marquee rubber band
 * @return {boolean}
 */
DvtGraphMarqueeHandler.prototype.isMarqueeHappening = function()
{
  return this.m_bMouseDown;
}
// Handler used for marquee zoom operations
/**
 * @constructor
 */
var   DvtGraphMarqueeZoomHandlerBase = function(context, rootDisplayable, bounds, zoomMode, panelBehavior)
{
    this.InitZoom(context, rootDisplayable, bounds, zoomMode, panelBehavior);
}  

DvtGraphMarqueeZoomHandlerBase.ZOOM_OFF  = 0;
DvtGraphMarqueeZoomHandlerBase.ZOOM_ON   = 1;
DvtGraphMarqueeZoomHandlerBase.ZOOM_AUTO = 2;  // on if the CTRL key is pressed

DvtObj.createSubclass(DvtGraphMarqueeZoomHandlerBase, DvtGraphMarqueeHandler, "DvtGraphMarqueeZoomHandlerBase");

DvtGraphMarqueeZoomHandlerBase.prototype.InitZoom = function(context, rootDisplayable, bounds, zoomMode, panelBehavior) {
    DvtGraphMarqueeZoomHandlerBase.superclass.Init.call(this, context, rootDisplayable, bounds, panelBehavior);

    this.m_bZoomIn    = true ;   // true == zoom in cursor, false == zoom out cursor
    
    this.m_zoomMode = DvtGraphMarqueeZoomHandlerBase.ZOOM_ON;
    if (zoomMode != null)
        this.m_zoomMode = zoomMode;

    this._isMarqueeEnabled = false;   // false == default mode, true == zoom mode
    if(this.m_zoomMode == DvtGraphMarqueeZoomHandlerBase.ZOOM_ON) {
       this._isMarqueeEnabled = true;  // for auto, isZooming is turned on by keyboard
       this.updateCursor();
    }   
}

/**
 * Process shift-zoom key events
 * @param {boolean} bKeyDown
 */
DvtGraphMarqueeZoomHandlerBase.prototype.processShiftZoom = function(bKeyDown)
{
  // Toggle between ZoomIn vs ZoomOut on SHIFT
     
  //  var bZoomState:Boolean = (! event.shiftKey)  ; // false if SHIFT, else true
  // 
  //  The above test doesn't seem to work in FireFox (ok for IE7).  The boolean
  //  is always false.  The following seems to be ok though.
      
  var bZoomIn = !bKeyDown;        // true if zoom in, false if zoom out
  if (this.m_bZoomIn != bZoomIn)  // if zoom state change
  {                                     
    this.m_bZoomIn = bZoomIn ;
    this.updateCursor();
  }
}
// Handler used for marquee graph marquee zoom operations
/**
 * @constructor
 */
var   DvtGraphMarqueeZoomHandler = function(context, chart, bounds, zoomMode, zoomDirection)
{
    this.InitZoom(context, chart, bounds, zoomMode, zoomDirection);
}  

DvtGraphMarqueeZoomHandler.TOK_X =  "__X__" ;
DvtGraphMarqueeZoomHandler.TOK_Y =  "__Y__" ;
DvtGraphMarqueeZoomHandler.TOK_W =  "__W__" ;
DvtGraphMarqueeZoomHandler.TOK_H =  "__H__" ;

DvtObj.createSubclass(DvtGraphMarqueeZoomHandler, DvtGraphMarqueeZoomHandlerBase, "DvtGraphMarqueeZoomHandler");

DvtGraphMarqueeZoomHandler.prototype.InitZoom = function(context, chart, bounds, zoomMode, zoomDirection) {
    this.m_chart = chart;
    var panelBehavior = DvtGraphMarqueeZoomHandler.convertDirection(zoomDirection);
    DvtGraphMarqueeZoomHandler.superclass.InitZoom.call(this, context, chart, bounds, zoomMode, panelBehavior);
}

DvtGraphMarqueeZoomHandler.convertDirection = function(direction) {
    if (direction == DvtGraphCore.DATA_ZOOM_BOTH) {
        return DvtGraphMarqueeHandler.PANEL_BEHAVIOR_BOTH;
    } else if (direction == DvtGraphCore.DATA_ZOOM_HORIZ) {
        return DvtGraphMarqueeHandler.PANEL_BEHAVIOR_HORIZ;
    } else if (direction == DvtGraphCore.DATA_ZOOM_VERT) {
        return DvtGraphMarqueeHandler.PANEL_BEHAVIOR_VERT;
    }
    return DvtGraphMarqueeHandler.PANEL_BEHAVIOR_BOTH;
}

DvtGraphMarqueeZoomHandler.prototype.HandleMarqueeDone = function() {
   //  Terminate zoom interactivity.  
   this._isMarqueeEnabled = false ;
   
   //---------------------------//
   //    Fire the event to js   //
   //---------------------------//
   var  initJs  = null;
   var  finalJs  = null;
   var  bSingleClick = null;
   
   //TODO: implement events
   
   //  If a rubber band rect has width and height < 2, we treat this a single click.

   if (this._panelWidth  < 2 && this._panelHeight < 2)
   {
      if (this.m_bZoomIn && (this.m_chart._zoomInClickEventJs != null))
      {
        initJs = this.m_chart._zoomInClickEventJs ;
      }
      else  if ( (! this.m_bZoomIn) && (this.m_chart._zoomOutClickEventJs != null) )
      {
        initJs = this.m_chart._zoomOutClickEventJs ;
      }
      bSingleClick = true ;                  // m_iBandRectWidth and m_iBandRectHeight
                                             // not used for single-click event.
   }
   else
   {
      //  Have a complete rubber band rect

      if (this.m_bZoomIn && (this.m_chart._zoomInRectEventJs != null))
      {
        initJs = this.m_chart._zoomInRectEventJs ;
      }
      else if ( (! this.m_bZoomIn) && (this.m_chart._zoomOutRectEventJs != null))
      {
        initJs =  this.m_chart._zoomOutRectEventJs ;
      }
   }

   // The event wants coords relative to the View (which is really m_Container),
   // so we need to map the rubber band rect coords.
   var  mappedX = this._panelX + this.x ;
   var  mappedY = this._panelY + this.y ;

   //  Emit the event
   if (initJs != null)
   {
     finalJs = initJs.replace(DvtGraphMarqueeZoomHandler.TOK_X, mappedX);
     finalJs = finalJs.replace(DvtGraphMarqueeZoomHandler.TOK_Y, mappedY);

     if (! bSingleClick)                               
     {
        finalJs = finalJs.replace(DvtGraphMarqueeZoomHandler.TOK_W, this._panelWidth);
        finalJs = finalJs.replace(DvtGraphMarqueeZoomHandler.TOK_H, this._panelHeight);
     }                                  

     if (finalJs != null)
     {
       //BUG 34184877 - MAKE DVT COMPATIBLE WITH CSP STANDARDS
       var jsArray = finalJs.match(/'(.*?)'/g);
       var src = jsArray[0].replace(/\'/g, "");
       var eventData = jsArray[2].replace(/\'/g, "");
       var bi_gdzXData = jsArray[3].replace(/\'/g, "");
       var bi_gdzYData = jsArray[4].replace(/\'/g, "");
       if(jsArray.length>5)
       {
         var bi_gdzWData = jsArray[5].replace(/\'/g, "");
         var bi_gdzHData = jsArray[6].replace(/\'/g, "");
         AdfPage.PAGE.findComponent(src).getPeer().sendToServerPPR({source:src ,event: eventData ,bi_gdzX: bi_gdzXData ,bi_gdzY: bi_gdzYData ,bi_gdzW: bi_gdzWData ,bi_gdzH: bi_gdzHData});
       }
       else
       {
         AdfPage.PAGE.findComponent(src).getPeer().sendToServerPPR({source:src ,event: eventData ,bi_gdzX: bi_gdzXData ,bi_gdzY: bi_gdzYData});
       }
     }
   }

}


/**
 */ 

DvtGraphMarqueeZoomHandler.prototype.updateCursor = function() {
// TODO: add cursor support
   if(this.isInBounds(null))
   {
     if(this._isMarqueeEnabled) {
        if (this.m_bMouseDown) {
            var selectCursor = DvtSelectionEffectUtils.getSelectingCursor();
            this.m_chart.setCursor("pointer");
        } else {
            // TODO: in the future, this will be a zoom in cursor
            this.m_chart.setCursor("pointer");
        }
     } else {
        // No cursor should be shown
        this.removeCursor();
        return;
     }
   }
   else {
     // Cursor is not in plotArea, hide it
     this.removeCursor();
   }
}  

/*----------------------------------------------------------------------*/
/*    removeCursor()                                               */
/*----------------------------------------------------------------------*/
DvtGraphMarqueeZoomHandler.prototype.removeCursor = function() {
    this.m_chart.setCursor("default");
}
/**
 * Drag Source event handler for DvtGraph
 * @param {DvtGraphCore} graph
 * @param {DvtContext} context
 * @class DvtGraphDragSource
 * @extends DvtDragSource
 * @constructor
 */
var DvtGraphDragSource = function (graph, context) {
  this.Init(context);
  
  this._graph = graph;
  this._pointerOffset = null;
};

DvtObj.createSubclass(DvtGraphDragSource, DvtDragSource, "DvtGraphDragSource");

/**
 * @override
 */
DvtGraphDragSource.prototype.setDragCandidate = function (obj) {
  var detObj = (obj && obj.getDetObj) ? obj.getDetObj() : null;
  DvtGraphDragSource.superclass.setDragCandidate.call(this, detObj);
}

/**
 * @override
 */
DvtGraphDragSource.prototype.getDragContext = function (mouseX, mouseY) {
  if (this.getDragObject() && this._graph) 
    return this._graph.getDragContext(mouseX, mouseY);
  else 
    return DvtGraphDragSource.superclass.getDragContext.call(this, mouseX, mouseY);
}

/**
 * @override
 */
DvtGraphDragSource.prototype.getDragOffset = function (mouseX, mouseY) {
  var offset = null;
  if (this.getDragObject() && this._graph) {
    var bounds = this._graph.getDragFeedbackBounds();
    if (bounds) {
      offset = {};
      offset.x = mouseX - bounds.x;
      offset.y = mouseY - bounds.y;
      offset.x += DvtDragSource.DRAG_FEEDBACK_MARGIN;
      offset.y += DvtDragSource.DRAG_FEEDBACK_MARGIN;
    }
  }
  return offset;
}

/**
 * @override
 */
DvtGraphDragSource.prototype.getPointerOffset = function (xOffset, yOffset) {
  this._pointerOffset = {x: xOffset, y: yOffset};
  
  // For multi selections, use the pointer location instead of the center
  if (this._graph && this._graph.getSelectionHandler().getSelection().length > 1) {
    this._pointerOffset.x = 0;
    this._pointerOffset.y = 0;
  }
  
  // Pass the modified offset to the superclass
  return this._pointerOffset;
}

/**
 * Returns the precomputed drag offset applied by the peer, if available.
 * @return {Object}
 */
DvtGraphDragSource.prototype.__getPointerOffset = function() {
  return this._pointerOffset;
}
/**
 * Drop Target event handler for DvtGraph
 * @param {DvtGraphCore} graph
 * @class DvtGraphDropTarget
 * @extends DvtDropTarget
 * @constructor
 */
var DvtGraphDropTarget = function(graph) {
  this._graph = graph;
};

DvtObj.createSubclass(DvtGraphDropTarget, DvtDropTarget, "DvtGraphDropTarget");

/**
 * @override
 */
DvtGraphDropTarget.prototype.acceptDrag = function (mouseX, mouseY, clientIds) {
  if(this._graph)
    return this._graph.acceptDrag(mouseX, mouseY, clientIds);
  else
    return DvtGraphDropTarget.superclass.acceptDrag.call(this, mouseX, mouseY, clientIds);
}

/**
 * @override
 */
DvtGraphDropTarget.prototype.dragEnter = function () {
  if(this._graph)
    return this._graph.dragEnter();
  else
    return DvtGraphDropTarget.superclass.dragEnter.call(this);
}

/**
 * @override
 */
DvtGraphDropTarget.prototype.dragExit = function () {
  if(this._graph)
    return this._graph.dragExit();
  else
    return DvtGraphDropTarget.superclass.dragExit.call(this);
}

/**
 * @override
 */
DvtDropTarget.prototype.getDropSite = function (mouseX, mouseY) {
  if(this._graph)
    return this._graph.getDropSite(mouseX, mouseY);
  else
    return DvtGraphDropTarget.superclass.getDropSite.call(this, mouseX, mouseY);
}
// Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
/*--------------------------------------------------------------------------*/
/*   DVTGraphParser      Platform independent graph xml parser              */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  XML Tag Reference                                                       */
/*  -----------------                                                       */
/*                                                                          */
/*  Elements :                                                              */
/*               <c>      circle                                            */
/*               <f>      font                                              */
/*               <g>      gradient                                          */
/*               <h>      handle                                            */
/*               <l>      line                                              */
/*               <lg>     line gradient                                     */
/*               <lm>     lineMarker                                        */
/*               <m>      marker                                            */
/*               <o>      oval (ellipse)                                    */
/*               <p>      path                                              */
/*               <pie>    pie - 2D and 3D                                   */
/*               <pg>     polygon                                           */
/*               <pl>     polyline                                          */
/*               <pr>     user properties                                   */
/*               <r>      rectangle                                         */
/*               <rs>     range slider                                      */
/*               <rszb>   range sider zoom button                           */
/*               <ro>     referenceObject                                   */
/*               <ros>    referenceObjects                                  */
/*               <sa>     shape attribute                                   */
/*               <sl>     pie slice - 2D and 3D                             */
/*               <t>      text                                              */
/*               <ttf>    tooltip format                                    */
/*                                                                          */
/*  <graph> and <gaugeset> Attributes :                                     */
/*                                                                          */
/*               aD=       animation down-color/alpha                       */
/*               aDC=      animation type (NONE, AUTO, ON)                  */
/*               aDi=      animation init effects (NONE, AUTO)              */
/*               aDu=      animation duration (ms)                          */
/*               aI=       animation indicators (NONE, ALL)                 */
/*               aU=       animation up-color/alpha                         */
/*               ads=      ADS candidate                                    */
/*               dataZoomInRectEventJS=                                     */
/*               dataZoomOutRectEventJS=                                    */
/*               dataZoomInClickEventJS=                                    */
/*               dataZoomOutClickEventJS=                                   */
/*               drag=    drag/drop enable, bounded/unbounded               */
/*               gaugeType=     gauge type                                  */
/*               graphType=     graph type                                  */
/*               h=       chart height                                      */
/*               hs=      hide/show series                                  */
/*               id=      chart ID       (unused?)                          */
/*               mz       marquee zoom mode                                 */
/*               pan=     drag panning mode                                 */
/*               pse=     pieslice explode behavior                         */
/*               psea=    pieslice explodeAll/uniteAll behavior             */
/*               se=      series effect  ("g" = gradient, ???)              */
/*               sel=     selection mode type (single or multiple)          */
/*               selList=   middletier has a selection listener             */
/*               sr=      series rollover effects                           */
/*               tooltips=  enable/disabled tooltips                        */
/*               tooltipEffect=  e.g. shadow, glow                          */
/*               threed=  chart is 3D                                       */
/*               ve=      visual effect  ("t/f" = show visual effects)      */
/*                        e.g. 3D pie - show PieBorders, 2D - show PieGlow  */
/*               w=       chart width                                       */
/*               zoomAndScroll=   zoom and scroll behavior                  */
/*               zoomDir=   zoom directions                                 */
/*                                                                          */
/*                                                                          */
/*  <md> Metadata Attributes                                                */
/*               ac=      autocolor (for gauges)                            */
/*               ang=     start & end angles for dial gauges                */
/*               d=       dial gauge metadata                               */
/*               id=      gauge id                                          */
/*               slp=     pie slice label position                          */
/*               slt=     pie slice label type                              */
/*               tc=      gauge threshold colors                            */
/*               tv=      gauge threshold values                            */
/*                                                                          */
/*                                                                          */
/*  Object Attributes :                                                     */
/*               angEx=   angleExtent                                       */
/*               angS=    angleStart                                        */
/*               a=       angle          (for text)                         */
/*               b=       bounding rect in a gradient                       */
/*               bv=      bevel  spec                                       */
/*               c=       color                                             */
/*               c=       color list for <g>                                */
/*               clip=    id of clipping rect name                          */
/*               ct=      closureType                                       */
/*               depth=   depth in pixels of 3D pie                         */
/*               ex=      pie slice exploded offset                         */
/*               fc=      fillColor                                         */
/*               fid=     font id in a <t> to match a <f>                   */
/*               ft=      fillType                                          */
/*               gl=      glow spec                                         */
/*               go=      mouse-transparent (graphics only)                 */
/*               groups=  array of group ID strings                         */
/*               groupsLabels  array of group label strings                 */
/*               G=       gId    (group ID)                                 */
/*               j=       alignment                                         */
/*               ls=      lineStyle                                         */
/*               l=       label for 3D/2D pie                               */
/*               lw=      lineWidth                                         */
/*               mt=      marker type (for scatter plots)                   */
/*               M=       mId      (misc. ID)                               */
/*               md=      metaData                                          */
/*               O=       oId      (object ID)                              */
/*               p=       points   (for polygon <pg>)                       */
/*               p=       positional info (for rect, line, oval, circle...) */
/*               p=       positional info for <rs> and <rszb>               */
/*               p=       position list for <g>                             */
/*               por=     point-of-rotation for <p>                         */
/*               pts=     points for a custom gradient in <g>               */
/*               PSLVF=   pie slice label value format spec                 */
/*               rotate=  starting angle for drawing slices                 */
/*               S=       sId     (series ID)                               */
/*               scrollJS= scroll event JS in <rs> rangeslider              */
/*               series=  array of series ID strings                        */
/*               seriesLabels  array of series label strings                */
/*               sh=      shadow  spec                                      */
/*               sa=      shapeAttribute                                    */
/*               tp=      thumb size and start position for <rszb>          */
/*               t=       specific tooltip text for the object.             */
/*               tt=      on detobj true/false use of tooltip template      */
/*                        on <md> specifies a tooltip template              */
/*               umc=     anaytic layer underlay shape color (& alpha)      */
/*               ums=     anaytic layer underlay shape type                 */
/*               ut=      untruncated text (for <t> elements                */
/*               v=       visibility                                        */
/*               x1in=    range slider zoom event javascript                */
/*               x1out=    ..    ..     ..    ..      ..                    */
/*               y1in=     ..    ..     ..    ..      ..                    */
/*               y1out=    ..    ..     ..    ..      ..                    */
/*               y2in=     ..    ..     ..    ..      ..                    */
/*               y2out=    ..    ..     ..    ..      ..                    */
/*               zoomJS=  zoom event JS in <rs> rangeslider                 */
/*                                                                          */
/*                                                                          */
/*  Data value attributes for tooltip generation                            */
/*                                                                          */
/*               XV=       formatted X tooltip value                        */
/*               YV=       formatted Y tooltip value                        */
/*               ZV=       formatted Z tooltip value                        */
/*               GM=       group member                                     */
/*               SM=       series member                                    */
/*               XM=       X member                                         */
/*               XM=       Y member                                         */
/*               XM=       Z member                                         */
/*               OM=       open member                                      */
/*               LM=       low member                                       */
/*               HM=       high member                                      */
/*               CM=       close member                                     */
/*               ON=       object name                                      */
/*               OD=       object description                               */
/*               OID=      object ID                                        */
/*               OIN=      object instance                                  */
/*               CUMSTKV=  cumulative stacked value                         */
/*               CUMPCTV=  cumulative percentage value                      */
/*               PIEPCTV=  pie percentage value                             */
/*               HV=       hilo high value                                  */
/*               LV=       hilo low value                                   */
/*               OV=       hilo open value                                  */
/*               CV=       hilo close value                                 */
/*               VV=       hilo volume value                                */
/*               PV=       pareto value                                     */
/*               AV=       funnel actual value                              */
/*               TV=       funnel target value                              */
/*               AUV=      funnel unformatted actual value                  */
/*               TUV=      funnel unformatted target value                  */
/*                                                                          */
/*                                                                          */
/*  Attribute Values :                                                      */
/*                                                                          */
/*               ftC      FT_COLOR                                          */
/*               ftG      FT_GRADIENT                                       */
/*               ftP      FT_PATTERN                                        */
/*               ftT      FT_TEXTURE                                        */
/*               grC      custom gradient                                   */
/*               gdD      GD_DOWN                                           */
/*               gdD45    GD_DIAGONAL_45                                    */
/*               gdD135   GD_DIAGONAL_135                                   */
/*               gdR      GD_RADIAL                                         */
/*               gdRi     GD_RIGHT                                          */
/*                                                                          */
/*                                                                          */
/*  Animation Attributes and Values on <graph> or <gaugeSet>                */
/*                                                                          */
/*               aD       animation down-color/alpha                        */
/*               aDC      animation type (NONE, AUTO, ON)                   */
/*               aDi      animation init effects (NONE, AUTO)               */
/*               aDu      animation duration (ms)                           */
/*               aI       animation indicators (NONE, ALL)                  */
/*               aU       animation up-color/alpha                          */
/*                                                                          */
/*                                                                          */
/*  Object ID's :    (see also class ObjType)                               */
/*               AR       AreaRiser                                         */
/*               ARB      AreaRiserBottom                                   */
/*               ART      AreaRiserTop                                      */
/*               BR       BarRiser                                          */
/*               BRB      BarRiserBottom                                    */
/*               BRS      BarRiserSide                                      */
/*               CB       ChartBackground                                   */
/*               CRF      CubeRiserFace                                     */
/*               GB       GaugeBackground                                   */
/*               GSB      GaugeSetBackground                                */
/*               LA       LegendArea                                        */
/*               LeM      LegendMarker                                      */
/*               LM       LineMarker                                        */
/*               LR       LineRiser                                         */
/*               LRS      LineRiserSide                                     */
/*               LRT      LineRiserTop                                      */
/*               LT       LegendText                                        */
/*               PA       PlotArea                                          */
/*               PFL      PieFeelerLine                                     */
/*               O1L      O1Label                                           */
/*               O2L      O2Label                                           */
/*               PGO      PieGlowOval   (obsolete)                          */
/*               PSL      PieSliceLabel                                     */
/*               PSLI     PieSliceLabel (internal)                          */
/*               Sl       Slice                                             */
/*               SC       SliceCrust                                        */
/*               SCT      StockCloseTick                                    */
/*               SOT      StockOpenTick                                     */
/*               SR       StockRiser                                        */
/*               X1L      X1Label                                           */
/*               X1MG     X1MajorGrid                                       */
/*               Y1L      Y1Label                                           */
/*               Y1MG     Y1MajorGrid                                       */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*  Notes:                                                                  */
/*                                                                          */
/*  1)  The following values are cached (that is, if they are missing, the  */
/*      value implied is that from the previous time it was specified),     */
/*  2)  For some attributes, an empty string means clear the cache.         */
/*                                                                          */
/*      a=       (text angle)                                               */
/*      c=       (line and polyline color/alpha)                            */
/*      dir= and p= (gradient)                                              */
/*      fid=     (font ID)                                                  */
/*      ft=      (fill type)                                                */
/*      fc=      (fill color/alpha)                                         */
/*      G=       (group ID)               // empty string means clear cache */
/*      go=      (mouse transparent)                                        */
/*      j=       (text alignment)                                           */
/*      mt=      (marker type)                                              */
/*      O=       (object ID)                                                */
/*      S=       (series ID)              // empty string means clear cache */
/*      sh=      (shadow)                 // empty string means clear cache */
/*      tt=      (on det object)                                            */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/**
  *  Parses graph xml into an object list (the det list). 
  *  @class DvtGraphParser
  *  @extends DvtObj
  *  @constructor
  *  @param  {context}  context  The platform dependent context.
  *  @param  {DvtGraphCore}  chart  The associated chart.
  *  @param  {DvtGraphAnimation}  anim  The animation object (maybe null).
  */
var  DvtGraphParser = function(context, chart, anim)
{
   this._Init(chart, context, anim) ;
};  

DvtObj.createSubclass(DvtGraphParser, DvtObj, "DvtGraphParser") ;

DvtGraphParser.NEW_LINE = "~@";

/**   @private  @final  */
DvtGraphParser.MD_GROUP_SEPARATOR     = "~;" ;    // <md  groups="nnn~;nnn~;.../>
/**   @private  @final  */
DvtGraphParser.MD_MACROVAL_SEPARATOR  = "~;" ;    // <md  XM="sss~;sss~;.../>
/**   @private  @final  */
DvtGraphParser.MD_D_SEPARATOR         = "~:" ;    // <md  d="nnn~;nnn~;.../>
/**   @private  @final  */
DvtGraphParser.MD_DATAVAL_SEPARATOR   = ","  ;    // <md  Y="sss,sss,.../>
/**   @private  @final  */
DvtGraphParser.MD_VF_SEPARATOR        = "~;" ;    // <md  YVF="sss~;sss~;.../>
/**   @private  @final  */
DvtGraphParser.MD_TT_SEPARATOR        = "~;" ;    // <m or lm  t="sss~;sss~;.../>
/**   @private  @final  */
DvtGraphParser.MD_SEL_SERIES_SEPARATOR  = "~;";   // <md sel="sss~:ggg;ggg;~:ggg;ggg;~;sss .../>
/**   @private  @final  */
DvtGraphParser.MD_SEL_GROUP_SEPARATOR   = "~:";   // <md sel="sss~:ggg;ggg;~:ggg;ggg;~;sss .../> 

/**   @private  @final */
DvtGraphParser.cATTR_X1IN         = "x1in" ;  // rangeslider zoom button zoom
/**   @private  @final */
DvtGraphParser.cATTR_X1OUT        = "x1out" ; //   event javascript
/**   @private  @final */
DvtGraphParser.cATTR_Y1IN         = "y1in" ;
/**   @private  @final */
DvtGraphParser.cATTR_Y1OUT        = "y1out" ;
/**   @private  @final */
DvtGraphParser.cATTR_Y2IN         = "y2in" ;
/**   @private  @final */
DvtGraphParser.cATTR_Y2OUT        = "y2out" ;
/**   @private  @final */
DvtGraphParser.cATTR_X1ACTUAL     = "x1actual" ;
/**   @private  @final */
DvtGraphParser.cATTR_Y1ACTUAL     = "y1actual" ;
/**   @private  @final */
DvtGraphParser.cATTR_Y2ACTUAL     = "y2actual" ;
/**   @private  @final */
DvtGraphParser.cATTR_ALLACTUAL    = "allactual" ;

//  Misc constants

/**   @private  @final */
DvtGraphParser.TRUE          = "t";
/**   @private  @final  */
DvtGraphParser.COMMA         = ',' ;
/**   @private  @final  */
DvtGraphParser.PLOTAREACLIP  = "plotAreaClip" ;
/**   @private  @final  */
DvtGraphParser._MIN_TOUCH_MARKER_SIZE = 16; // minimum marker size for touch devices


/**   @private   */
DvtGraphParser.prototype._Init = function(chart, context, anim)
{
   this._ctx                      = context ;
   this._Chart                    = chart ;
   this._anim                     = anim ;
   this._bBiDi                    = false ;
   this._type                     = null       // graph/gauge type {@link DvtGraphType}

   this._aRisers                  = null ;     // array of arrays of risers in series index order 
   this._aLegends                 = null ;     // array of legend objs in series index order 
   this._aMiscLegends             = null ;     // array of legend objs (with no series index).
   this._aHMarkers                = null ;     // array of arrays of hidden marker objs in series index order.
   this._Y1Axis                   = null ;     // Y1 axis
   this._Y2Axis                   = null ;     // Y2 axis
   this._XAxis                    = null ;     // X axis
   this._DataSummary              = null ;     // DvtDataSummary obj.
   this._Data                     = null ;     // DvtGraphData obj.
   this._aXGridLines              = null ;     // array of x grid lines
   this._aY1GridLines             = null ;     // array of y1 grid lines
   this._aY2GridLines             = null ;     // array of y2 grid lines
   this._FrameSep                 = null ;     // frame separator in split charts
   this._aXLabels                 = null ;     // array of x-labels
   this._aY1Labels                = null ;     // array of y1-labels
   this._aY2Labels                = null ;     // array of y2-labels

   this._3DBars                   = null ;     // array of arrays for 3D bar surfaces
   this._a3DCurBar                = null ;     // array of current 3D bar faces being assembled
   this._3DBarCurRiser            = null ;     // the actual 3d bar detlist object
   this._iCurBarSeries            = -1 ;       // series of current 3d bar
   this._iCurBarGroup            = -1 ;        // group of current 3d bar

   this._pieChart                 = null ;     // DvtPieChart - current pie being assembled.

    //  For referenceObject parsing and linkage

   this._aRefFrom                 = null ;     // det objects and more, referred from
   this._aRefTo                   = null ;     // det objects referred to
   this._bNeedRefObjHandler       = false ;    // true = need ref obj event handler

   //  For clip rect resolution.

   this._aForClipResolution       = null ;     // entry pairs : clipID and DetObj
   this._bHavePlotArea            = false ;    // true if we have found the plot area

   //  For Hide/Show Series resolution

   this._HSS                      = null ;     // direct string from the xml.

   //  Cached values

   this._cacheObjId               = null ;
   this._cacheSeriesId            = null ;
   this._cacheSeriesIndex         = null ;
   this._cacheGroupId             = null ;

   this._cacheFillType            = null ;
   this._cacheBorderColor         = "#000000" ;
   this._cacheBorderAlpha         = 1 ;
   this._cacheColor               = null ;
   this._cacheColorAlpha          = 1 ;
   this._cacheFillColor           = "#000000" ;
   this._cacheFillAlpha           = 1 ;

   this._cacheGradStops           = null ;
   this._cacheGradDir             = "gdRi" ;
   this._cacheGradAngle           = null ;        // ang=
   this._cacheGradCustom          = null ;        // custom gradient direction (pts=)
   this._cacheShadow              = null ;        // cached string
   this._cacheShadowObj           = null ;        // cached obj
   this._cacheMarkerSizes         = null ;
   this._cacheMarkerType          = null ;        // see DvtMarker class
   this._bCacheMouseTransparency  = false ;
   this._bCacheUseTooltipTemplate = false ;
   this._cacheTextAngle           = 0 ;
   this._cacheTextJustify         = 'l' ;

   this._workRect                 = new DvtRectangle() ; // used to avoid multi obj creation

   this._bCacheUseTooltipTemplate = null ;
   this._sTooltipTemplate         = null ;        // tooltip template parsed from metadata

   // Stroke and fill properties are parsed into the following work vars:

   this._workC                    = null ;             // color
   this._workCA                   = null ;             //  and alpha
   this._workFC                   = null ;             // fill color
   this._workFA                   = null ;             //  and alpha
   this._workBC                   = null ;             // border color
   this._workBA                   = null ;             //  and alpha
   this._workLW                   = null ;             // line width
   this._workLS                   = DvtStroke.SOLID ;  // line style
   this._workDash                 = null ;             // dash/dot length(s) if line style = dashed
   this._workImgURL               = null ;             // image URL
   this._workImgDsplyMode         = null ;             // image display mode

   //  Pie

   this._pieLabelType             = null ;
   this._pieFeelerColor           = null ;
   this._pieLabelPos              = null ;
   this._pieFrameX                = 0 ;
   this._pieFrameY                = 0 ;
   this._pieFrameW                = 0 ;
   this._pieFrameH                = 0 ;
   this._bPieAutoExplode          = false ;
   this._arPieSliceLabels         = null ;
   this._pieSliceLabelVF          = null ;
   this._pieExplodeAll            = true ;

   this._initSelections           = null ;
   this._selectJS                 = null ;

   /** Macros
     * @private
     */
   this._arMacros = ["S",       "G",                                //  0 - 1
                     "X",       "Y",       "Z",                     //  2 - 4
                     "XM",      "YM",      "ZM",                    //  5 - 7
                     "CUMSTKV", "CUMPCTV", "PIEPCTV",               //  8 - 10
                     "HV",      "LV",      "OV",     "CV",  "VV",   // 11 - 15
                     "PV",      "AV",      "TV",                    // 16 - 18
                     "ON",      "OD",      "OID",    "OIN",         // 19 - 22
                     "OM",      "LM",      "HM",     "CM",           // 23 - 26
                     "AUV",     "TUV"] ;                            //27-28

   this.ATTR_XAVF   = "XAVF"  ;   // X-axis  value format (for scrollbar)
   this.ATTR_YAVF   = "YAVF"  ;   // Y-axis  value format (for scrollbar)
   this.ATTR_Y2AVF  = "Y2AVF" ;   // Y2-axis value format (for scrollbar)

   this._iSIndex  =  0 ;    // index to S entry
   this._iGIndex  =  1 ;    // index to G entry
   this._iXIndex  =  2 ;    // index to X entry  (note same as DvtGraphData.DV_X)
   this._iYIndex  =  3 ;    // index to Y entry  ( ..   ..  .. DvtGraphData.DV_Y)
   this._iZIndex  =  4 ;    // index to Z entry  ( ..   ..  .. DvtGraphData.DV_Z)

   this.VF        = "VF" ;  // suffix to add to X,Y, or Z for value format attribute.
   this.XL        = "XL" ;  // XL = alternate [X] tooltip macro replacement strings.
   this.YL        = "YL" ;  // YL = alternate [Y] tooltip macro replacement strings.
   this.ZL        = "ZL" ;  // ZL = alternate [Z] tooltip macro replacement strings.

   //  Misc. constants

   this.RADS_PER_DEGREE = (Math.PI / 180) ;
   this.DEGREES_PER_RAD = (180 / Math.PI) ;

   this.LINEMARKER_HALFSIZE =  3 ;   // Line markers are currently 6px square

   this._invisFill = DvtSolidFill.invisibleFill();  // invisible tooltip marker fill

   //support for selection - map of related det objects of stock marker
   this._stockMarkersMap = null;
   //support for selection - map of related faces of 3D pie/ring slices
   this._pie3DFacesMap = null;
   
   //BUG FIX #12427741: array of objects and their shadows; apply shadows
   //after the objects have been added to the display list
   this._shadowMap = null;
   this._bevelMap = null;

   this._linkColor  = '#0000ff' ;    // text link color
   
   this._currentHandleId = null;
   
   this._tempLayers = null;
} ;



/*---------------------------------------------------------------------*/
/*   _addHiddenMarker()                                                */
/*---------------------------------------------------------------------*/
/**
  *   Adds a hidden marker object (for tooltips) into a series indexed array
  *   of arrays.
  *   @private
  */
DvtGraphParser.prototype._addHiddenMarker = function(obj)
{
   if (! this._aHMarkers) {
     this._aHMarkers = new Array(this._Chart.getSeriesCount()) ;
   }

   var sidx = obj._Dlo._seriesIdx ;
   if (! this._aHMarkers[sidx]) {
     this._aHMarkers[sidx] = [] ;
   }
   this._aHMarkers[sidx].push(obj) ;
};



/*---------------------------------------------------------------------*/
/*   _addLegend()                                                      */
/*---------------------------------------------------------------------*/
/**
  *   Adds a legend marker or text object into a series indexed array of arrays.
  *   @private
  */
DvtGraphParser.prototype._addLegend = function(obj)
{
   var dlo = obj._Dlo ;

   if (! this._aLegends) {
     this._aLegends = [] ;
   }

   var sidx = dlo.getSeriesIndex() ;

   if (sidx >= 0) {

     if (! this._aLegends[sidx]) {
       this._aLegends[sidx] = [] ;
     }

     var id  = dlo.getObjId() ;
     var idx = (id === DvtGraphObjType.LEGEND_MARKER? 0 : 1) ;
     this._aLegends[sidx][idx] = obj ;
   }
   else {
     if (! this._aMiscLegends) {
       this._aMiscLegends = [] ;
     }
     this._aMiscLegends.push(obj) ;
   }

};



/*---------------------------------------------------------------------*/
/*   _addMarker()                                                      */
/*---------------------------------------------------------------------*/
/**
  *   Adds a marker object into a line or area riser's associated marker array.
  *   @private
  */
DvtGraphParser.prototype._addMarker = function(obj)
{
   var sidx = obj._Dlo._seriesIdx ;
   
   if (this._aRisers) {
     var aSeries = this._aRisers[sidx] ;
     if (aSeries) {
       var len = aSeries.length;
       for (var i = 0; i < len; i++) {
          var riser  = aSeries[i];
          var objId = riser._Dlo._objId ;
          if ((objId === DvtGraphObjType.LINE_RISER) ||
              (objId === DvtGraphObjType.AREA_RISER) ||
              (objId === DvtGraphObjType.LINE_RISER_TOP)) {

            if (! riser._aLMarkers) {       // insert a marker
              riser._aLMarkers = [] ;       // property array
            }
            riser._aLMarkers.push(obj) ;
          }
       }
     }
   }
};


/*---------------------------------------------------------------------*/
/*   _addRiser()                                                       */
/*---------------------------------------------------------------------*/
/**
  *   Adds a riser object into a series indexed array of arrays.
  *   @private
  */
DvtGraphParser.prototype._addRiser = function(obj)
{
   if (! this._aRisers) {
     this._aRisers = new Array(this._Chart.getSeriesCount()) ;
   }

   var sidx = obj._Dlo._seriesIdx ;
   if (! this._aRisers[sidx]) {
     this._aRisers[sidx] = [] ;
   }
   this._aRisers[sidx].push(obj) ;
};


/*---------------------------------------------------------------------*/
/*    _addToReferredFrom()                                             */
/*---------------------------------------------------------------------*/
/**
  *   @private
  */
DvtGraphParser.prototype._addToReferredFrom = function(obj, sID)
{
   if (! this._aRefFrom)
     this._aRefFrom = [] ;

   this._aRefFrom.push(obj) ;
   this._aRefFrom.push(sID) ;
};



/*---------------------------------------------------------------------*/
/*    _addToReferredTo()                                               */
/*---------------------------------------------------------------------*/
/**
  *   @private
  */
DvtGraphParser.prototype._addToReferredTo = function(obj)
{
   if (! this._aRefTo)
     this._aRefTo = [] ;

   this._aRefTo.push(obj) ;
};


/*---------------------------------------------------------------------*/
/*   _buildHSSeriesState()                                             */
/*---------------------------------------------------------------------*/
/**
  *   Creates a boolean array and count of true values from the initial string
  *   string in the xml and passes them to the chart.
  *   @private
  */
DvtGraphParser.prototype._buildHSSeriesState = function()
{
   if (!this._HSS) {
     return ;
   }
   
   var split = this._HSS.split(";");
   var hss = split[0];
   var hsLayers = split.slice(1, split.length);

   //  hss  is a string like "ffftfttf"  where t means series is hidden
   //  and f means visible. Internally we will flip this and maintain an
   //  array where true = visible. 

   //  Create the series state array in series index order
   var arSeriesState   = new Array(this._Chart.getSeriesCount()) ;
   var len       = arSeriesState.length ;
   var nShowing  = 0 ;
   var i ;

   if (hss.length === 0) {
     for (i = 0; i < len; i++) {
        arSeriesState[i] = true ;
        nShowing++ ;
     }
   }
   else {
     var sLen = hss.length ;
     if (sLen > len) {
       sLen = len ;
     }
     for (i = 0; i < sLen; i++) {
        if (arSeriesState[i] = (hss.charAt(i) === 'f')) {
          nShowing++ ;
        }
     }
     if (sLen < len) {
       for (; i < len; i++) {
        arSeriesState[i] = true ;
        nShowing++ ;
       }
     }
   }

  this._Chart.setHSSState(arSeriesState, nShowing) ;

  if (hsLayers[0]) {
    var hiddenMiscIds = [];
    var miscId;
    for (var i=0; i<hsLayers.length; i++) {
      miscId = DvtArrayUtils.getIndex(this._tempLayers, hsLayers[i]);
      if (miscId != -1)
        hiddenMiscIds.push(miscId);
    }
    this._Chart.setHSLegendMiscId(hiddenMiscIds);
  }
};



/*---------------------------------------------------------------------*/
/*  _checkTooltipTemplate()                                            */
/*---------------------------------------------------------------------*/
/**
  *  Checks the tooltip template attribute and sets the "use tooltip template"
  *  cached value this._bCacheUseTooltipTemplate.
  *  @private
  */
DvtGraphParser.prototype._checkTooltipTemplate = function(node)
{
  // tt="t" use template, tt="f" don't use template

  var  s  = node.getAttribute("tt") ;

  if (s !== null && s.length > 0) {
    this._bCacheUseTooltipTemplate = ( (s.charAt(0) === DvtGraphParser.TRUE) && this._Data.hasTooltipTemplate() ) ;  // cache it
  }

}  ;


/*---------------------------------------------------------------------*/
/*   _computeCustomAngleAndStops()                                     */
/*---------------------------------------------------------------------*/
/**  Returns the angle in degrees of the line joining the two specified points,
  *  and adjusts the stop ratios based on the extension of the line to intersect
  *  with the supplied bounding rectangle.
  *  @param {String} pts       the pts attribute "x1,y1,x2,y2".
  *  @param {Array}  arBounds  the boundary rectangle
  *  @param {array}  arRatios  the stops values.
  *  @returns {number} the angle in degrees of the line joining the two specified
  *  points, and adjusts the supplied stop ratios based on the extension of the
  *  line to intersect with the supplied bounding rectangle.
  */
DvtGraphParser.prototype._computeCustomAngleAndStops = function(pts, arBounds, arRatios)
{
   var xBound = arBounds[0] ;
   var yBound = arBounds[1] ;
   var wBound = arBounds[2] ;
   var hBound = arBounds[3] ;

   // Console.log("pts=\"" + pts + "\" xBound=" + xBound + " yBound=" + yBound +
   //                    " wBound=" + wBound + " hBound=" + hBound) ;

   //  Parse the pts= attribute into (x1,y1) (x2,y2).  These represent the
   //  coords of points on the line d (see diagram below).

   var  x1 ;
   var  y1 ;
   var  x2 ;
   var  y2 ;
   var  i, i2 ;

   i  = pts.indexOf(',') ;                  // pts -> "x1,y1,x2,y2"
   x1 = parseInt(pts.substring(0, i)) ;
   i2 = pts.indexOf(',', ++i) ;
   y1 = parseInt(pts.substring(i, i2)) ;
   i  = pts.indexOf(',', ++i2) ;
   x2 = parseInt(pts.substring(i2, i)) ;
   y2 = parseInt(pts.substring(++i)) ;

   var angle ;
   if (x1 !== x2) {
     angle = ((Math.atan2(y2 - y1, x2 - x1)) * this.DEGREES_PER_RAD) ;
   }
   else {
     angle = (y1 >= y2)? 90 : -90 ;
   }

   //----------------------------------------------------------------------------//
   //  Now need to adjust the stop ratios based on the custom gradient points    //
   //  and the bounding rect.                                                    //
   //                                                                            //
   //  line1                      A                                              //
   //  +-------------------------/--------*    1st stop =  (stop1 * d ) + d1     //
   //  |                       /          |                 _______________      //
   //  |l                    *  (x2,y2)   |l                      AB             //
   //  |i                  /              |i                                     //
   //  |n                /                |n                                     //
   //  |e       line d /                  |e                                     //
   //  |3            /                    |4   2nd stop =  (stop2 * d ) + d1     //
   //  |           /                      |                 _______________      //
   //  |         /                        |                        AB            //
   //  |       *  (x1,y1)                 |                                      //
   //  |     d1                           |                                      //
   //  +---/------------------------------+                                      //
   //     A     line2     bounding rect        nth stop =  (stopn * d ) + d1     //
   //                                                       _______________      //
   //                                                             AB             //
   //                                                                            //
   //   d  =  length of the line bewteen points (x1,y1) and (x2,y2).             //
   //   d1 =  distance from (x1,y1) to the intersection on the boundary rect.    //
   //   AB =  distance between the two points of intersection with the bounding  //
   //         bounding rectangle.                                                //
   //----------------------------------------------------------------------------//

   //  Extend (x1,y1) and (x2,y2) to intersect with boundary rectangle, and find
   //  thses points of intersection.  The points will be called A and B  (it does
   //  not matter which is A and which is B).

   var pt1  = new DvtPoint(x1, y1) ;
   var pt2  = new DvtPoint(x2, y2) ;
   var pt3  = new DvtPoint(xBound, yBound) ;
   var pt4  = new DvtPoint(xBound + wBound, yBound) ;
   var ptIntersectLine1 = DvtLineUtils.intersect2Lines(pt1, pt2, pt3, pt4) ;

   pt3.y  = yBound + hBound ;
   pt4.x  = xBound + wBound ;
   pt4.y  = pt3.y ;
   // pt1 = (x1,y1), pt2 = (x2, y2), pt3 = (xBound, yBound + hBound), pt4 = (xBound + wBound, yBound + hBound)
   var ptIntersectLine2 = DvtLineUtils.intersect2Lines(pt1, pt2, pt3, pt4) ;

   pt3.y  = yBound ;
   pt4.x  = xBound ;
   // pt1 = (x1,y1), pt2 = (x2, y2), pt3 = (xBound, yBound), pt4 = (xBound, yBound + hBound)
   var ptIntersectLine3 = DvtLineUtils.intersect2Lines(pt1, pt2, pt3, pt4) ;

   pt3.x  =  xBound + wBound ;
   pt4.x  =  pt3.x ;
   // pt1 = (x1,y1), pt2 = (x2, y2), pt3 = (xBound + wBound, yBound), pt4 = (xBound + wBound, yBound + hBound)
   var ptIntersectLine4 = DvtLineUtils.intersect2Lines(pt1, pt2, pt3, pt4) ;

   //Console.log("isect1=" + (ptIntersectLine1 == null ? "none" : ("(" + ptIntersectLine1.x + "," + ptIntersectLine1.y + ")"))) ;
   //Console.log("isect2=" + (ptIntersectLine2 == null ? "none" : ("(" + ptIntersectLine2.x + "," + ptIntersectLine2.y + ")"))) ;
   //Console.log("isect3=" + (ptIntersectLine3 == null ? "none" : ("(" + ptIntersectLine3.x + "," + ptIntersectLine3.y + ")"))) ;
   //console.log("isect4=" + (ptIntersectLine4 == null ? "none" : ("(" + ptIntersectLine4.x + "," + ptIntersectLine4.y+ ")"))) ;

   var  d  ;     // lenth of line bewteen (x1,y1) and (x2,y2)
   var  d1 ;     // lenth of line bewteen (x1,y1) and its extension to a boundary line
   var  AB ;     // length of line bewteen the two boundary intersection points
   var  Ax ;     // intersection with a boundary line
   var  Ay ;
   var  Bx ;     // intersection with a boundary line
   var  By ;

   //  Find points of intesection with bounding rect

   var bHaveCoord = false ;

   if (ptIntersectLine1 !== null) {
      bHaveCoord = true ;
      Ax = ptIntersectLine1.x ;
      Ay = ptIntersectLine1.y ;
   }
   if (ptIntersectLine2 !== null) {
      if (! bHaveCoord) {
        bHaveCoord = true ;
        Ax = ptIntersectLine2.x ;
        Ay = ptIntersectLine2.y ;
      }
      else  {
        Bx = ptIntersectLine2.x ;
        By = ptIntersectLine2.y ;
      }
   }
   if (ptIntersectLine3 !== null) {
      if (! bHaveCoord) {
        bHaveCoord = true ;
        Ax = ptIntersectLine3.x ;
        Ay = ptIntersectLine3.y ;
      }
      else  {
        Bx = ptIntersectLine3.x ;
        By = ptIntersectLine3.y ;
      }
   }
   if (ptIntersectLine4 !== null)  {
      if (! bHaveCoord)  {
        Ax = ptIntersectLine4.x ;
        Ay = ptIntersectLine4.y ;
      }
      else  {
        Ax = ptIntersectLine4.x ;
        Ay = ptIntersectLine4.y ;
      }
   }


   //  Get lengths d, AB and dl

   d  = DvtLineUtils.lineLen(x1, y1, x2, y2) ;
   AB = DvtLineUtils.lineLen(Ax, Ay, Bx, By) ;

   var v1 = DvtLineUtils.lineLen(x1, y1, Ax, Ay) ;
   var v2 = DvtLineUtils.lineLen(x2, y2, Ax, Ay) ;
   d1 =  (v1 < v2)?  v1 : DvtLineUtils.lineLen(x1, y1, Bx, By) ;

   //Console.log("x1,y1 = (" + x1 + ", " + y1 + ")  (" + x2 + ", " + y2 + ")") ;
   //Console.log("Ax,Ay = (" + Ax + "," + Ay + ") Bx,By = (" + Bx + "," + By + ")") ;
   //Console.log("d1 = " + d1 + "  d = " + d + "  AB = " + AB) ;


   //  Now adjust the supplied stop ratio's

   for (i = 0; i < arRatios.length; i++)  {
      var  ratio = arRatios[i] ;

      //Console.log("old ratio (" + i + ") = " + arRatios[i] + " (" + ratio + ")") ;

      ratio = (((ratio * d) + d1)/AB) ;

      if (ratio > 1) {                               // extra bulletproofing.
        ratio = 1 ;                                  // calcs have gone wrong, and say
      }
      else if (ratio < 0) {                          // ratio > 255, flash does not render
        ratio = 0 ;                                  // any of the gradients.
      }
      arRatios[i] = ratio ;

      //Console.log("new ratio (" + i + ") = " + arRatios[i]  + " (" + arRatios[i]/255 + ")") ;
   }

   //Console.log("returning angle=" + angle) ;

   return angle ;
} ;



/*---------------------------------------------------------------------*/
/*   _findRisersForSeries()                                            */
/*---------------------------------------------------------------------*/
/**
  *   Returns an array of riser object for a given series index.
  *   @private
  */
DvtGraphParser.prototype._findRisersForSeries = function(sidx)
{
   var ar = null ;

   if (this._aRisers) {
     if (this._aRisers[sidx]) {
       ar = this._aRisers[sidx] ;
     }
   }

   return ar ;
};

/*---------------------------------------------------------------------*/
/*   parse()      Parse the supplied XML into an array of renderable   */
/*                objects.                                             */
/*---------------------------------------------------------------------*/
/**
  *  Parses the supplied XML into an array of displayable objects in a detlist.
  *  @param {String}  sXML
  *  @param {DvtGraphAnimation}  anim  a DvtGraphAnimation object (may be null).
  *  @type Array
  */
DvtGraphParser.prototype.parse = function(sXML)
{
   if (! sXML) {
     return ;
   }

   var nRootVer = 0 ;                            // get root version
   var sHead = sXML.substring(0, 40) ; 
   var j  = sHead.indexOf('version=') ;
   if (j >= 0) {
     nRootVer = parseFloat(sHead.substr(j +9)) ; // parse out the version number
   }

   j = sHead.indexOf('bidi=') ;                  // optional bidi="t" ?
   if (j >= 0) {
     this._bBiDi = (sHead.charAt(j+6) === DvtGraphParser.TRUE) ;
     this._Chart.setBiDi(this._bBiDi) ;
   }

   // TODO: The following lines look like they deal with whitespace parsing differences in the DOMParser, which would
   //       not exist after switching to the DvtXMLParser.  Remove these lines when the switch is done.
   /*sXML = sXML.replace(/[\n\r]/gi,'~;') ; // TDO temp until we figure out whats going on with the DOMParser
   sXML = sXML.replace(/>~;/gi,'>') ;     // TDO temp until we figure out whats going on with the DOMParser*/
   sXML = sXML.replace(/>[\n\r]/gi,'>') ; // Workaround for DOMParser, remove all whitespace between or after elements
   
   // Re-encode new line characters, since they are not returned by the XML parser
   sXML = sXML.replace(/[\n\r]+/gi, DvtGraphParser.NEW_LINE) ; 
   if (DvtGraphParser.NEW_LINE == sXML.substring(sXML.length-2))
     sXML = sXML.substring(0,sXML.length-2);
       
   var bGauge    = false ;
   var objList   = null ;
   var domParser = new DOMParser();
   var xmlDoc    = domParser.parseFromString(sXML, 'text/xml');

   var nodeGraph = xmlDoc.getElementsByTagName("graph") ;

   if ((! nodeGraph) || (nodeGraph.length !== 1)) {
     nodeGraph = xmlDoc.getElementsByTagName("gaugeSet") ;
     if ((! nodeGraph) || (nodeGraph.length !== 1)) {
       return false ;
     }
     else {
       bGauge = true ;
     }
   }

   this._DataSummary = new DvtDataSummary(this._Chart) ;


   /*-----------------------*/
   /*     Begin Parsing     */
   /*-----------------------*/

   var attr ; 
   var nodes  ;
   var node = nodeGraph[0] ;

   //  Parse common graph/gauge attributes

   this._type          = node.getAttribute(bGauge? "gaugeType" : "graphType") ;
   this._Chart._type   = this._type;

   // selection - create new map of related det objects
   this._stockMarkersMap = {};
   this._pie3DFacesMap   = {};

   this._parseGraphTag(node, bGauge) ;
   this._parseAnimationAttributes(node, this._Chart) ;

    if (this._Chart.isHorizontal()) {
        this._Chart.setDataCursor(new DvtDataCursor(this._ctx, true));
    } else {
        this._Chart.setDataCursor(new DvtDataCursor(this._ctx, false));
    }

   //  Parse all children of <graph> / <gaugeset>

   if (node.hasChildNodes())  {
     nodes  = node.childNodes ;
     if (nodes)  {
        objList = this._parseNodes(nodes) ;
     }
   }
   
   // TODO: move to xml part when these features are in xml
   this._parseResources();
   
   /*---------------------------*/
   /*  Final detlist processing */
   /*---------------------------*/

   this._buildHSSeriesState();           // create usable array and pass to chart
   this._resolveRefObjLinkage() ;        // link referencing and referenced objects
   this._resolveClipRects() ;            // late bind to clip rects

   this._Chart.setDataSummary(this._DataSummary) ;
   this._Chart.setRisers(this._aRisers) ;
   this._Chart.setLegends(this._aLegends) ;
   this._Chart.setMiscLegends(this._aMiscLegends) ;
   this._Chart.setHiddenMarkers(this._aHMarkers) ;

   if (this._XAxis) {
     this._Chart.setXAxis(new DvtGraphAxis(this._Chart, this._XAxis, this._aXGridLines, this._aXLabels)) ;
   }
   if (this._XAxis) {
     this._Chart.setY1Axis(new DvtGraphAxis(this._Chart, this._Y1Axis, this._aY1GridLines, this._aY1Labels)) ;
   }
   if (this._XAxis) {
     this._Chart.setY2Axis(new DvtGraphAxis(this._Chart, this._Y2Axis, this._aY2GridLines, this._aY2Labels)) ;
   }

   // Support for selection - link related det objects together

   this._resolveRelatedStockMarkers();
   this._resolveRelatedPie3DFaces();
   
   // TODO: byoshimo remove when storage location decided on or chart API created
   this._Chart._stockMarkersMap = this._stockMarkersMap;

   // Support for selection - clear the map of related det objects

   this._stockMarkersMap = null;
   this._pie3DFacesMap   = null;
   
   //BUG FIX #12427741: transfer the shadow map to the chart to apply them
   //after the objects are added to the display list
   this._Chart._shadowMap = this._shadowMap;
   this._Chart._bevelMap = this._bevelMap;
   this._shadowMap = null;
   this._bevelMap = null;

   // set initial selections
   if (this._initSelections != null) {
     // The string comes in the form:
     // "seriesId~:groupId~;seriesId2~:groupId2 . ."            1 group per series
     // or
     // "seriesId~:groupId~:groupId;seriesId2~:groupId2 . ."    multiple groups per series
     var selectedIds = [];
     var selections = this._initSelections.split(DvtGraphParser.MD_SEL_SERIES_SEPARATOR);
     for (i=0; i<selections.length; i++) {
         var selection = selections[i];
         var ar = selection.split(DvtGraphParser.MD_SEL_GROUP_SEPARATOR);
         var groups = ar.slice(1);
         for (j=0; j<groups.length; j++) {
             selectedId = ar[0] + "~:" + groups[j]; //ar[0] is the Series ID
             selectedIds.push(selectedId);
         }
     }
     this._Chart.setSelectedIds(selectedIds);
   }

   return objList ;       // returns an array of renderable objects

} ;   // end parse()



/*---------------------------------------------------------------------*/
/*  _parseContextMenu()                                                */
/*---------------------------------------------------------------------*/
/**
 * Parses a context menu definition.
 */
DvtGraphParser.prototype._parseContextMenu = function(node) {
  // Add this definition to the chart's context menu handler
  var handler = this._Chart.__getContextMenuHandler();
  handler.add(new DvtDomXmlNode(node));
};


/*-------------------------------------------------------------------*/
/*   _parseDatatipFormat()                                           */
/*-------------------------------------------------------------------*/
/**
  *   @private
  */
DvtGraphParser.prototype._parseDatatipFormat = function(node)
{
  //  TDO 
  var ttipColor = node.getAttribute('fc');
  var ttipFill = new DvtSolidFill(ttipColor);
  this._Chart.setDatatipFill(ttipFill);
};


/*---------------------------------------------------------------------*/
/*  _parseListNode()                                                   */
/*---------------------------------------------------------------------*/
/**
  *   @private
  */
DvtGraphParser.prototype._parseListNode = function(node) {

    var obj = null;

    if (node === null)
        return null;

    var objId = node.getAttribute("O") ;     // Object Id

    if (objId !== null) {
        this._cacheObjId = objId ;
    }
    else {
        objId = this._cacheObjId ;
    }

    var elem = node.nodeName ;

    if (elem === 'l') {
        obj = this._parseLine(objId, node) ;
    }
    else if (elem === 'r') {
        if (objId === DvtGraphObjType.TIME_SELECTOR) {
          obj = this._parseTimeSelector(objId, node) ;
        }
        else {
          obj = this._parseRect(objId, node) ;
        }
    }
    else if (elem === 't') {
        obj = this._parseText(objId, node) ;
    }
    else if (elem === 'm') {
        obj = this._parseMarker(objId, node) ;
    }
    else if (elem === 'pg')  {
        obj = this._parsePolygon(objId, node) ;
    }
    else if (elem === 'pl')  {
        obj = this._parsePolyline(objId, node) ;
    }
    else if (elem === 'p')  {
        obj = this._parsePath(objId, node) ;
    }
    else if (elem === 'c' || elem == 'o')  {
    obj = this._parseEllipse(objId, node) ;
    }
    else if (elem === 'f')  {
        obj = this._parseFont(node) ;
    this._Chart.addFont(obj) ;
        obj = null ;
    }
    else if (elem === 'lm')  {
        this._parseLineMarker(objId, node) ;
    }
    else if (elem === "sl") {                  // 2D/3D pie slice
        this._parsePieSlice(objId, node) ;
    }
    else if (elem === 'md')   {
        this._parseMetadata(node);
    }
    else if (elem === 'data')  {
        this._parseData(node) ;
    }
    else if (elem == "pie")  {                 // 2D/3D pie
        obj = this._parsePie(objId, node) ;
    this._pieChart = obj;
    }
    else if (elem === "ttf")  {    // tooltip formatting
        this._parseTooltipFormat(node);
    }
    else if (elem === "dtf")  {    // tooltip formatting
        this._parseDatatipFormat(node);
    }
    else if (elem === 'clipPath') {
        this._parseClipPath(node) ;
    }
    else if (elem === "menus") {
        this._parseContextMenu(node);
    }
    else if (elem === 'rs') {
        obj = this._parseRangeSlider(objId, node);
    }
     else if (elem === 'rszb')                  // range slider zoom button
    {
        this._parseRangeSliderZoomButton(node) ;
    }
    else if (elem === 'h') {
        obj = this._parseHandle(objId, node);
    }
    else if (elem === "spb") {
        this._parseShowPopupBehavior(node);
    }
    else if (elem === 'scrollableLegend') {
       // TODO: generic scrollable legend does not have a graph object type set
       objId = DvtGraphObjType.SCROLLABLE_LEGEND;
       obj = this._parseScrollableLegend(objId, node);
    }
    else if (elem === 'img') {
        obj = this._parseImage(objId, node);
    }
    else if (elem === "dataCursor") {
        this._parseDataCursor(node);
    }
    else if (elem === "magnifyLens") {
        this._parseMagnifyLens(node);
    }

    else if (elem === "markerDef") {
        this._parseMarkerDef(new DvtDomXmlNode(node));
    }

    return obj;
};


/*---------------------------------------------------------------------*/
/*   _parseNodes()         Parse all elements of <graph>               */
/*---------------------------------------------------------------------*/
/**  Parse all elements of a &lt;graph&gt; or &lt;gaugeset&gt; tag.
  *  @private
  *  @returns {Array}  An array of {@link DvtObj} objects.
  */
DvtGraphParser.prototype._parseNodes = function(nodes)
{
   var  objList ;
   var  obj ;
   var  len = nodes.length ;
   var  i ;
   var  ar ;

   if (len === 0) {return null ;}

   this._parseData(null) ;      // create a temp (empty) Data object

   objList = [] ;               // create the detlist

   for (i = 0; i < len; i++)
   {
     var node = nodes[i] ;
     if (node === null || node.nodeName === '#text') {
       continue ;
     }

     obj = this._parseListNode(node);

     //  Update the object list with the created objects.

     if (obj !== null) {
        if (obj instanceof Array) {
           ar = obj ;
           var  arLen = ar.length ;
           for (var j = 0; j < arLen; j++) {
              obj = ar[j] ;
              objList.push(obj) ;
           }
        }
        else 
          objList.push(obj) ;
     }
   }         // end while nodes

   return objList ;
} ;



/*---------------------------------------------------------------------*/
/*  _parseAnimationAttributes()                                        */
/*---------------------------------------------------------------------*/
/**
  *  Parses the xml animation attributes into the supplied DvtGraphAnimation
  *  object and chains it to the DvtGraphCore object.
  *  @private
  *  @returns {boolean} true if animation is to be preformed, else false.
  */
DvtGraphParser.prototype._parseAnimationAttributes = function(node, chart)
{
  var  anim = this._anim ;
  if (! anim) {
    return false ;
  }

  if ((anim.animOnDataChange === DvtGraphAnimation.ADS) ||
      (anim.animOnDataChange === DvtGraphAnimation.PPR))    { 
    anim.animOnDataChange = DvtGraphAnimation.AUTO;     // in case aDC is not set.
  }
  s = node.getAttribute('aDC') ;        // data change - ("auto", "on", "none" 
  if (s) {
    if (s == 'on') {
     s = DvtGraphAnimation.AUTO ;      // on and auto are synonmous
    }
    anim.animOnDataChange = s;
  }
    
  if (anim.animOnDisplay === DvtGraphAnimation.DISPLAY) {   // is graph allowing initFx?
    s = node.getAttribute('aDi') ;
    if (s) {
      anim.animOnDisplay = s;
    }
  }
  if (anim.animOnDisplay === DvtGraphAnimation.DISPLAY) {   // if not changed,
    anim.animOnDisplay = DvtGraphAnimation.NONE ;           // definitely turn it off.
  }

  if ((anim.animOnDataChange === DvtGraphAnimation.NONE) &&
      (anim.animOnDisplay    === DvtGraphAnimation.NONE)) {
    return false ;
  }

  var s = node.getAttribute('aDu') ;    // animation duration (ms)
  var duration = 1000;                  // default if not specified

  if (s !== null) {
    duration = parseInt(s,10) ;
  }


  if (duration === 0) {
    anim.animOnDataChange  = DvtGraphAnimation.NONE ;   // zero animation duration
    anim.animOnDisplay     = DvtGraphAnimation.NONE ;   // is same as no animation
    return true ;
  }
  anim.dataChangeDuration  = duration ;
  anim.displayDuration     = duration ;


  s = node.getAttribute('aI') ;                // animation indicators
  if (s !== null && s === DvtGraphAnimation.NONE) {
    anim.indicators = DvtGraphAnimation.NONE ;
  }

  s = node.getAttribute('aU') ;                // animation indicator up-color/alpha
  if (s) {
    anim.upColor = DvtColorUtils.getPound(s) ;
    anim.upAlpha = DvtColorUtils.getAlpha(s) ;
  }

  s = node.getAttribute('aD') ;                // animation indicator down-color/alpha
  if (s) {
    anim.downColor = DvtColorUtils.getPound(s) ;
    anim.downAlpha = DvtColorUtils.getAlpha(s) ;
  }

  s = node.getAttribute('aT') ;                // animation twinkle color/alpha
  if (s) {
    anim.twinkleColor = DvtColorUtils.getPound(s) ;
    anim.twinkleAlpha = DvtColorUtils.getAlpha(s) ;
  }

  return true ;
} ;


/*---------------------------------------------------------------------*/
/*    _parseBaseAttribs()                                              */
/*---------------------------------------------------------------------*/
/**
  *   Parses the common attributes of the xml elements.
  *   @private
  */
DvtGraphParser.prototype._parseBaseAttribs = function(obj, node, objId, bUseSA)
{
   var  dlo = obj._Dlo ;               // get the DvtDetObj
   var  n ;

   if (bUseSA === undefined) {        // check optional shape attributes boolean
     bUseSA = true ;                  // default if omitted.
   }

   this._parseGroupIndex(node) ;
   dlo.setGroupIndex(this._cacheGroupIndex) ;
   dlo.setGroupId(this._cacheGroupId) ;

   this._parseSeriesIndex(node) ;
   dlo.setSeriesIndex(this._cacheSeriesIndex) ;
   dlo.setSeriesId(this._cacheSeriesId) ;

   // Parse MiscId (used for indexing into a DvtGraphData obj's layers array)

   n = this._parseMiscId(node) ;
   if (n >= 0) {
     dlo.setMiscId(n) ;
   }

   attr = node.getAttribute('stId') ;
   if (attr) {                       // we currently ignore -ve values
      n = parseInt(attr) ;
      if (n >= 0)
        dlo.setStackId(n) ;
   }

   //  Parse Reference Objects

   if (objId.substr(0, 2) === 'RO')    // if oID starts with "RO" (e.g. "ROB or ROF")
     this._addToReferredTo(obj) ;      // add object to parser's list of referred-to
                                       // det objects

   // Parse fill and stroke                                   

   this._parseFillandStroke(objId, obj, node);
    
   // Parse shadow
   this._parseShadow(node) ;
   if (this._cacheShadowObj) {
       //clone shadow obj before adding because it will be locked when added
       //BUG FIX #12427741: store the shadow to apply after the object is added to the display list, 
       //because we need to be able to get the bounding box for the object
       this._storeShadow(obj, this._cacheShadowObj.clone());
   }
    
   // Parse bevel
   if (objId !== "LeM") {
     var bevel = this._parseBevel(node) ;
     if (bevel) {
         //BUG FIX #12427741: store the bevel to apply after the object is 
         //added to the display list, because we need to be able to get the 
         //bounding box for the object
         this._storeBevel(obj, bevel);
     }
   }
    
   // Parse visibility

   attr = node.getAttribute("v") ;
   if (attr !== null) {
      //Bug Fix 13625051: invisible shape cause PPR problem in firefox
      //bug fix 13878084: reference objects set as hidden to start with but show up on hover
      if (attr == "h" && objId.substr(0, 2) === 'RO')
        obj.setVisible(attr !== "h") ;
      else if (attr == "h") 
        obj.setAlpha(0.001);
   }

   this._parseMouseTransparency(node) ;  // parse go=
   obj.setMouseEnabled(!this._bCacheMouseTransparency) ;

   //  Parse tooltips

   this._checkTooltipTemplate(node) ;   // cache value if tt= attrb found.

   if (! this._bCacheMouseTransparency) {
     this._parseTooltip(node, obj);
   }

   // Parse clip path

   attr = node.getAttribute("clip") ;    // clip="(#id)"
   if (attr) {
       attr = this._Chart.createSubId(this._parseClipPathID(attr)); 
       dlo._clipPath = this._Chart.getClipPath(attr) ;  // find clip object
       if (dlo._clipPath === null) {  // if clip path not specified yet, will resolve at end of parsing.
           if (this._aForClipResolution === null)
               this._aForClipResolution = [] ;
           this._aForClipResolution.push(attr) ;   // } we keep pairs of data for
           this._aForClipResolution.push(detObj) ; // } each clip request (id, and detobj).
       }
   }
   
   // Parse user props
   
   attr = node.getAttribute("Z");
   if (attr) {
     dlo.setUserProps(attr);
   }
    
   // Add object to legend objects array and set cursor if it's selectable
   if (objId === DvtGraphObjType.LEGEND_MARKER) {
       this._addLegend(obj) ;
       if (this._HSS) {
           obj.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
       }
   }

   //  Look for child nodes of the object (such as <lg>, <ros> and <sa>).
   // Markers are a special case
   if (node.nodeName !== 'm' ) {
    if (node.hasChildNodes()) {
        this._parseBaseChildNodes(node, obj, bUseSA) ;
    }
   }
   
   // Set drilling on a shape attribute
   attr = node.getAttribute('drillJs');
      if (attr) {
       this._parseDrill(obj, attr);
   }
   
} ;


/*---------------------------------------------------------------------*/
/*   parseBaseChildNodes()                                             */
/*---------------------------------------------------------------------*/
/**
  *   Parse the  <ros>, <lg>, <sa> children of the node,
  *   and update the supplied det obj.
  */
DvtGraphParser.prototype._parseBaseChildNodes = function(nodes, obj, bUseSA)
{
   var  aChild = nodes.childNodes ;
   var  l = aChild.length ;

   if (! l) {
     return ;
   }

   //  parse the  <lg>, <ros>, <sa> subnodes

   var node ;
   var s ;
   for (var i = 0; i < l; i++)
   {
     node = aChild[i] ;
     s    = node.nodeName ;


     if (s === 'ros')  {                      // referenceObjects
       this._parseRefObjects(obj, node) ;
     }
     else if (bUseSA && s === 'sa')  {        // shapeAttributes
       this._parseShapeAttributes(obj, node) ;
     }
     else if ( s === 'lg')  {

       //  TDO - JRM
     }

   }
};


/*---------------------------------------------------------------------*/
/*  _parseBevel()                                                     */
/*---------------------------------------------------------------------*/
/**
 *   Parses the node for a bv= attribute, and updates the cache.
 *   @private
 */
DvtGraphParser.prototype._parseBevel = function(node)
{
   var attr = node.getAttribute('bv') ;
   if (attr)
   {
      return this._parseBevelAttrs(attr) ;
   }
   return null;
};




/*----------------------------------------------------------------------*/
/*    _parseBevelAttrs()      Parse a bevel bv= attribute               */
/*----------------------------------------------------------------------*/
/*  Format of bv attribute                                              */
/*                                                                      */
/*  bv="highlightcolor/alpha,shadowcolor/alpha,distance,strength,angle, */
/*                                          blurX,blurY, quality, type" */
/*                                                                      */
/*  where any of the individual values can be omitted as long as a sep0 */
/*  arator comma is still specified.                                    */
/*                                                                      */
/*                                                                      */
/*  Note:  A bevel is not applied if the resulting image exceeds 2880   */
/*         pixels in width or height.  If, for example, you zoom in on  */
/*         a large movie clip with a bevel applied, the bevel is turned */
/*         off if the resulting image exceeds the limit of 2880 pixels. */
/*                                                                      */
/*  Arguments                                                           */
/*  ---------                                                           */
/*                                                                      */
/*  highlight color/alpha  -  the highlight color and transparency of   */
/*                            the bevel.  Default is 0xffffffff.        */
/*  shadow color/alpha     -  the highlight color and transparency of   */
/*                            the bevel.  Default is 0xff000000.        */
/*  distance    - the offset distance of the bevel. Valid values are in */
/*                pixels (floating point). The default is 4.            */
/*  strength    - the strength of the imprint or spread. Valid values   */
/*                are from 0 to 255. The larger the value, the more     */
/*                color is imprinted and the stronger the contrast be-  */
/*                tween the bevel and the background. The default value */
/*                is 1.                                                 */
/*  angle       - the angle of the bevel (floating point). Valid values */
/*                are from 0 to 360.0 deg. The default value is 45 deg. */
/*                the angle value represents the angle of the theoret-  */
/*                ical light source falling on the object and determines*/
/*                the placement of the effect relative to the object.   */
/*                If the distance property is set to 0, the effect is   */
/*                not offset from the object and, therefore, the angle  */
/*                property has no effect.                               */
/*  blurX       - the amount of horizontal blur  (floating point). Valid*/
/*                values are 0 to 255.0  Default is 4.                  */
/*                Note:  values that are a power of 2 are optimized to  */
/*                render more quickly than other values.                */
/*  blurY       - the amount of vertical blur (float). Valid values are */
/*                0 to 255.0  Default is 4.                             */
/*                Note:  values that are a power of 2 are optimized to  */
/*                render more quickly than other values.                */
/*                                                                      */
/*  quality     - the number of times to apply the effect. (int) Valid  */
/*                values are 0 to 15.  Default is 1                     */
/*                Note: bevels with lower values are rendered more      */
/*                quickly. For most applications, a quality value of    */
/*                1 (low), 2 (medium), or 3 (high) is sufficient.  Al-  */
/*                though you can use additional numeric values up to 15 */
/*                to achieve different effects, higher values are rend- */
/*                ered more slowly. Instead of increasing the value of  */
/*                quality, you can often get a similar effect, and with */
/*                faster rendering, by simply increasing the values of  */
/*                the blurX and blurY properties.                       */
/*  type        - the placement of the bevel on the object.  Inner and  */
/*                outer bevels are placed on the inner or outer edge; a */
/*                full bevel is placed on the entire object. Valid      */
/*                values are "i" (inner), "o"  (outer), "f"  (full).    */
/*----------------------------------------------------------------------*/


DvtGraphParser.prototype._parseBevelAttrs = function(s)
{
   if (! s) {
     return new DvtBevel() ;
   }


   // Defaults

   var hilightRgba   = null ;
   var shadowRgba    = null;
   var bevelAngle    = 45 ;
   var bevelStrength = 1.0 ;
   var bevelQuality  = 1 ;                // range  0 to 15
   var bevelDistance = 4.0 ;
   var bevelType     = "inner" ;          // "i" = inner, "o" == outer, "f" == full
   var bevelBlurX    = 4.0 ;
   var bevelBlurY    = 4.0 ;
   var bevelType     = "inner";
   var bKnockout     = false ;

   if (s != "")          // any values specified ?
   {
     var  n ;
     var sVal  ;

     var arVals  =  s.split(',') ;

     for (var i = 0; i < arVals.length; i++)
     {
       sVal = arVals[i] ;

       if (sVal == null || sVal.length == 0)
         continue ;

       switch (i)
       {
          case 0:   // 1st arg is highlight color (and optionally alpha)
                    {
                      hilightRgba = DvtColorUtils.getRGBA(sVal) ;
                    }
                    break ;

          case 1:   // 2nd arg is shadow color (and optionally alpha)
                    {
                      shadowRgba = DvtColorUtils.getRGBA(sVal) ;
                    }
                    break ;

          case 2:   // 3rd arg is distance
                    {
                       n = parseFloat(sVal) ;
                       if (! isNaN(n))
                         bevelDistance = n ;
                    }
                    break ;

          case 3:   // 4th arg is strength
                    {
                       n = parseFloat(sVal) ;
                       if (! isNaN(n))
                         bevelStrength = n ;
                    }
                    break ;

          case 4:   // 5th arg is angle
                    {
                       n = parseFloat(sVal) ;
                       if (! isNaN(n))
                         bevelAngle = n ;
                    }
                    break ;

          case 5:   // 6th arg is blurX
                    {
                       n = parseFloat(sVal) ;
                       if (! isNaN(n))
                         bevelBlurX = n ;
                    }
                    break ;

          case 6:   // 7th arg is blurY
                    {
                       n = parseFloat(sVal) ;
                       if (! isNaN(n))
                         bevelBlurY = n ;
                    }
                    break ;

          case 7:   // 8th arg is quality
                    {
                       n = parseInt(sVal, 1) ;
                       if (! isNaN(n))
                         bevelQuality = n ;
                    }
                    break ;

          case 8:   // 9th arg is type
                    {
                      if (sVal.charAt() == "o")             // outer
                        bevelType = DvtBevel.TYPE_OUTER ;
                      else if (sVal.charAt() == "f")        // full
                        bevelType = DvtBevel.TYPE_FULL ;
                      else
                        bevelType = DvtBevel.TYPE_INNER ;   // inner
                    }
                    break ;
       }     // end switch
     }     // end for
   }    // end if have values

   return new DvtBevel(bevelDistance,     // distance
                       bevelAngle,        // angle
                       hilightRgba,       // highlight rgba
                       shadowRgba,        // shadow rgba
                       bevelBlurX,        // blurX
                       bevelBlurY,        // blurY
                       bevelStrength,     // strength
                       bevelQuality,      // quality
                       bevelType,         // type
                       bKnockout) ;       // knockout
};



/*---------------------------------------------------------------------*/
/*   _parseClipPath()                                                  */
/*---------------------------------------------------------------------*/
/*  Typical definition:                                                */
/*                                                                     */
/*      <clipPath id="plotAreaClip">                                   */
/*        <r p="42,10,334,259"/>'                                      */
/*      </clipPath>                                                    */
/*                                                                     */
/*  This clip path will be referenced by id by another object. e.g.    */
/*                                                                     */
/*      <pl points="42.0,47.0 . . ." clip="(#plotAreaClip)"  . . ./>   */
/*---------------------------------------------------------------------*/
/**
  *   Parse the clipPath element and add to the clipping rects array.
  *   @private
  */
DvtGraphParser.prototype._parseClipPath = function(node)
{
   var sID = this._Chart.createSubId(node.getAttribute('id')) ;

   if ((sID === null)  || (! node.hasChildNodes())) {
     return ;        // can't continue, need an id to reference
   }

   var nodes  = node.childNodes ;
   var  len   = nodes.length ;
   if (len <= 0) {
     return ;
   }

   var  xPos ;     // clipping rect x
   var  yPos ;     // and y.
   var  w ;        // clipping rect width
   var  h ;        // clipping rect height

   var thisNode = nodes[0] ;       // only looking at first child rect at this point,
   if (thisNode.nodeName === "r") {
     var  s ;

     s = thisNode.getAttribute('p') ;    // p="x,y,w,h"
     if (s !== null) {
       var  x1 ;
       var  x2 ;

       x1   = s.indexOf(DvtGraphParser.COMMA) ;
       xPos = parseInt(s.substring(0, x1), 10) ;
       if (! isNaN(xPos)) {
         x2   = s.indexOf(DvtGraphParser.COMMA, ++x1) ;
         yPos = parseInt(s.substring(x1, x2)) ;
         if (! isNaN(yPos)) {
           x1  = s.indexOf(DvtGraphParser.COMMA, ++x2) ;
           w   = parseInt(s.substring(x2, x1), 10) ;
           h   = parseInt(s.substring(++x1), 10) ;
           if (( ! isNaN(w)) && (! isNaN(h))) {
             if (w !== 0 && h !== 0) {             // only care if we have a usable rect
               var  clip = new DvtGraphClipPath(sID) ;

               clip.addRect(xPos, yPos, w, h) ;
               this._Chart.addClipPath(clip) ;     // add to chart's internal list

               // Update chart with plot area rectangle

               if (sID.indexOf(DvtGraphParser.PLOTAREACLIP) != -1 && (! this._bHavePlotArea)) {
                 this._Chart.setPlotRect(xPos, yPos, w, h) ;
                 this._bHavePlotArea  = true ;
               }
             }
           }
         }
       }
     }
   }          // end if node is 'r'
};



/*--------------------------------------------------------------------------*/
/*   _parseClipPathID()      Returns the string between the (# and the      */
/*                           trailing right paren.                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*    Format is  <c ...  clip="(#id)">                                      */
/*--------------------------------------------------------------------------*/
/**
  *  Extracts and returns the string between the # and the trailing right parenthesis.
  *  @private
  *  @type String
  */
DvtGraphParser.prototype._parseClipPathID = function(s)
{
   var id = null ;

   if (s) {
     var n = s.indexOf("#", 1) ;      // expect # following the left paren

     if (n >= 1) {
        n += 1 ;                      // bump over the '#'
        var m = s.indexOf(")", n) ;   // find trailing end paren

        if (m > n) {
           id = s.substring(n, m) ;
        }
     }
   }
   return id ;
};



/*---------------------------------------------------------------------*/
/*   _parseData()                                                      */
/*---------------------------------------------------------------------*/
/**
  *   Parse the &lt;data&gt; xml element.  
  *   @private
  *   @type DvtGraphData
  */
DvtGraphParser.prototype._parseData = function(node)
{
   var  data ;

   if (! this._Data) {
     data = new DvtGraphData() ;
     this._Data        = data ;     //  There is always a data obj in the chart.
     this._Chart.setData(data) ;
     if (node === null) {
       return ;
     }
   }
   else {
     data = this._Data
   }

   var attr ;
   var arColors ;
   var ar ;
   var len ;
   var i ;
   var s ;

   //  Get the group values

   attr = node.getAttribute("groups") ;
   if (attr !== null) {
      ar = attr.split(DvtGraphParser.MD_GROUP_SEPARATOR) ;

      if (ar !== null && ar.length > 0) {
        data.setGroups(ar) ;
      }
   }

   //  Get the series values

   attr = node.getAttribute("series") ;
   if (attr !== null) {
      ar = attr.split(DvtGraphParser.MD_GROUP_SEPARATOR) ;

      if (ar !== null && ar.length > 0) {
        data.setSeries(ar) ;
      }
   }

   //  Get the group labels.

   attr = node.getAttribute("groupLabels") ;
   if (attr !== null)  {
      ar = attr.split(DvtGraphParser.MD_GROUP_SEPARATOR) ;

      if (ar !== null && ar.length > 0) {
        data.setGroupLabels(ar) ;
      }
   }

   //  Get the O1 group labels.

   attr = node.getAttribute("O1GroupLabels") ;
   if (attr !== null)  {
      ar = attr.split(DvtGraphParser.MD_GROUP_SEPARATOR) ;

      if (ar !== null && ar.length > 0) {
        data.setO1GroupLabels(ar) ;
      }
   }

   //  Get the series labels

   attr = node.getAttribute("seriesLabels") ;
   if (attr !== null) {
      ar = attr.split(DvtGraphParser.MD_GROUP_SEPARATOR) ;

      if (ar !== null && ar.length > 0) {
        data.setSeriesLabels(ar) ;
      }
   }

   //  Get the series colors

   attr  = node.getAttribute("sc") ;
   if (attr !== null) {
      ar = attr.split(',') ;

      if (ar !== null && ar.length > 0)  {
        len = ar.length ;
        if (len > 0)  {
           arColors = [] ;
           for (i = 0; i < len; i++)  {
             arColors.push(ar[i]) ;
           }
        }

        data.setSeriesColors(arColors) ;
        this._arSeriesColors = arColors; // Keep this in case we need to update for split series
      }
   }

   //------------------------------------------------------------//
   //  Get and preprocess the Value, Label and Member Attrs in   //
   //  conjunction with tooltip template (from the preceding     //
   //  <metadata>.                                               //
   //------------------------------------------------------------//

   var sTemplate  = this._sTooltipTemplate ;   // parsed from <metadata>

   var  iMacroID ;

   //  To save time generating the tooltips on mouseover, will do a little processing
   //  of the string now.

   if (sTemplate === null || sTemplate.length === 0)  {
      //  Have no template but might have some X, Y or Z data, so will state
      //  macros supported since we will not passing this in with
      //  Data.setTooltipTemplate().

      data.setMacrosSupported(this._arMacros.length) ;
   }
   else  {        // process the template
     var   iStart ;
     var   iEnd          = 0 ;
     var   iTemplate     = 0 ;
     var   sMacro        = null ;
     var   iMacrosLength = this._arMacros.length ;
     var   arMacroIDs    = [] ;
     var   arTemplate    = [] ;
     var   isFunnel=false;
//   s = sTemplate.replace(c_reCarriageReturn, cEMPTY) ;   // remove \r's to avoid double spacing
     s = sTemplate ;                                       // revisit [[[[[[[[[

     do  {
       iStart = s.indexOf("[", iTemplate) ;   // find potential start of a macro

       if (iStart < 0)  {
         s = s.substring(iTemplate) ;         // get possible trailing string of template
         if (s.length > 0) {
           arTemplate.push(s) ;               // save trailing string
         }
         break ;
       }
       iEnd   = s.indexOf("]", iStart + 2) ;  // find end of macro

       if (iEnd < 0) {
         break ;                              // didn't find it, we're done
       }

       //  If macro is a known, save the string preceding the macro
       //  in arTemplate, and the macro ID in arMacroIDs.
       sMacro = s.substring(iStart + 1, iEnd) ;
       for (i = 0; i < iMacrosLength; i++) {
         if (sMacro === this._arMacros[i])   {  
           arTemplate.push(s.substring(iTemplate, iStart)) ;  // save preceding string
           arMacroIDs.push(i) ;                               // save the macro ID
           if(i==17 ||i==18) {
               isFunnel=true;
           }
           break ;
         }
       }
       if(isFunnel)
       {
             arMacroIDs.push(27);//add unformatted actual value
             arMacroIDs.push(28);//add unformatted target value
       }
       iTemplate = iEnd + 1 ;
       sMacro    = null ;

     } while (true) ;              // loop until template parsing is completed.


     //  If we found any macros, store details of the template strings and
     //  the macros in the data object.

     if (arMacroIDs.length > 0) {
       // Set tooltip template array components,  an array of macro indexes in
       // order of appearance in the template, and a count of all possible macros
       // supported.

       data.setTooltipTemplate(arTemplate, arMacroIDs, this._arMacros.length) ;

       //dbg("tooltipTemplate = " + arTemplate) ;
       //dbg("macro ID's = " + arMacroIDs) ;


       //  Get data attribute strings for macros excluding series/group and X,Y and Z macros.

       len = arMacroIDs.length ;

       for (i = 0; i < len; i++)  {
         ar = null ;
         iMacroID = arMacroIDs[i] ;

         //dbg("[" + i + "] getting values for [" + x + "] = "   +  c_arMacros[x]) ;

         if (iMacroID > this._iZIndex)  {                     // (series/group macros handled separately,
                                                              // and X, Y, Z will be handled later)
           s = node.getAttribute(this._arMacros[iMacroID]) ;  // get the attribute value
           if ( s !== null) {
             ar = s.split(DvtGraphParser.MD_MACROVAL_SEPARATOR) ;
             if (ar !== null || ar.length > 0) {
               data.setDataValues(iMacroID, ar, false) ; // these are string data values
             }

           }
         }
       }

     }            // end if have macro ID's

   }      // end if/else  have a template attribute


   //----------------------------------------------------//
   //  Get the X, Y, and Z data values and ValueFormats  //
   //----------------------------------------------------//

   for (iMacroID = this._iXIndex; iMacroID <= this._iZIndex; iMacroID++)  {
      ar = null ;
      s = node.getAttribute(this._arMacros[iMacroID]) ; // get the data attribute value
                                                        // (comma separated floats)
      if ( s !== null)  {
        ar = s.split(DvtGraphParser.MD_DATAVAL_SEPARATOR) ;
        if (ar === null || ar.length === 0) {
          ar = null ;                                  // no useful data found
        }
      }
      s = node.getAttribute(this._arMacros[iMacroID] + this.VF) ; // find XVF, YVF or ZVF

      var vf = this._parseValueFormat(s) ;          // (a VF is always returned)

      if (ar !== null) {
        data.setValueFormat(iMacroID, vf) ;
      }

      //  If the XVF, YVF, or ZVF formats are found (or we created a
      //  default one), the corresponding X=, Y=, or Z= data are floats,
      //  and not formatted string values.

      if (ar !== null && s !== null)   {          // if have format and data
        for (x = 0; x < ar.length; x++) {         // convert strings to floats
           ar[x] = parseFloat(ar[x]) ;
        }
        data.setDataValues(iMacroID, ar, true) ;  // reset the data array to floats.
      }
      else  {   //  No value format attribute.
         //  See if there is a corresponding XL=, YL=, or ZL= data attribute,
         //  and save this for use by Data.makeTooltip().

         if (iMacroID === this._iXIndex) {
            s = node.getAttribute(this.XL) ;
         }
         else if (iMacroID === this._iYIndex) {
            s = node.getAttribute(this.YL) ;
         }
         else if (iMacroID === this._iZIndex) {
            s = node.getAttribute(this.ZL) ;
         }
         else {
            s = null ;
         }

         if (s !== null) {
            var arXYZL = s.split(DvtGraphParser.MD_MACROVAL_SEPARATOR) ;
            if (arXYZL !== null && arXYZL.length > 0)  {
               data.setAltLValues(iMacroID, arXYZL) ;    // formatted values

               if (ar !== null) {                        // Convert the strings to floats
                 DvtArrayUtils.toFloat(ar) ;
                 data.setDataValues(iMacroID, ar, true); // reset the data array.
              }
            }
         }
      }

       //dbg("setting data i = " + i + " macroID=" + macroID + " using " + ar) ;
       //dbg("Data=" + oData.toString()) ;

   }      // end for  (processing of X, Y, Z values and formats)


   //  Parse any XAVF and YAVF axis value formats

   s = node.getAttribute(this.ATTR_XAVF) ;
   if (s !== null) {
     data.setXAVF(this._parseValueFormat(s)) ;
   }

   s = node.getAttribute(this.ATTR_YAVF) ;
   if (s !== null) {
     data.setYAVF(this._parseValueFormat(s)) ;
   }

   s = node.getAttribute(this.ATTR_Y2AVF) ;
   if (s !== null) {
     data.setY2AVF(this._parseValueFormat(s)) ;
   }


   //----------------------------------------------------//
   //  Pie related parsing                               //
   //----------------------------------------------------//

   if (this._Chart.isPie()) {
     s = node.getAttribute('psl') ;     //  pie series labels
     if (s) {
        ar = s.split(DvtGraphParser.MD_GROUP_SEPARATOR) ;

        if (ar && ar.length > 0) {
          this._arPieSliceLabels = ar ;
        }
     }
   }

} ;

/*---------------------------------------------------------------------*/
/*   _parseDrill()      Parses drilling attribute                      */
/*---------------------------------------------------------------------*/
DvtGraphParser.prototype._parseDrill = function(obj, drillLink)
{
    var dlo = obj._Dlo;
    var sa = dlo.getShapeAttr();
    if (sa) {
    sa.drillJS = drillLink;
    } else {
       sa = new DvtShapeAttr();
       sa._chart = this._Chart;
       sa.drillJS = drillLink;
       dlo.setShapeAttr(sa);
       obj.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
    }
}


/*---------------------------------------------------------------------*/
/*   _parseEllipse()      Parses an ellipse or circle                  */
/*---------------------------------------------------------------------*/
/**
  *  Parses a &lt;c&gt; or &lt;o&gt; tag.
  *  @private
  *  @type DvtShape
  */
DvtGraphParser.prototype._parseEllipse = function(objId, node)
{
   //  <c d="29,273,313,273" O="O1AxisLine" c="#80a0a0a0"/>
   //  <c p="100.0,128.0,85.0" ang="-20.0,220.0" ct="CHORD" O="PA"
   //     sh="" ft="ftC" fc="" t="Dial Gauge - Metric Inside\nRow 1: 63   (Medium)" tt="f"/> ;

   var attr ;
   var n ;

   //  See if this is an arc of a circle/ellipse

   var bArc = false ;
   var angleStart ;
   var angleExtent ;
   var closureType ;

   attr = node.getAttribute("ang") ;         // ang="angleStart,angleExtent"
   if (attr !== null) {
     bArc = true ;                           // need an arc, not a circle or ellipse 
     n = attr.indexOf(',') ;
     angleStart = parseFloat(attr.substring(0, n)) ;
     angleExtent = parseFloat(attr.substring(++n)) ;
     closureType = node.getAttribute("ct") ;
     if (! closureType) {
       closureType = DvtArc.PIE ;
     }
   }

   attr   = node.getAttribute("p") ;       // p="cx,cy,rx[,ry]"
   var ar = attr.split(',') ;

   ar = DvtArrayUtils.toFloat(ar) ;

   var bSelectable = (this._Chart.isSelectionSupported() &&
                        (objId === DvtGraphObjType.SLICE         ||
                        objId === DvtGraphObjType.PIE_BAR_SLICE));
   var shape;
   if (bSelectable && bArc) {
     shape = new DvtGraphSelectableArc(this._ctx,ar[0], ar[1], ar[2], ar[3], angleStart, angleExtent, closureType);
     shape.setDataColor(this._Chart.getSeriesColor(this._parseSeriesIndex(node)));
     shape.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
     shape.setZOrderManager(this._Chart);
   } else {
     shape = (bArc?  new DvtArc(this._ctx,ar[0], ar[1], ar[2], ar[3], angleStart, angleExtent, closureType) :
                       (node.nodeName == 'c')? new DvtCircle(this._ctx, ar[0], ar[1], ar[2]) :
                                               new DvtOval(this._ctx, ar[0], ar[1], ar[2], ar[3])) ;
   }
   
   DvtGraphSysUtils.makeDetObject(shape, this._Chart, objId) ;
   this._parseBaseAttribs(shape, node, objId) ;
   
   if (bSelectable && bArc) {
      this._storePie3DFace(shape._Dlo);
   }
   
   if (this._workBC && this.workLS === DvtStroke.DASHED) {
      var stroke = new DvtSolidStroke(this._workBC, this._workBA, this._workLW) ;
      stroke.setStyle(DvtStroke.DASHED, this._workDash) ;
      shape.setStroke(stroke) ;
     } 

   if (objId === DvtGraphObjType.SLICE || objId === DvtGraphObjType.PIE_BAR_SLICE) {
       this._addRiser(shape);
   }

   return shape ;
} ;


/**
 * Returns if the node is invisible
 * @private
 */
DvtGraphParser.prototype._isInvisible = function (node) {
    //  Parse object fill
    var ft = this._parseFillType(node) ;
    if (ft === DvtFill.COLOR) {
        var fc = node.getAttribute('fc');
        var bc = node.getAttribute('bc') ;
        if (fc == null && this._cacheFillColor == null && bc == null) {
            return true;
        }
    }
    return false;
}


/*--------------------------------------------------------------------------*/
/*   _parseFillandStroke()                                                  */
/*--------------------------------------------------------------------------*/
/**
  *  Parses attributes associate with an object's fill and stroke 
  *  @private
  */
DvtGraphParser.prototype._parseFillandStroke = function(objId, obj, node)
{
    this._workC            = null ;             // color
    this._workCA           = null ;             //  and alpha
    this._workFC           = null ;             // fill color
    this._workFA           = null ;             //  and alpha
    this._workBC           = null ;             // border color
    this._workBA           = null ;             //  and alpha
    this._workLW           = null ;             // line width
    this._workLS           = DvtStroke.SOLID ;  // line style
    this._workDash         = null ;             // dash/dot length(s) if line style = dashed
    this._workImgURL       = null ;             // image URL
    this._workImgDsplyMode = null ;             // image display mode
    
    // Parse line width
    attr = node.getAttribute("lw") ;
    this._workLW = (attr == null ? 1 : parseInt(attr)) ;
    
    // Parse line style
    attr =  node.getAttribute('ls') ;
    if (attr) {
        var ls       = this._parseLineStyle(attr) ;
        this._workLS = ls ;
        if (ls === DvtStroke.DASHED) {
            this._workDash = DvtStroke.DASH ;       // std dash definition
        }
        else if (ls === DvtStroke.DOTTED) {
            this._workDash = DvtStroke.DOT ;        // std dot definition
        }
        else if (ls === DvtStroke.DASHED_DOTTED) {
            this._workDash = DvtStroke.DASH_DOT ;   // std dash/dot definition
        }
    }
    
    //  Parse and set object fill
    var alpha ;
    var ft = this._parseFillType(node) ;
    if (ft === DvtFill.COLOR || ft === DvtFill.PATTERN) {
        attr = node.getAttribute('fc') ;    // fill type is color  #[aa]rrggbb
        if (attr !== null) {
            var fc = ((attr !== '')? ((attr.length > 7)? DvtColorUtils.getPound(attr) : attr) : null) ;
            if (fc) {
                alpha = DvtColorUtils.getAlpha(attr) ;
            }
            this._workFC         = fc ;
            this._workFA         = alpha ;
            this._cacheFillColor = fc ;
            this._cacheFillAlpha = alpha ;
        }
        else {
            this._workFC = this._cacheFillColor ;
            this._workFA = this._cacheFillAlpha ;
        }
        
        // Needed for updating series color of split series
        if (objId === DvtGraphObjType.LINE_MARKER && this._Data.getLayers() !== null) {
          this._arSeriesColors[this._parseSeriesIndex(node)] = this._workFC;
          this._Data.setSeriesColors(this._arSeriesColors);
        }
        
        if (ft === DvtFill.COLOR) {
            obj.setSolidFill(this._workFC, this._workFA) ;
        }
    }
    
    if (ft === DvtFill.GRADIENT) {
        if (node.hasChildNodes()) {
            var nodes = node.childNodes ;
            var len = nodes.length ;
            for (i = 0; i < len; i++) {
               var child = nodes[i] ;
                
               if (child.nodeName ==='g') {
                   obj.setFill(this._parseGradient(objId, child)) ;
                   break ;
               }
            }
        }
    } 
    else if (ft === DvtFill.TEXTURE) {
        // TODO: set fill for texture
        attr = node.getAttribute('tURL');
        if (attr) {
            this._workImgURL = attr ;
            attr = node.getAttribute('TDM') ;   // texture display mode
            this._workImgDsplyMode = (attr === "s") ? this._workImgDsplyMode = DvTFill.STRETCHED : DvtFill.TILED ;
        }
        else {                            // if no texture URL use white fill
            ft = DvtFill.COLOR ;
            this._workFC = '#ffffff';
            this._workFA = 1 ;
        }
    }
    else if (ft === DvtFill.PATTERN) {
        var fp = node.getAttribute('fp') ;
        if (fp) {
            obj.setFill(new DvtPatternFill(fp, this._workFC)) ;
        } else {
            obj.setSolidFill('#ffffff');
        }
    }
    
    //  Parse color (used by lines and polylines)
    attr  = node.getAttribute('c') ;         // color  #[aa]rrggbb
    if (attr !== null) {
        this._workC = ((attr !== '') ? ((attr.length > 7)? DvtColorUtils.getPound(attr) : attr) : null) ;
        if (this._workC) {
            this._workCA = DvtColorUtils.getAlpha(attr) ;
        }
        this._cacheColor = this._workC ;
        this._cacheColorAlpha = this._workCA ;
    }
    else {
        this._workC = this._cacheColor  ;
        this._workCA = this._cacheColorAlpha  ;
    }
    
    //  Parse border color
    attr = node.getAttribute('bc') ;      // #[aa]rrggbb
    if (attr !== null) {                  //  no border color = don't draw borders
        this._workBC  = (attr.length > 7)? DvtColorUtils.getPound(attr) : attr ;
        this._workBA  = DvtColorUtils.getAlpha(attr) ;
    }
    if (this._workBC) {
        obj.setSolidStroke(this._workBC, this._workBA, this._workLW) ;
    }
};



/*---------------------------------------------------------------------*/
/*   _parseFillType()                                                  */
/*---------------------------------------------------------------------*/
/**
  *  Parses an ft= attribute, and returns a value such as {@link DvtFill#COLOR}.
  *  @private
  *  @returns {number}
  */
DvtGraphParser.prototype._parseFillType = function(node)
{
   var ft = node.getAttribute("ft") ;

   if (ft !== null) {
      if (ft === 'ftC') {
        ft = DvtFill.COLOR ;
      }
      else if (ft === 'ftG') {
        ft = DvtFill.GRADIENT ;
      }
      else if (ft === 'ftP') {
        ft = DvtFill.PATTERN ;
      }
      this._cacheFillType = ft ;
   }
   else {
     ft = this._cacheFillType ;
   }

   return ft ;

};


/*---------------------------------------------------------------------*/
/*   _parseFont()                                                      */
/*---------------------------------------------------------------------*/
/*                                                                     */
/*   <f id="0" font="11 #000000 'sans-serif'"/>                        */
/*                                                                     */
/*   font format:   "size #rrggbb 'font' b i u"                        */
/*                                                                     */
/*  The single quotes around the font name may be omitted if the name  */
/*  does not contain blanks.                                           */
/*---------------------------------------------------------------------*/
/**
  *  Parse a &lt;f&gt; font tag and returns a {@link DvtFont} object.
  *  @private
  *  @type DvtFont
  */
DvtGraphParser.prototype._parseFont = function(node)
{
   var fid =  parseInt(node.getAttribute("id"), 10);

   // Parse the font string: "size color font-family font-weight"
   var attr = node.getAttribute("font") ;
   
   // If the font name is multiple words, it will be enclosed in single quotes.  Replace with placeholder.
   var fontName = null;
   var startIndex = attr.indexOf("'");
   var endIndex = attr.indexOf("'", startIndex+1);
   if(startIndex >= 0 && endIndex >= 0) {
     fontName = attr.substring(startIndex, endIndex+1);
     attr = attr.replace(fontName, "font-family");
   }
   
   var ar = attr.split(' ') ;
   var size  = ar[0] ;
   var color = ar[1] ;
   var alpha = DvtColorUtils.getAlpha(color) ;
   color = (color.length > 7)? DvtColorUtils.getPound(color) : color ;

   fontName = fontName ? fontName : ar[2] ;
   if (fontName.charAt(0) === "'") {
     fontName = fontName.substring(1, fontName.length -1) ; 
   }
   
   // By default, use the default font from the SVG renderer
   if (fontName === 'Default' || fontName === 'sans-serif')
     fontName = null;

   // Check for bold, italic, underline.

   var  bBold      = false ;
   var  bItalic    = false ;
   var  bUnderline = false ;

   if (ar.length > 3) {
      var  len  = ar.length ;
      var  c ;

      for (var i = 3; i < len; i++)  {
        c = ar[i].toUpperCase() ;
        if (c === 'B') {
          bBold = true ;
        }
        else if (c === 'I') {
          bItalic = true ;
        }
        else if (c === 'U') {
          bUnderline = true ;
        }
      }
   }

   var font = new DvtFont(size, color, alpha, fontName, bBold, bItalic, bUnderline) ;

   font._Id = fid ;    // internal Id just for graph
   return  font ;

} ;


/*---------------------------------------------------------------------*/
/*    _parseFontId()                                                   */
/*---------------------------------------------------------------------*/
/**
  *   Parses an fid= attribute and returns the id or the cached id.
  *   @private
  *   @type number
  */
DvtGraphParser.prototype._parseFontId = function(node)
{
   var fid = node.getAttribute("fid") ;

   if (fid) {
     fid = parseInt(fid, 10) ;
     this._cacheFontId = fid ;
   }
   else {
     fid = this._cacheFontId ;
   }

   return fid ;
};



/*---------------------------------------------------------------------*/
/*    _parseGradient()                                                 */
/*---------------------------------------------------------------------*/
/**
  *   Parses a &lt;g&gt; gradient specification and returns a {@link DvtLinearGradientFill)
  *   or a {@link DvtRadialGradientFill}.
  *   @private
  *   @type DvtGradientFill
  */
DvtGraphParser.prototype._parseGradient = function(objId, child)
{
   // <g dir="gdRi" b="47,113,21,160" p="0.0,0.15,0.3,0.65,0.85,1.0" 
   //               c="#26517c,#728eaa,#3f668c,#003366,#26517c,#002d5b"/>' ;
   // <g ang="270" b="47,113,21,160" p="0.0,0.15,0.3,0.65,0.85,1.0" 
   //              c="#26517c,#728eaa,#3f668c,#003366,#26517c,#002d5b"/>' ;

   var  grad = null ;         // returned obj
   var  attr ;
   var  angle = 0 ;           // degrees
   var  dir ;
   var  cx, cy ; 
   var  rx, ry ; 
   var  r ; 
   var  arBounds   = null ;
   var  bUseBounds = false ;  // true if bounding box used

   //  Parse colors and stop ratios

   var arColors = null ;
   var arAlphas = null ;
   var arStops  = null ;

   attr = child.getAttribute("p") ;                     // stops
   if (attr === null) {
     arStops = this._cacheGradStops ;
   }
   else if (attr !== null)  {
     arStops   = attr.split(',') ;
     DvtArrayUtils.toFloat(arStops) ;
     this._cacheGradStops = arStops ;
   }

   attr = child.getAttribute("c") ;                     // colors
   if (attr !== null) {
     arColors  = attr.split(',') ;
     arAlphas  = [] ;
     var len = arColors.length ;

     for (var i = 0; i < len; i++)  {
        attr = arColors[i] ;
        if (attr.length > 7) {                          // extended MT #aarrggbb ?
          arColors[i] = DvtColorUtils.getPound(attr) ;  // remove alpha
        }
        arAlphas.push(DvtColorUtils.getAlpha(attr)) ;
     }
   }
   else {     // just return a default gradient

    arColors = [black, white] ;
    arAlphas = [1, 1] ;
   }

   //  Parse gradient bounding box

   attr = child.getAttribute("b") ;          // b="x,y,w,h"
   if (attr !== null)  {
     arBounds  = attr.split(',') ;
     DvtArrayUtils.toFloat(arBounds) ;
   }
   bUseBounds = (arBounds &&
                 !((arBounds[0]==0) && (arBounds[1]==0) && 
                   (arBounds[2]==0) && (arBounds[3]==0)));


   // Parse ang= first.  ang= uses the standard mathematical convention
   // of a zero-angle (horizontal west-to-east) line rotated positively
   // anti-clockwise,or negatively clockwise.

   attr = child.getAttribute("ang") ;   // grad angle
   dir  = child.getAttribute("dir") ;   // grad direction

   if (attr !== null)  {                // angle specified?
     angle = parseFloat(attr) ;
     if (isNaN(angle))
       angle = 0 ;
     this._cacheGradAngle = angle ;
   }
   else {                 //  no angle, use the direction flag.
     if (dir !== null) {
       this._cacheGradDir = dir ;
     }
     else {
       dir = this._cacheGradDir ;       // use cached direction
     }

     //  Parse direction flag for GD45 and GD135.  If a bounding box is
     //  also defined, these definitions are interpreted as the angle 
     //  relative to the top left and right corners of the bounding box to
     //  their diametrically opposed opposite corners.

     if (dir !== null) {                // no angle, is there a direction flag?
       if (dir == 'gdD') {              // down
         angle = -90 ;
       }
       else if (dir == "gdRi"){         // left to right
         angle = 0 ;
       }
       else if (dir == "gdD45") {       // 45 deg, or top right to bottom left of bbox
         angle = -135;
         
         if (bUseBounds) {
           radians = Math.atan2(arBounds[3], arBounds[2]);
           degrees = radians * this.DEGREES_PER_RAD;
           angle = (degrees += 180) ;
         }
       }
       else if (dir == "gdD135"){       // 135 deg, or top left to bottom right of bbox
         angle = -45;
         
         if (bUseBounds) {
           radians = Math.atan2(arBounds[3], arBounds[2]);
           degrees = radians * this.DEGREES_PER_RAD;
           angle = -degrees;
         }
       }
       else if (dir === 'gdR') {        // radial
         attr  = child.getAttribute("cx") ;
         cx = (attr != null ? parseInt(attr) : 0) ;
         attr  = child.getAttribute("cy") ;
         cy = (attr != null ? parseInt(attr) : 0) ;
         attr  = child.getAttribute("r") ;
         r  = (attr != null ? parseInt(attr) : 0) ;
         attr  = child.getAttribute("rx") ;
         rx = (attr != null ? parseInt(attr) : 0) ;
         attr  = child.getAttribute("ry") ;
         ry = (attr != null ? parseInt(attr) : 0) ;

         // We currently don't handle oval gradients.  Here we just set 
         // the radius to the x radius (which seems to give reasonable
         // results).

         if (rx != 0) {
           r = Math.max(rx, ry) ;
         }
       }
       else if (dir == "gdC"){             // custom angle ?
          s = child.getAttribute('pts') ;  // custom details are in pts="x1,y1,x2,y2"
          if (s !== null) {
            this._cacheCustomPts = s ;
          }
          else {
            s = this._cacheGradCustom ;    // use cached value
          }

          //  Get the implied angle, and adjust the stops based on the custom
          //  details.

          angle = this._computeCustomAngleAndStops(s, arBounds, arStops) ;
       }
       else {
         angle = 0 ;                        // use default left to right.
       }
     }
   }    // end parse ang= and dir=

   grad =  (dir === 'gdR')? new DvtRadialGradientFill(arColors, arAlphas, arStops, cx, cy, r, arBounds) :
                            new DvtLinearGradientFill(angle, arColors, arAlphas, arStops, arBounds) ;
   return grad ;
} ;


/*---------------------------------------------------------------------*/
/*   _parseGraphTag()                                                  */
/*---------------------------------------------------------------------*/
/**
  *  Parses a &lt;graph&gt; or &lt;gaugeset&gt; tag attributes.
  *  @private
  */
DvtGraphParser.prototype._parseGraphTag = function(node, bGauge)
{
   this._Chart._width  = parseInt(node.getAttribute("w"));
   this._Chart._height = parseInt(node.getAttribute("h"));

   var n ;

   if (! bGauge) {

      s  = node.getAttribute('threed') ;          // is it a 3D chart?
      if (s)
        this._Chart.set3D(s.charAt(0) === DvtGraphParser.TRUE) ;


      s = node.getAttribute('sr') ;      //  Chart series rollover behavior
      if (s != null) {
        n = DvtGraphCore.RB_NONE ;
        var a = s.split(" ") ;
        l  = a.length ;
      
        for (i = 0; i < l; i++) {
          s = a[i] ;
          if (s === "RB_DIM")
            n =  DvtGraphCore.RB_DIM ;
          else if (s === "RB_HIGHLIGHT")
            n =  DvtGraphCore.RB_HILITE ;
        }
        this._Chart.setSROEffect(n) ;
      }

      s = node.getAttribute('hs');        // Hide and Show series
      if (s !== null && s !== undefined) {
        // If present, it is enabled. The syntax is "tttfftftft", where
        // 't' means that the series at that index is hidden.
      
        this._HSS = s ;                   // will be processed further after <data> parsed.
        s = node.getAttribute('hsr');
        if (s)
          this._Chart.setHSRescale(s=="t");
        else
          this._Chart.setHSRescale(false);
        s = node.getAttribute('hsnb');
        if (s)
          this._Chart.setHSNewBehaviorUsage(s);
        else
          this._Chart.setHSNewBehaviorUsage("off");
      }
      
      // Attachment mode check
      s = node.getAttribute('att');
      if (s && s == 't')
        this._Chart.setAttachmentMode(true);

      //  DataZoomEventAttrs

      s = node.getAttribute('zoomDir') ;
      if (s) {
         l = s.length ;
         var zoomDir = DvtGraphCore.DATA_ZOOM_NONE ;
         for (i = 0; i <l; i++)  {
           if (s.charAt(i) == "h")
             zoomDir = (zoomDir === DvtGraphCore.DATA_ZOOM_VERT)? DvtGraphCore.DATA_ZOOM_BOTH : DvtGraphCore.DATA_ZOOM_HORIZ ;
           else if (s.charAt(i) == "v")
             zoomDir = (zoomDir === DvtGraphCore.DATA_ZOOM_HORIZ)? DvtGraphCore.DATA_ZOOM_BOTH : DvtGraphCore.DATA_ZOOM_VERT ;
         }

        this._Chart.setZoomDir(zoomDir) ;
      }

      // Parse marquee zoom mode
      
      s = node.getAttribute('mz') ;
      if (s && s.charAt(0) === DvtGraphParser.TRUE) {
        this._Chart.setMarqueeZoomMode(DvtGraphMarqueeZoomHandlerBase.ZOOM_AUTO);
      }
      
      // Parse drag panning mode
      
      s = node.getAttribute('pan') ;
      if(s != null && s == DvtGraphParser.TRUE) {
        this._Chart.setPanMode(DvtGraphPanHandler.PAN_AUTO);
      }

      this._Chart.setZoomInOutJs(node.getAttribute('dataZoomInRectEventJs'),
                                 node.getAttribute('dataZoomOutRectEventJs'),
                                 node.getAttribute('dataZoomInClickEventJs'),
                                 node.getAttribute('dataZoomOutClickEventJs')) ;

      // Set zoom and scroll behaviour

      this._Chart.setZoomScrollBehavior(node.getAttribute('zoomAndScroll')) ;


      // pie series effect

      s = node.getAttribute('se') ;
      if (s) {
        if (s === 'c') {
          s = DvtFill.COLOR ;
        }
        else if (s === 'g') {
          s = DvtFill.GRADIENT ;
        }
        else if (s === 'p') {
          s = DvtFill.PATTERN ;
        }
        else {
          s = DvtFill.NONE ;
        }
        this._Chart.setSeriesEffect(s) ;
      }

      s = node.getAttribute('ve') ;                  // visual effect
      this._Chart.setVisualEffect(s && (s.charAt(0) === DvtGraphParser.TRUE)) ;


      s = node.getAttribute('pse') ;               // auto explode behavior
      this._bPieAutoExplode = (s && (s.charAt(0) === DvtGraphParser.TRUE));


      s = node.getAttribute('sel') ;               // Selections  -  sel="s" |"m"
      if (s) {
        this._Chart.setSelectionMode( (s != "m")? DvtGraphCore.SEL_SINGLE : DvtGraphCore.SEL_MULTI) ;
      }
      
      //  Selection listener

      s = node.getAttribute('selList') ;           // selList="t"
      if (s) {
      this._Chart.setSelListener(s.charAt(0) === DvtGraphParser.TRUE) ;
      }
      
      // ShapeAttributes link color
      s = node.getAttribute("linkColor");
      if (s) 
        this._linkColor = s;

      // Pie explodeAll/uniteAll behavior!

      s = node.getAttribute('psea') ;
      if (s) {
        this._pieExplodeAll = true ;
        //s = s.split(';');
        //this._pieExplodeAllResource = s[0];
        //this._pieUniteAllResource   = s[1];
      }

      // drop target feedback attrs

      s = node.getAttribute('dtBC');
      if (s)
          this._Chart.setDragFeedbackBorderColor(s);
      s = node.getAttribute('dtFC');
      if (s)
          this._Chart.setDragFeedbackFillColor(s);
      s = node.getAttribute('dtFA');
      if (s)
          this._Chart.setDragFeedbackFillAlpha(s);

   }  // end if (! gauge)

};


/*---------------------------------------------------------------------*/
/*   _parseGaugeMetaData()                                             */
/*---------------------------------------------------------------------*/
/**
  *   @private
  */
DvtGraphParser.prototype._parseGaugeMetaData = function(node)
{
   //  If chart is a dial gauge, extract the angle extents and data metadata
   //  e.g. <md ang="0.0,180.0" d="0.0~;100.0~;63.0~;63~,TopLabel~,BottomLabel"/>' ;
   //
   //  <md ang="startAngle,anglextent"
   //      d="minval~,maxval~,curval~,metriclabel~,toplabel~,bottomlabel"/>
   //
   //  "ang" contains the start angle and the angle between the gauge ends, so
   //  the example above shows a zero angle line (horizontal) and (going
   //  anticlockwise, a 180deg angle (i.e. a straight line).  A -90 angle is
   //  vertically downwards.

   var  s ;
   var  ar ;
   var  len ;
   var  i ;
   var  x ;
   var  gaugeData;

   if (this._type === DvtGraphType.DIAL)  {
      var dgd = new DvtDialGaugeData() ;

      s = node.getAttribute('ang') ;
      if (s != null) {
         x = s.indexOf(DvtGraphParser.COMMA) ;

         dgd._dialAngleStart  = parseFloat(s.substring(0, x)) ;
         dgd._dialAngleExtent = parseFloat(s.substring(++x)) ;
      }

      s = node.getAttribute('id') ;    // metadata id
      if (s !== null) {
        dgd._id = s ;
      }

      gaugeData = dgd ;
   }


   //-------------------------------------//
   //   Dial, LED or StatusMeter  Gauges  //
   //-------------------------------------//

   if (this._type === DvtGraphType.DIAL || this._type == DvtGraphType.LED ||
                                          (this._type.indexOf(DvtGraphType.STATUSMETER) >= 0))  {
      if (! gaugeData)  {
        gaugeData = new DvtGaugeData() ;
      }

      s  = node.getAttribute('d') ;        // get the min, max and current values
      if (s !== null) {
        ar = s.split(DvtGraphParser.MD_D_SEPARATOR) ;

        if (ar !== null && ar.length > 0)  {
          var  arLen = ar.length ;

          // temp code for bug 6909341 (multiple gauges in one player)
          if (this._Chart.getGaugeData() !== null) {   // multiple <md> tags in 1 set of xml?

            var anim = this._anim ;
            if (anim) {
              anim.animationOnDisplay    = DvtGraphAnimation.ANIMATION_NONE ;  // turn off initfx
              anim.animationOnDataChange = DvtGraphAnimation.ANIMATION_NONE ;  // turn off ADS/PPR
            }
          }

          gaugeData._minVal = parseFloat(ar[0]) ;   // convert the 1st three
          gaugeData._maxVal = parseFloat(ar[1]) ;   // numeric strings to
          gaugeData._curVal = parseFloat(ar[2]) ;   // floats

          //  Get the labels, if any

          if (arLen >= 4) {
            gaugeData._metricLabel = ar[3] ;
            if (arLen >= 5) {
              gaugeData._topLabel  = ar[4] ;
              if (arLen > 5) {
                gaugeData._bottomLabel = ar[5] ;
              }
            }
          }
        }
      }      // end process 'd'

      //  Before we get threshold values and colors, must check the autocolor
      //  attribute.  If false (or omitted) we ignore the threshold
      //  colors/values.

      s  = node.getAttribute('ac') ;
      if (s !== null && s === "t") {
        //  Get threshold values

        s  = node.getAttribute('tv') ;
        if (s !== null) {
          ar = s.split(DvtGraphParser.COMMA) ;
          if (ar !== null && ar.length > 0) {
            len = ar.length ;
            gaugeData._arThresholdValues = new Array(len) ;

            for (i = 0; i < len; i++)  {
              gaugeData._arThresholdValues[i] = parseFloat(ar[i]) ;
            }
          }
        }

        //  Get threshold colors

        if (gaugeData._arThresholdValues != null)  {
          s  = node.getAttribute('tc') ;
          if (s != null) {
            ar = s.split(DvtGraphParser.COMMA) ;

            if (ar !== null && ar.length > 0) {
              len = ar.length ;
              gaugeData._arThresholdColors = new Array(len) ;

              for (i = 0; i < len; i++) {
                gaugeData._arThresholdColors[i] = ar[i];
              }
            }
          }
          if (gaugeData._arThresholdColors === null)    // no colors so don't
            gaugeData._arThresholdValues = null ;       // need the values.
        }
      }         // end if have autocolor


      //  Get gauge tooltip

      gaugeData.m_sTooltip  = node.getAttribute('t') ;

      this._Chart.setGaugeData(gaugeData) ;
      return ;                             // end of gauge parsing
   }    // end  if gauge
} ;


/*---------------------------------------------------------------------*/
/*  _parseGroupIndex()                                                 */
/*---------------------------------------------------------------------*/
/**
 *   Parses the node for a G= attribute, and updates the cache.
 *   @private
 */
DvtGraphParser.prototype._parseGroupIndex = function(node)
{
   var s = node.getAttribute("G") ;   // group index
   if (s !== null) {
     if (s.length === 0) {            // empty string means clear the cache
       this._cacheGroupIndex = -1;
       this._cacheGroupId = null;
     } else {
         var n = parseInt(s, 10) ;
         if (! isNaN(n)) {
           if (n < this._Data.getGroupCount()) {
             this._cacheGroupIndex = n ;   // cache new group index
             this._cacheGroupId    = this._Data.getGroupFromIndex(n) ; // cache new series str.
           }
        }
     }
    }
};



/*---------------------------------------------------------------------*/
/*   _parseHandle()                                                    */
/*---------------------------------------------------------------------*/
/**
  *  Parses a &lt;h&gt; tag with handle attributes.
  *  @private
  *  @type DvtHandle
  */
DvtGraphParser.prototype._parseHandle = function(objId, node)
{
   // <h id="PA" c="54,21,215,237" b="54,-8,269,288"> ... nested objects ... </h>

   var  attr;
   var  ar;
   var  clipRect = null;
   var  boundRect = null;

   var sId = node.getAttribute('id');

   attr = node.getAttribute('c');
   if (attr) {
       clipRect = new DvtRectangle();
       this._parseRectString(attr, clipRect);
   }

   attr = node.getAttribute('b');
   if (attr) {
       ar = attr.split(',') ;
       boundRect = DvtArrayUtils.toFloat(ar) ;
   }

   this._currentHandleId = DvtGraphHandle.createHandleId(sId);
   
   var h = new DvtGraphHandle(this._ctx, sId, boundRect, clipRect, this._Chart.createSubId(sId));
   if (node.hasChildNodes())  {
       var nodes = node.childNodes ;
       if (nodes)  {
           for (var i = 0; i < nodes.length; i++) {
                // if there is a clip path defined on the children of a handle that has the same coordinates as those of the parent handle, don't add it!
               if (nodes[i].nodeName == "clipPath" && nodes[i].childNodes.length > 0){
                 var child = nodes[i].childNodes[0];
                 if (child.getAttribute('p') == node.getAttribute('c')) continue;
               }

               var obj = this._parseListNode(nodes[i]);

               if (obj !== null) {
                    if (obj instanceof Array) {
                    ar = obj ;
                    var  arLen = ar.length ;
                    for (var j = 0; j < arLen; j++) {
                        obj = ar[j] ;
                        h.addObject(obj) ;
                    }
                }
                else
                    h.addObject(obj) ;
                }
           }
       }
   }
   this._currentHandleId = null;

   DvtGraphSysUtils.makeDetObject(h, this._Chart, objId) ;

   // register with handle manager
   this._Chart.getHandleManager().addHandle(h);

   // TODO temp hack for managing legend handle
   if(sId==='scrollableLegend'){
       this._Chart._legendHandle = h;
   }

   return h;
};


/*--------------------------------------------------------------------------*/
/*     _parseImage()                                                         */
/*--------------------------------------------------------------------------*/
DvtGraphParser.prototype._parseImage = function(objId, node)
{
   var x = node.getAttribute('x');
   var y = node.getAttribute('y');
   var w = node.getAttribute('w');
   var h = node.getAttribute('h');
   var url = node.getAttribute('url');
   
   var img = new DvtImage(this._ctx, url, (x ? parseInt(x) : 0), (y ? parseInt(y) : 0), (w ? parseInt(w) : 0), (h ? parseInt(h) : 0)) ;
   DvtGraphSysUtils.makeDetObject(img, this._Chart, objId) ;
   
   this._parseTooltip(node, img);
   
   var attr = node.getAttribute('drillJs');
   if (attr) {
       this._parseDrill(img, attr);
   }
   
   return img;
}


/*---------------------------------------------------------------------*/
/*   _parseLine()                                                      */
/*---------------------------------------------------------------------*/
/**
  *  Parses a &lt;l&gt; tag
  *  @private
  *  @type DvtShape
  */
DvtGraphParser.prototype._parseLine = function(objId, node)
{
   //  <l p="29,273,313,273" O="O1AxisLine" c="#80a0a0a0"/>

   var  attr ;
   var  ar ;

   attr = node.getAttribute("p") ;
   ar = attr.split(',') ;

   DvtArrayUtils.toFloat(ar) ;

   var line;
   var bStockObj = (objId === DvtGraphObjType.STOCK_RISER      ||
                    objId === DvtGraphObjType.STOCK_OPEN_TICK  ||
                    objId === DvtGraphObjType.STOCK_CLOSE_TICK ||
                    objId === DvtGraphObjType.STOCK_HIGH_LINE  ||
                    objId === DvtGraphObjType.STOCK_LOW_LINE);
   
   if (this._Chart.isSelectionSupported() && bStockObj)
   {
     line = new DvtGraphDetLine(this._ctx, ar[0], ar[1], ar[2], ar[3]);
   }
   else
   {
     line = new DvtLine(this._ctx, ar[0], ar[1], ar[2], ar[3]) ;
   }
   DvtGraphSysUtils.makeDetObject(line, this._Chart, objId) ;
   this._parseBaseAttribs(line, node, objId) ;

   //selection support - store related stock markers
   //so we can link them together later
   //NOTE: need to call _parseBaseAttribs before doing this 
   if (bStockObj)
   {
     this._storeStockMarker(line._Dlo);
   }

   if (this._workC) {
     var stroke = new DvtSolidStroke(this._workC, this._workCA, this._workLW) ;
     if (this._workLS !== DvtStroke.SOLID) {
       stroke.setStyle(this._workLS, this._workDash) ;
     }
     
     if(objId !== DvtGraphObjType.LEGEND_MARKER && objId !== DvtGraphObjType.BOX_LINE)
       stroke.setLineEnd(DvtStroke.ROUND) ;
     
     line.setStroke(stroke) ;
   }

   line.setPixelHinting(objId === DvtGraphObjType.O1_AXIS_LINE      ||
                        objId === DvtGraphObjType.O1_MINOR_GRID     ||
                        objId === DvtGraphObjType.O1_MAJOR_GRID     ||
                        objId === DvtGraphObjType.X1_AXIS_LINE      ||
                        objId === DvtGraphObjType.X1_MINOR_GRID     ||
                        objId === DvtGraphObjType.X1_MAJOR_GRID     ||
                        objId === DvtGraphObjType.Y1_AXIS_LINE      ||
                        objId === DvtGraphObjType.Y2_AXIS_LINE      ||
                        objId === DvtGraphObjType.Y1_MINOR_GRID     ||
                        objId === DvtGraphObjType.Y2_MINOR_GRID     ||
                        objId === DvtGraphObjType.Y1_MAJOR_GRID     ||
                        objId === DvtGraphObjType.Y2_MAJOR_GRID     ||
                        objId === DvtGraphObjType.LEGEND_AREA       ||
                        objId === DvtGraphObjType.STOCK_HIGH_LINE   ||
                        objId === DvtGraphObjType.STOCK_LOW_LINE    ||
                        objId === DvtGraphObjType.STOCK_RISER       ||
                        objId === DvtGraphObjType.STOCK_CLOSE_TICK  ||
                        objId === DvtGraphObjType.STOCK_OPEN_TICK   ||
                        objId === DvtGraphObjType.BOX_LINE          ||
                        objId === DvtGraphObjType.REFOBJ_FRONT      ||
                        objId === DvtGraphObjType.REFOBJ_BACK       ||
                        (objId === DvtGraphObjType.TICK_MARK && this._type.indexOf(DvtGraphType.STATUSMETER) != -1) ||
                        objId === DvtGraphObjType.OTHER             ||    // TDO this is used for title underlining
                                                                   // and other stuff! Too many usases of 'other'
                        objId === DvtGraphObjType.QUADRANT_LINE);

   if (objId === DvtGraphObjType.LINE_RISER) {
     this._addRiser(line) ;
   }

   //  Classify axis components

   if (objId === DvtGraphObjType.Y1_AXIS_LINE) {             // axes
     this._Y1Axis = line ;
   }
   else if (objId === DvtGraphObjType.Y2_AXIS_LINE) {
     this._Y2Axis = line ;
   }
   else if (objId === DvtGraphObjType.X1_AXIS_LINE ||
            objId === DvtGraphObjType.O1_AXIS_LINE) {
     this._XAxis = line ;
   }

   if (objId === DvtGraphObjType.X1_MINOR_GRID     ||        // x grid line
       objId === DvtGraphObjType.X1_MAJOR_GRID) {
     if (! this._aXGridLines) {
       this._aXGridLines = [] ;
     }
     this._aXGridLines.push(line) ;
     line.setMouseEnabled(false);
   }
   else  if (objId === DvtGraphObjType.Y1_MINOR_GRID     ||  // y1 grid line(s)
             objId === DvtGraphObjType.Y1_MAJOR_GRID) {
     if (! this._aY1GridLines) {
       this._aY1GridLines = [] ;
     }
     this._aY1GridLines.push(line) ;
     line.setMouseEnabled(false);
   }
   else  if (objId === DvtGraphObjType.Y2_MINOR_GRID     ||  // y2 grid line(s)
             objId === DvtGraphObjType.Y2_MAJOR_GRID) {
     if (! this._aY2GridLines) {
       this._aY2GridLines = [] ;
     }
     this._aY2GridLines.push(line) ;
     line.setMouseEnabled(false);
   }

   if (objId === DvtGraphObjType.FRAME_SEPARATOR) {          // split frame separator
     this._FrameSep = line ;
     this._Chart.setFrameSeparator(line);
   }

   return line ;
} ;


/*-------------------------------------------------------------------------------*/
/*   _parseLineMarker()                                                          */
/*-------------------------------------------------------------------------------*/
/**
  *   Parses a &lt;lmc&gt; tag.
  *   @private
  */
DvtGraphParser.prototype._parseLineMarker = function(objId, node)
{
   // Note shapeAttributes and t="tooltip" are not supported by DvtLineMarker, so
   // middle-tier will send  a marker object when that support is needed.

   var  obj = new DvtLineMarker(this._Chart) ;
   var  i ;
   var  len ;
   
   obj.setHandleId(this._currentHandleId);
   //Parse coordinates and convert each top left to center coords
   var s = node.getAttribute('p') ;       // p="x,y,. . ."
   if (s) {
      var arCoords = s.split(',') ;
      len = arCoords.length ;
      for (i = 0; i < len; i++) {
         arCoords[i] = parseFloat(arCoords[i]) + this.LINEMARKER_HALFSIZE ;
      }
      obj.setCoords(arCoords) ;

   }

   // Parse the marker size   
   s = node.getAttribute('ms') ;     //  ms="n"
   var  ms = 2 * this.LINEMARKER_HALFSIZE ;   // default
   if (s !== null) {
     ms = parseInt(s, 10) ;
     if (isNaN(ms)) {
       ms = 6 ;                      // use default
     }
   }

   this._parseGroupIndex(node) ;
   obj.setGroupIndex(this._cacheGroupIndex) ;

   this._parseSeriesIndex(node) ;
   obj.setSeriesIndex(this._cacheSeriesIndex) ;
   
   this._Chart.setSeriesMarkerSize(obj.getSeriesIndex(), ms) ;

   this._parseShadow(node) ;    // Although LM's do not use shadows need to check
                                // if the xml wants the shadow cache cleared.
                                
   this._checkTooltipTemplate(node) ;  // Check for the tt attr 

   // Finally, add the object to the Chart's tooltip helper.

   this._Chart.getMarkerTooltipHelper(true).addMarker(obj);

   //  And mark the associated riser as supporting tooltips

   var ar = this._findRisersForSeries(this._cacheSeriesIndex) ;

   if (ar) {
     len = ar.length ;
     for (i = 0; i < len; i++) {
        var dlo = ar[i]._Dlo ;
        if (dlo._objId.indexOf(DvtGraphObjType.LINE_RISER) != -1 || dlo._objId === DvtGraphObjType.AREA_RISER) {
            dlo.setUseTooltip(true) ;
        }
     }
   }

} ;


/*--------------------------------------------------------------------------*/
/*   _parseLinestyle()                                                      */
/*--------------------------------------------------------------------------*/
/**
  *  @private
  */
DvtGraphParser.prototype._parseLineStyle = function(s)
{
  var  ls ;

  switch (s) {

    case "LS_SOLID"    :  ls = DvtStroke.SOLID ;
                          break ;
    case "LS_DASH"     :  ls = DvtStroke.DASHED ;
                          break ;
    case "LS_DOTTED"   :  ls = DvtStroke.DOTTED ;
                          break ;
    case "LS_DASH_DOT" :  ls = DvtStroke.DASHED_DOTTED ;
                          break ;
    default            :  ls = DvtStroke.SOLID ;
                          break ;
  }

  return ls ;
};



/*-------------------------------------------------------------------------*/
/*   _parseMarker()                                                        */
/*-------------------------------------------------------------------------*/
/* Typical XML looks like this:                                            */
/*                                                                         */
/* <m p="119,60,281,101" O="LM" G="0" S="0" tt="t" mt="c,8" fc="#003366"/> */
/*                                                                         */
/* where p contains one or more pairs of top left (x,y) coords.            */
/*                                                                         */
/* One more <t> child nodes are permitted                                  */
/*                                                                         */
/* e.g.   <m p="x1,y1,x2,y2, . . . .>                                      */
/*          <f . . ./>                                                     */
/*          <t . . .>text1</t>                                             */
/*          <t . . .>text2</t>                                             */
/*        </m>                                                             */
/*-------------------------------------------------------------------------*/
/**
  *   Parses a &lt;m&gt; tag.
  *   @private
  *   @type DvtShape
  */
DvtGraphParser.prototype._parseMarker = function(objId, node)
{
    var attr ;
    var arCoords ;                          // Marker coordinates
    var arSizes ;                           // Marker sizes
    var sType ;                             // Marker type
    var nMarkers = 0 ;                      // # of Markers
    var arMarkers = [];                     // Marker(s)
    var arCustomProps = [];                 // Marker custom props
    var bBubbleOrScatter = (this._Chart.isScatterChart() || this._Chart.isBubbleChart());
    var bPolar = this._Chart.isPolarChart();
    var bLegendMarker = objId === DvtGraphObjType.LEGEND_MARKER;
    var bRiser = (objId === DvtGraphObjType.LINE_MARKER && bBubbleOrScatter && this._Chart.isInitFx());
    var bLineMarker = (objId === DvtGraphObjType.LINE_MARKER || objId === DvtGraphObjType.PARETO_LINE_MARKER) && !bBubbleOrScatter;
    var bSelectionSupport = this._Chart.isSelectionSupported();
    var invisible = this._isInvisible(node);
    var i;
    
    // Parse marker coordinates (p="x1,y1,x2,y2,...")
    attr = node.getAttribute('p') ;
    if (attr !== null) {
        arCoords = attr.split(',') ;             
        DvtArrayUtils.toFloat(arCoords) ;
        nMarkers = arCoords.length / 2 ;
    }
    else {
        return null ;
    }
    
    // Parse marker type and size(s) (mt="type,size1,size2,size3,...")
    attr = node.getAttribute("mt") ;         // 
    if (attr === null) {
        sType    =  this._cacheMarkerType ;
        arSizes  =  this._cacheMarkerSizes ; 
        DvtArrayUtils.toFloat(arSizes, 0.5) ;
    }
    else  {
        var x  = attr.indexOf(',') ;
        
        if (x >= 0) {
            sType = attr.substring(0, x) ;
            attr = attr.substring(x+1) ;
            if (attr !== null) {
                arSizes = attr.split(',');
            }
            if (arSizes === null || arSizes.length === 0) {
                arSizes = this._cacheMarkerSizes ;
            }
            else {
                this._cacheMarkerSizes = DvtArrayUtils.toFloat(arSizes, 0.5) ;
            }
        }
        else {
            sType   = attr ;
            arSizes = this._cacheMarkerSizes ;
        }
        sType = DvtMarker.convertShapeString(sType);
    
          // Adjust the server marker size to the correct client size used by DvtMarker.  Server size sent for
          // legend marker and data markers is different, and the adjustment is only needed for data markers.
          if (sType === DvtMarker.HUMAN && objId !== DvtGraphObjType.LEGEND_MARKER) {
            for (i = 0;i < arSizes.length;i++)
              arSizes[i] *= 1.4;// 1.4 is the exact adjustment value needed
          }
      
          this._cacheMarkerType = sType;
    }
    // End marker type and size parsing
    
    // If the sizes were adjusted above, adjust the coords since they point to the top left.
    if (sType === DvtMarker.HUMAN && objId !== DvtGraphObjType.LEGEND_MARKER) {
      // Adjust the coordinates (top left) for the sizes  
      for (i = 0;i < arCoords.length;i += 2) {
        // If a size has been individually specified for each marker
        // We need to add (oldSize - newSize)/2, which is (x - 1.4x)/2 = x/7
        if (i / 2 < arSizes.length) {
          arCoords[i] -= arSizes[i / 2] / 7;
          arCoords[i + 1] -= arSizes[i / 2] / 7;
        }
        else if (arSizes.length > 0) {
          // one size specified for all markers
          arCoords[i] -= arSizes[0] / 7;
          arCoords[i + 1] -= arSizes[0] / 7;
        }
      }
    }
          
     //  Parse the analytic layer values.
    var  arAnLayTypes     = null ;
    var  arAnLayColors    = null ;
    
    attr = node.getAttribute('ums') ;              // analytic layer type(s)
    if (attr)  {
        arAnLayTypes = attr.split(',') ;            // Empty element means no analytic layer for associated marker.
        attr = node.getAttribute('umc') ;           // analytic layer color(s)
        if (attr) {
            arAnLayColors = attr.split(',') ;         // empty elements mean use default color
            if (arAnLayTypes.length === 0) {
                arAnLayTypes  = null ;
                arAnLayColors = null ;
            }
        }
    }
    
    var size = arSizes[0];
    var markerX = arCoords[0];
    var markerY = arCoords[1];
    
    // Make invisible small markers bigger to make touch interaction easier
    if (DvtAgent.isTouchDevice() && !bSelectionSupport && 
            invisible && size < DvtGraphParser._MIN_TOUCH_MARKER_SIZE) {
      markerX -= (DvtGraphParser._MIN_TOUCH_MARKER_SIZE - size) / 2;
      markerY -= (DvtGraphParser._MIN_TOUCH_MARKER_SIZE - size) / 2;
      size = DvtGraphParser._MIN_TOUCH_MARKER_SIZE;
    }

    // Create the first marker
    var marker; 
    if (bRiser) {
      marker = new DvtMarkerRiser(this._ctx, sType, null, markerX, markerY, size, size, null, null, null, true) ;
    } 
    // Bubble and scatter graphs are draggable and should use DvtGraphDetMarker which implements the draggable API
    else if (!bLegendMarker && (bBubbleOrScatter || bPolar)) {
      marker = new DvtGraphDetMarker(this._ctx, sType, null, markerX, markerY, size, size, null, null, null, true)
    }
    else if (bLineMarker && bSelectionSupport) {
      marker = new DvtGraphSelectableLineMarker(this._ctx, sType, null, markerX, markerY, size, size, null, null, null, true) ;
      marker.setZOrderManager(this._Chart);
    } 
    else {  
      marker = new DvtMarker(this._ctx, sType, null, markerX, markerY, size, size, null, null, null, true);
    }
    
    // Parse remaining attributes from marker node
    DvtGraphSysUtils.makeDetObject(marker, this._Chart, objId) ;
    var dlo    = marker._Dlo 
    this._parseBaseAttribs(marker, node, objId) ;
    
    var markFill = marker.getFill();
    if (markFill instanceof DvtGradientFill) {
        var arBounds = [];
        arBounds[0] = arCoords[0];
        arBounds[1] = arCoords[1];
        arBounds[2] = arSizes[0];
        arBounds[3] = arSizes[0];
    }
    else if (markFill instanceof DvtSolidFill) {
        //Check for hollow markers with 0 opacity. Need 0.001 opacity for mouse detection
        if (markFill.getColor() === 'none') {
            markFill = DvtSolidFill.invisibleFill();
            marker.setFill(markFill);
        }
    }
    
    if (!bLegendMarker && bSelectionSupport) {
      marker.setDataColor(this._Chart.getSeriesColor(dlo.getSeriesIndex()));
      marker.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
    }
    
    if (dlo.getUserProps() != null) {
      arCustomProps = dlo.getUserProps().split("~;");
      if (arCustomProps.length >= 1 && arCustomProps[0] != null)
        dlo.setUserProps(arCustomProps[0]);
      else 
        arCustomProps = null;
    }
    
    // Tooltip Support
    var arTooltip = null;
    var tooltip = node.getAttribute("t");
    if(tooltip) 
      arTooltip = tooltip.split(DvtGraphParser.MD_TT_SEPARATOR);
      
    // Apply tooltip for the first marker
    if(arTooltip && arTooltip.length > 0)
      dlo.setTooltipText(arTooltip[0]);

    arMarkers.push(marker) ;     // push first marker
    
    // Create additional markers if multi-markers in <m> specified
    if (nMarkers > 1) {
        var  firstMarker = marker ;
        var  fdlo = dlo ;            // use members from first marker created
        var  sidx = dlo.getSeriesIndex()
        var  sid  = dlo.getSeriesId()
        var  gidx = dlo.getGroupIndex() ;
        var  clipPath = dlo._clipPath;
        var  lastSize = arSizes[0] ;
        var markStroke = marker.getStroke() ;
        
        var  idx  = 2 ;
        for (i = 1; i < nMarkers; i++)  {
            markerX = arCoords[idx];
            markerY = arCoords[idx + 1];
            
            if (i < arSizes.length) {
                size = arSizes[i] ;
            }
            else {
                size = lastSize ;
            }
            lastSize = size ;
            
            // Make invisible small markers bigger to make touch interaction easier
            if (DvtAgent.isTouchDevice() && !bSelectionSupport && 
                    invisible && size < DvtGraphParser._MIN_TOUCH_MARKER_SIZE) {
                markerX -= (DvtGraphParser._MIN_TOUCH_MARKER_SIZE - size) / 2;
                markerY -= (DvtGraphParser._MIN_TOUCH_MARKER_SIZE - size) / 2;
                size = DvtGraphParser._MIN_TOUCH_MARKER_SIZE;
            }
            
            if (bRiser) {
              marker = new DvtMarkerRiser(this._ctx, sType, null, markerX, markerY, size, size, null, null, null, true) ;
            } 
            // Bubble and scatter graphs are draggable and should use DvtGraphDetMarker which implements the draggable API
            else if (!bLegendMarker && (bBubbleOrScatter || bPolar)) {
              marker = new DvtGraphDetMarker(this._ctx, sType, null, markerX, markerY, size, size, null, null, null, true)
            }
            else if (bLegendMarker || !bSelectionSupport) {
              marker = new DvtMarker(this._ctx, sType, null, markerX, markerY, size, size, null, null, null, true);
            }
            else if (bLineMarker) {
                marker = new DvtGraphSelectableLineMarker(this._ctx, sType, null, markerX, markerY, size, size, null, null, null, true) ;
                marker.setZOrderManager(this._Chart);
            } 
            
            if (!bLegendMarker && bSelectionSupport) {
              marker.setDataColor(this._Chart.getSeriesColor(this._parseSeriesIndex(node)));
              marker.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
            }
            
            DvtGraphSysUtils.makeDetObject(marker, this._Chart, objId) ;
            var mdlo = marker._Dlo ;
            mdlo.setGroupIndex(++gidx) ;
            mdlo.setGroupId(this._Data.getGroupFromIndex(gidx)) ;
            mdlo.setSeriesIndex(sidx) ;
            mdlo.setSeriesId(sid) ;
            
            if(arTooltip && arTooltip.length > i)
              mdlo.setTooltipText(arTooltip[i]);
              
            mdlo.setUseTooltip(fdlo.isUseTooltip()) ;
            mdlo._clipPath = clipPath;
            
            if ((arCustomProps != null) && (i < arCustomProps.length))
              mdlo.setUserProps(arCustomProps[i]);
              
            marker.setMouseEnabled(firstMarker.isMouseEnabled());
            marker.setFill(markFill);
            marker.setStroke(markStroke);
            
            // Add shadows
            if (this._cacheShadowObj) {
                this._storeShadow(marker, this._cacheShadowObj.clone());
            }
            
            arMarkers.push(marker) ;
            idx += 2 ;
        }
    }
    
    // Parse child nodes (<ros> <sa> <t> <f>)
    var children     = node.childNodes;
    var shapeAttribs = [];   // Parse shape attributes and associate with marker(s) later
    var refObjs      = null; // Parse reference objects and associate with marker(s) later
    var markerText   = [];

    if (children) {
        for (i = 0; i < children.length; i++) {
            child = children[i];
            childName = child.nodeName;
            if (childName === 'sa') {
                shapeAttribs.push(child);
            }
            else if (childName === 'ros')  {
                refObjs = child;
            }
            else if (childName === 'f') {
                this._Chart.addFont(this._parseFont(child));
            }
            else if (childName === 't') {
                var childId = child.getAttribute('O');
                var text = this._parseText(childId, child);
                DvtGraphSysUtils.makeDetObject(text, this._Chart, childId);
                this._parseTooltip(child, text);
                markerText.push(text);
            }
        }
    }
    
    // Final marker processing

    for (i = 0; i < nMarkers; i++) {
        var mark = (arMarkers? arMarkers[i] : marker) ;
        // Associate child nodes
        if (shapeAttribs.length > 0) {
            this._parseShapeAttributes(mark, shapeAttribs[i]);
        }
        if (refObjs) {
            this._parseRefObjects(mark, child);
        }
        if (arAnLayTypes) {
            var ut = arAnLayTypes[i];
            var uc = arAnLayColors[i];
            this._setAnalyticLayerVals(mark._Dlo, ut, uc);
            mark.setUnderlay(mark._Dlo.getUnderlayType(), DvtColorUtils.getRGB(uc),
                                                          DvtColorUtils.getAlpha(uc));
        }
        if (markerText.length > 0) {
          text = markerText[i];
          text._Dlo.setSeriesIndex(mark._Dlo.getSeriesIndex());
          text._Dlo.setSeriesId(mark._Dlo.getSeriesId());
          text._Dlo.setGroupIndex(mark._Dlo.getGroupIndex());
          text._Dlo.setGroupId(mark._Dlo.getGroupId());
          text._Dlo.setUnderlayFC(mark._Dlo.getUnderlayFC());
          text._Dlo.setUnderlayType(mark._Dlo.getUnderlayType());
          mark.setText(text);
          arMarkers.push(markerText[i]);    // add to end of return array since all will
        }                                   // be added as child of chart
        
        // Associate markers with chart
        if (bLineMarker || bBubbleOrScatter) {
            if (bBubbleOrScatter || bPolar) {
                this._addRiser(mark) ;     
                if (bRiser && mark.setInitFx) {
                    mark.setInitFx(true);
                }
                if (this._Chart.isDataAnimation()) {
                    mark._Dlo._DCHandler = new DvtDCMarker(this._ctx, mark) ;
                }
            }
            else if (invisible) {
                this._addHiddenMarker(mark) ;
            }
            else {
                this._addMarker(mark);            // add to line/area riser marker list
            }
        }
    }

    return arMarkers;
} ;


/*-------------------------------------------------------------------------*/
/*   _parseMarkerDef()                                                     */
/*-------------------------------------------------------------------------*/
/* Typical XML looks like this:                                            */
/*                                                                         */
/*    <markerDef id="/resources/svg/redStar.svg">                          */
/*     <p bc="#000000" sc="0" sm="4" fc="#ff0000"                          */
/*        d="M7.5412187576293945,1.2909982204437256L5.867734909057617,...  */
/*    </markerDef>                                                         */
/*-------------------------------------------------------------------------*/
DvtGraphParser.prototype._parseMarkerDef = function(node)
{
    DvtMarkerUtils.createMarkerDef(this._ctx, node);
} ;

/*---------------------------------------------------------------------*/
/*     _parseMetadata()                                                */
/*---------------------------------------------------------------------*/
/**
  *  Parses a &lt;md&gt; meta data tag.
  *  @private
  */
DvtGraphParser.prototype._parseMetadata = function(node)
{
   var  s ;
   var  ar ;
   var  x ;

   if (this._Chart.isGauge()) {
     this._parseGaugeMetaData(node) ;
   }

   //-------------------------------------//
   //      parse for PIE related info     //
   //-------------------------------------//

   if (this._type === DvtGraphType.PIE) {

     s = node.getAttribute('pfc') ;     // pie feeler color
     if (s) {

       var c = (s.length > 7)? DvtColorUtils.getPound(s) : s ;
       this._pieFeelerColor = s;
     }

     //  Note: "psl" (pie slice labels) are defined in the <data> tag, so will be parsed later.

     s = node.getAttribute('slp') ;     // slice label position
     if (s) {
         // n, i, o, of, ofin

         if (s.charAt(0) === 'n')  {
            this._pieLabelPos = DvtPieChart.LABEL_POS_NONE ;
         }
         else if (s.charAt(0) === 'i')  {
             this._pieLabelPos = DvtPieChart.LABEL_POS_INSIDE ;
         }
         else if (s.charAt(0) === 'o') {
             if (s.length === 1) {
                 this._pieLabelPos = DvtPieChart.LABEL_POS_OUTSIDE ;
             }
             else if (s.length === 2 && s.charAt(1) === 'f')  {
                 this._pieLabelPos = DvtPieChart.LABEL_POS_OUTSIDE_FEELERS ;
             }
             else if (s.length === 4 && s.charAt(2) === 'i') {
                 // punt on this one
                 this._pieLabelPos = DvtPieChart.LABEL_POS_OUTSIDE_FEELERS ;
             }
         }
     }

     s = node.getAttribute("slt") ;     // slice label type
     if (s)   {
         // acceptable values are :  p, v, c, t, tp, tv

         if (s.charAt(0) === 'p') {
             this._pieLabelType = DvtPieChart.LABEL_TYPE_PERCENT ;
         }
         else if (s.charAt(0) === 'v')  {
             this._pieLabelType = DvtPieChart.LABEL_TYPE_VALUE ;
         }
         else if (s.charAt(0) === 'c') {
             this._pieLabelType = DvtPieChart.LABEL_TYPE_CUSTOM ;
         }
         else if (s.charAt(0) === 't')  {
             if (s.length === 1) {
                this._pieLabelType = DvtPieChart.LABEL_TYPE_TEXT ;
             }
             else if (s.charAt(1) === 'p')  {
                this._pieLabelType = DvtPieChart.LABEL_TYPE_TEXT_PERCENT ;
             }
             else if (s.charAt(1) === 'v')  {
                this._pieLabelType = DvtPieChart.LABEL_TYPE_TEXT_VALUE ;
             }
         }
     }

     s = node.getAttribute('PSLVF') ;     // pie slice label value format
     this._pieSliceLabelVF = this._parseValueFormat(s) ;

   }   // end if chart type == "PIE"



   //------------------------------------------------------------//
   //  Parsing for non-gauge related attributes continues below  //
   //------------------------------------------------------------//

   //  Parse the y-axis and x-axis value/scale metadata in a DvtDataSummary object.

   var  x1 ;
   var  pxMin ;
   var  pxMax ;
   var  ds = this._DataSummary ;

   s = node.getAttribute("x1Vals") ;      // x1Vals="minVal,maxVal" data values
   if (s) {
     x = s.indexOf(',') ;
     ds.x1MinVal = parseFloat(s.substring(0, x)) ;   // min x data value
     ds.x1MaxVal = parseFloat(s.substring(x+1)) ;    // max x data value
   }

   s = node.getAttribute("y1Vals") ;      // y1Vals="baseVal,minVal,maxVal" data values
   if (s)  {
     x = s.indexOf(',') ;
     ds.y1BaseVal = parseFloat(s.substring(0, x)) ;  // baseline y data value
     x1 = s.indexOf(',', ++x) ;
     ds.y1MinVal = parseFloat(s.substring(x, x1)) ;  // min y data value
     ds.y1MaxVal = parseFloat(s.substring(x1+1)) ;   // max y data value
   }

   s = node.getAttribute("y2Vals") ;      // y2Vals="baseVal,minVal,maxVal" data values
   if (s != null)  {
     x = s.indexOf(',') ;
     ds.y2BaseVal = parseFloat(s.substring(0, x)) ;   // baseline y2 data value
     x1 = s.indexOf(',', ++x) ;
     ds.y2MinVal  = parseFloat(s.substring(x, x1)) ;  // min y2 data value
     ds.y2MaxVal  = parseFloat(s.substring(x1+1)) ;   // max y2 data value
   }

   s = node.getAttribute("x1Coords") ;     // pixel values
   if (s)  {                               // x1Coords="pxmin,pxmax"
     x = s.indexOf(',') ;
     pxMin  = parseFloat(s.substring(0, x)) ;
     pxMax  = parseFloat(s.substring(x+1)) ;

     //  Compute x value/pixel ratio

     ds.xPxMin   = pxMin ;
     ds.xPxMax   = pxMax ;
     ds.xPxRatio = Math.abs((pxMin - pxMax)/(ds.x1MaxVal - ds.x1MinVal)) ;
   }

   s = node.getAttribute("y1Coords") ;     // pixel values
   if (s) {                                // y1Coords="y1BaseLine,pxmin,pxmax"
     x = s.indexOf(',') ;
     ds.y1Baseline = parseFloat(s.substring(0, x)) ;
     x1 = s.indexOf(',', ++x) ;
     pxMin = parseFloat(s.substring(x, x1)) ;
     pxMax  = parseFloat(s.substring(x1+1)) ;

     //  Compute y value/pixel ratio

     ds.yPxMin   = pxMin ;
     ds.yPxRatio = Math.abs((pxMin - pxMax)/(ds.y1MaxVal - ds.y1MinVal)) ;
   }


   //  Populate the ChartData object with layer info
   //  Typical xml is:
   //
   //  layers="0,3~;0,1,2~;1,4~;2,5~;3,4,5"          // split series
   //  layers="c~;#8f993366~;d~;#8f99cc33~;td~;t"    // analytic layers


// split series and analytic layers

   s = node.getAttribute('layers') ;
   if (s) {
     ar = s.split(DvtGraphParser.MD_GROUP_SEPARATOR) ;

      if (ar && ar.length > 0) {
        this._tempLayers = ar;
        this._Data.setLayers(ar) ;
      }
   }


   // Parse the Menu Ids for Selectable Objects

   s = node.getAttribute("mid");
   if (s)
     this._Chart.setMenuIds(s.split(","));


   //  Get the tooltip template and save for later <data> processing

   this._sTooltipTemplate  = node.getAttribute("tt") ;


   //  Check for "selection" model info.  We don't have a detlist yet, so will
   //  defer setting initial selections until parsing is complete.

   this._initSelections = node.getAttribute('sel') ;
   this._selectJS       = node.getAttribute('selJS') ;

} ;




/*---------------------------------------------------------------------*/
/*  _parseMiscId()                                                     */
/*---------------------------------------------------------------------*/
/**
  *  Parses a det object's miscellaneous Id and updates the det object.
  *  @return {number} the miscellaneous Id parsed, -1 if not found.
  *  @private
  */
DvtGraphParser.prototype._parseMiscId = function(node)
{
   var n  = -1 ;
   var s  = node.getAttribute("M") ;    // misc Id

   if (s !== null) {                    // we currently ignore -ve values
     n = parseInt(s, 10) ;
   }

   //Note: the misc ID is not cached

   return n ;
};


/*---------------------------------------------------------------------*/
/*  _parseMouseTransparency()                                          */
/*---------------------------------------------------------------------*/
/**
  *  Tests for the mouse-transparency attribute and sets the cache. 
  *  @private
  */
DvtGraphParser.prototype._parseMouseTransparency = function(node)
{
  var s = node.getAttribute("go") ;
  if (s !== null) {
      this._bCacheMouseTransparency = (s === DvtGraphParser.TRUE) ;    // cache the value
  }

  return this._bCacheMouseTransparency ;
};



/*---------------------------------------------------------------------*/
/*   _parseObjId()                                                      */
/*---------------------------------------------------------------------*/
/**
  *  Parses the O= attribute and updates the cache.
  *  @private
  *  @type DvtShape
  */
DvtGraphParser.prototype._parseObjId = function(node)
{
   var  objId = node.getAttribute("O") ;     // Object Id

   if (objId !== null) {
     this._cacheObjId = objId ;
   }
   else {
    objId = this._cacheObjId ;
   }

   return objId ;
};



/*---------------------------------------------------------------------*/
/*   _parsePath()                                                      */
/*---------------------------------------------------------------------*/
/**
  *  Parses a &lt;p&gt; path tag containg SVG Path commands.
  *  @private
  *  @type DvtShape
  */
DvtGraphParser.prototype._parsePath = function(objId, node)
{
   //  <p d="M14.0,79.0C11.79,79.001,10.0,80.79,10.0,83.0L10.0,104.0L190.0,104.0L190.0,83.0C190.001,80.79,188.21,79.0,186.0,79.0Z"...>

   var  attr ;
   var  ar ;
   var  len ;
   var  i ;

   attr = node.getAttribute("d") ;

   //  Unpack the graph format

   var s = attr.replace(/([MLCZQ])/gi, ',$1,')   // create array of coords from the
   ar = s.split(',') ;                           // compacted graph format
   len = ar.length

   if (! ar[0]) {
     ar.splice(0,1) ;       // remove probable empty 1st from list
     len-- ;
   }
   if (! ar[len-1]) {
     ar.splice(len-1,1) ;   // remove probable empty last from list
     len-- ;
   }

   for (i = 0; i < len; i++) {
     if (! ar[i]) {
       ar.splice(i,1) ;     // remove possible other empty entries.
       len-- ;
     }
   }
      
   //  Convert coordinates in command array to floats.

   for (i = 0; i < len;  i++) {
      s = ar[i] ;
      if (s != 'Z') {
        ar[i+1] = parseFloat(ar[i+1]) ;
        ar[i+2] = parseFloat(ar[i+2]) ;
        i += 2 ; 
      }
      else {
        continue ;
      }

      if (s == 'M' || s == 'L') {
        continue;
      }
      if (s == 'C') {
        ar[i+1] = parseFloat(ar[i+1]) ;
        ar[i+2] = parseFloat(ar[i+2]) ;
        ar[i+3] = parseFloat(ar[i+3]) ;
        ar[i+4] = parseFloat(ar[i+4]) ;
        i += 4 ; 
      }
      if (s == 'Q') {
        ar[i+1] = parseFloat(ar[i+1]) ;
        ar[i+2] = parseFloat(ar[i+2]) ;
        i += 2 ; 
      }
   }

   var bRiser = (objId === DvtGraphObjType.INDICATOR && this._Chart.isInitFx()) ;

   var bSelectable = (this._Chart.isSelectionSupported() &&
                        (objId === DvtGraphObjType.FUNNEL_SLICE || 
                        objId === DvtGraphObjType.SLICE         ||
                        objId === DvtGraphObjType.PIE_BAR_SLICE ||
                        objId === DvtGraphObjType.SLICE_CRUST   ||
                        objId === DvtGraphObjType.PIE_BAR_SLICE_CRUST));
   var path;
   var color = this._Chart.getSeriesColor(this._parseSeriesIndex(node));
   if (bRiser)
   {
     if (this._type === DvtGraphType.DIAL) {
       path = new DvtDialIndicator(this._ctx);
     } else {
       path = new DvtStatusIndicator(this._ctx) ;
     }
   }
   else if (bSelectable)
   {
     path = new DvtGraphSelectablePath(this._ctx);
     path.setDataColor(color);
     path.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
     path.setZOrderManager(this._Chart);
   }
   else
   {
     path = new DvtPath(this._ctx) ;
   }
   DvtGraphSysUtils.makeDetObject(path, this._Chart, objId) ;
   path.setCommands(ar) ;

   if (this._type === DvtGraphType.DIAL) {   
       var por = node.getAttribute("por") ;
       if (por != null) {
         var arr = por.split(',') ; ;
         path._xPor  = parseFloat(arr[0]) ;
         path._yPor = parseFloat(arr[1]) ;
       }
   }

   if (objId === DvtGraphObjType.INDICATOR && this._Chart.isDataAnimation()) {
     if (this._type === DvtGraphType.DIAL) {
       path._Dlo._DCHandler = new DvtDCDialIndicator(this._ctx, path);
     }
     else  {
       path._Dlo._DCHandler = new DvtDCStatusIndicator(this._ctx, path) ;
     }
   }

   this._parseBaseAttribs(path, node, objId) ; 

   if (objId === DvtGraphObjType.SLICE         ||
       objId === DvtGraphObjType.PIE_BAR_SLICE ||
       objId === DvtGraphObjType.SLICE_CRUST   ||
       objId === DvtGraphObjType.PIE_BAR_SLICE_CRUST) {
      this._storePie3DFace(path._Dlo);
   }
   
   if (objId === DvtGraphObjType.SLICE || objId === DvtGraphObjType.PIE_BAR_SLICE) {
       this._addRiser(path);
   }
   
   // set tooltip for gauge frame
   if (objId === DvtGraphObjType.GAUGE_FRAME) {
    var tooltip = this._Chart.getGaugeData().m_sTooltip
       if (tooltip) {
        path._Dlo.setTooltipText(tooltip) ;
        path._Dlo.setUseTooltip(true) ;
       }
   }

   return path ;
} ;



/*---------------------------------------------------------------------*/
/*      parsePie()                                                     */
/*---------------------------------------------------------------------*/
/**
  *   @private
  */
DvtGraphParser.prototype._parsePie = function(objId, node)
{
   //  Typical xml:

   //  <pie rotate="90.0" p="61.99687499999999,47.996875,200.00625000000002,200.00625" depth="0.0" fid="0"/>
   //  <sl ex="0.0" O="Sl" G="0" S="0" tt="t" fc="#003366"/>
   //  <sl ex="0.0" S="1" fc="#cc3300"/>
   //  <sl ex="0.0" S="2" fc="#666699"/>
   //  <sl ex="0.0" S="3" fc="#006666"/>
   //  <sl ex="0.0" S="4" fc="#ff9900"/>

   var t ;
   var t1 ;
   var x = 0 ;
   var y = 0 ;
   var w = this._Chart.getWidth() ;
   var h = this._Chart.getHeight() ;

   var pie = new DvtLegacyPieChart(this._Chart, this._ctx, this._Chart.__dispatchEvent, this._Chart) ;
   DvtGraphSysUtils.makeDetObject(pie, this._Chart) ;
   var dlo = pie._Dlo ;
   
   this._Chart.__setPieChart(pie);

   dlo.setObjId(DvtGraphObjType.PIE_CHART) ;
   this._parseBaseAttribs(pie, node, objId) ;

   var s = node.getAttribute('rotate') ;
   var anchorOffset = 0;
   if (s) {
     anchorOffset = parseFloat(s) ;
   }

   s = node.getAttribute('p') ;                 // p="x,y,w,h"
   if (s) {
     t  = s.indexOf(DvtGraphParser.COMMA) ;
     x  = parseFloat(s.substring(0, t)) ;
     t1 = s.indexOf(DvtGraphParser.COMMA, ++t) ;
     y  = parseFloat(s.substring(t, t1)) ;
     t  = s.indexOf(DvtGraphParser.COMMA, ++t1) ;
     w  = parseFloat(s.substring(t1, t)) ;
     h  = parseFloat(s.substring(++t)) ;
     if (isNaN(h)) {
       h = w ;
     }
   }

   s = node.getAttribute('depth') ;
   var depth = 0;
   if (s) {
     depth = parseFloat(s) ;
   }


   //   Get the pie slice labels.  If not found, the series labels are used,
   //   failing that, the series ID's are used.

   var ar ;
   if (! (ar = this._arPieSliceLabels)) {
     if (! (ar = this._Data.getSeriesLabels())) {
       if (! (ar = this._Data.getSeries())) {
       }
     }
   }

   // Pass collected slice info to the pie chart object.

   var halfW = w / 2 ;
   var halfH = h / 2 ;
   
   var isPie3D = this._Chart.is3D();
   pie.set3D(isPie3D);
   
   var pieChartFont = this._Chart.getFont(this._parseFontId(node));
   
   var pieInfo = new DvtPieInfo();
   pieInfo.setFeelerColor(this._pieFeelerColor);
   pieInfo.setFillType(this._Chart.getSeriesEffect());
   pieInfo.setCSSStyle(pieChartFont.getCSSStyle());
   pieInfo.setLabelPosition(this._pieLabelPos);
   pieInfo.setLabels(ar);
   pieInfo.setLabelType(this._pieLabelType);
   pieInfo.setValueFormat(this._pieSliceLabelVF);
   pieInfo.setVisualEffects(this._Chart.getVisualEffect());   
   pieInfo.setSliceInteractive(this._bPieAutoExplode);
   pieInfo.setInitFx(this._Chart.isInitFx());
   
   var anim = this._Chart.getAnimation();
   if(anim && anim.dataChangeDuration)
     pieInfo.setAnimationDuration(anim.dataChangeDuration/1000);
   
   pie.setPieInfo(pieInfo);
   
   var pieFrame = new DvtRectangle(this._pieFrameX, this._pieFrameY, this._pieFrameW, this._pieFrameH);
   var pieCenter = new DvtPoint(x+halfW, y+halfH);
   pie.setPosition(pieCenter, halfW, halfH, pieFrame);
   pie.setAnchorOffset(anchorOffset);
   pie.setDepth(depth);           

   this._pieChart = pie ;        // note for pie slice parsing  
   return pie ;
};


/*---------------------------------------------------------------------*/
/*      parsePieSlice()                                                */
/*---------------------------------------------------------------------*/
/**
  *   @private
  */
DvtGraphParser.prototype._parsePieSlice = function(objId, node)
{
  //  Collect the slice info.
  var slice = new DvtLegacySliceInfo() ;

  var s = node.getAttribute('ex') ;
  if (s)
    slice.setExplode(parseFloat(s)) ;

  this._parseGroupIndex(node) ;
  
  var pieInfo = this._pieChart.getPieInfo();
  // set on pie, not slice. Unfortunately, we have to set the group values when parsing the slices
  // and not when we parse the pie tag because we don't get the group information until we get the 
  // slices; group information is not included in the pie tag coming down from the middle tier
  pieInfo.setId(this._cacheGroupId);
  
    var idx = this._parseSeriesIndex(node) ;
  
  slice.setId(this._cacheSeriesId) ;
    slice.setIndex(idx);
  slice.setContextMenuId(this._Chart.getMenuId(this._cacheSeriesIndex, this._cacheGroupIndex));
  
  slice.setValue(this._Chart.getData(DvtGraphData.DV_Y,
                                     this._cacheSeriesIndex, this._cacheGroupIndex)) ;
                                     
    //Fill
    var attr = node.getAttribute('fc');
    if(!attr) 
        attr = this._Chart.getSeriesColor(this._cacheSeriesIndex) ;
        
    var fc = ((attr !== '')? ((attr.length > 7)? DvtColorUtils.getPound(attr) : attr) : null) ;
    var alpha = DvtColorUtils.getAlpha(attr) ;
    slice.setFillColor(fc) ;
    slice.setFillAlpha(alpha) ;
      
    var  ft  = this._parseFillType(node) ;
    if (ft === DvtFill.PATTERN) {
        this._pieChart._pieInfo.setFillType(ft) ;
        slice.setFillPattern(node.getAttribute('fp')) ;
    }
    else if (ft === DvtFill.TEXTURE) {
     // TDO
    }

    var stext = null;
    // Text set by CustomMarkerTextCallback
    attr = node.getAttribute('l') ;  // label
    if (attr) {
      stext = attr;
    }
    
    // Bug 13098740 set text taken from parsed data xml
    if (pieInfo.getLabelType() != DvtPieChart.LABEL_TYPE_CUSTOM) 
    {
      var labels = pieInfo.getLabels();
      if(idx >= 0 && idx <labels.length && labels)
        stext = labels[idx];
    }
  
    var parsedText = (!stext) ? "missing?" : stext;
    slice.setText(parsedText);
      
    //Border color
    attr = node.getAttribute('bc') ;      // #[aa]rrggbb
    if (attr) {
        slice.setStrokeColor((attr.length > 7)? DvtColorUtils.getPound(attr) : attr) ;
        slice.setStrokeAlpha(DvtColorUtils.getAlpha(attr)) ;
    }
    
    this._checkTooltipTemplate(node) ;   // cache value if tt= attrb found.
    
    if (! this._bCacheMouseTransparency) {
    
    attr = node.getAttribute("t") ;    // is a specific tooltip defined (t="tooltip text")
    if (attr) {                        // yes, use it.
    }
    else  {
       if (this._bCacheUseTooltipTemplate) {
         attr = this._Data.makeTooltipFromTemplate(this._cacheSeriesIndex,
                                                   this._cacheGroupIndex) ;
       }
    }
    
    if (attr !== null) {
      slice.setTooltip(DvtGraphParser.convertLineBreaks(attr)) ;
    }
  }
  
  // Parse user props 
  attr = node.getAttribute("Z");
  if (attr) {
    slice.setUserProps(attr);
  }
   
  // set objId
  slice.setObjId(objId);  
  
  // Parse child nodes.  Child nodes are either <sa> or <psl><sa/></psl>.
  var children = node.childNodes;
  if(children) {
    for(var i=0; i<children.length; i++) {
      if(children[i].nodeName === "sa") 
        slice.setShapeAttr(DvtShapeAttr.parse(this._Chart, children[i]));
      else if(children[i].nodeName === "psl") 
      {
        var pslChildren = children[i].childNodes;
        if(pslChildren) 
        {
          for(var j=0; j<pslChildren.length; j++)
          {
            if(pslChildren[j].nodeName === "sa") {
              var sa = DvtShapeAttr.parse(this._Chart, pslChildren[j]);
              if(sa.bLink)
                sa.linkColor = this._linkColor;
              
              slice.setLabelShapeAttr(sa);
            }
          }
        }
      }
    }
  }

  // Finally pass the sliceInfo to the pie chart.
  this._pieChart.addSliceInfo(slice) ;
};


/*---------------------------------------------------------------------*/
/*   _parsePolygon()                                                   */
/*---------------------------------------------------------------------*/
/**
  *  Parses a &lt;pg&gt; polygon tag.
  *  @private
  *  @type DvtShape
  */
DvtGraphParser.prototype._parsePolygon = function(objId, node)
{
   //  <pg p="29,273,313,273" O="O1AxisLine" c="#80a0a0a0" />

   var attr = node.getAttribute("p") ; // create array of coords
//------------OLD
   attr = attr.replace(/ /gi, ',')     // create array of coords
//------------

   attr = attr.split(',') ;
   attr = DvtArrayUtils.toFloat(attr) ;

   var bBar    = (objId === DvtGraphObjType.BAR_RISER          ||
                  objId === DvtGraphObjType.BAR_RISER_SIDE     ||
                  objId === DvtGraphObjType.BAR_RISER_TOP);
   var bPieBar = (objId === DvtGraphObjType.PIE_BAR_RISER      ||
                  objId === DvtGraphObjType.PIE_BAR_RISER_SIDE ||
                  objId === DvtGraphObjType.PIE_BAR_RISER_TOP);

   var pg = null ;

   if (objId === DvtGraphObjType.AREA_RISER && this._Chart.isInitFx())  {
     pg = new DvtAreaRiser(this._ctx, attr);
   }
   else if (this._Chart.isSelectionSupported() && (bBar || bPieBar))    {
     pg = new DvtGraphDetPolygon(this._ctx, attr);
     pg.setDataColor(this._Chart.getSeriesColor(this._parseSeriesIndex(node)));
     pg.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
     pg.setZOrderManager(this._Chart);
   }
   else  {
     pg = new DvtPolygon(this._ctx, attr) ;
   }
   DvtGraphSysUtils.makeDetObject(pg, this._Chart, objId);
   
   var dlo = pg._Dlo
   
   this._parseBaseAttribs(pg, node, objId) ;

   if (objId === DvtGraphObjType.AREA_RISER || objId === DvtGraphObjType.LINE_RISER_TOP) {
     this._addRiser(pg) ;
     dlo.setMultiRiser(true) ;
     if (this._Chart.isDataAnimation()) {
        pg._Dlo._DCHandler = new DvtDCLineArea(this._ctx, pg) ;
     }
   }
   else if (this._Chart.is3D()) {

      // store frame side reference for use in determining plot area rectangle closest to user
      if (objId === DvtGraphObjType.FRAME_SIDE) {
         this._Chart.setFrameSide(pg);
      }
      else if (bBar || bPieBar) {

        //  This is a face of a 3D bar.  Received xml polygon order is:
        //    rectangular cross section - side, front, top
        //    diamond cross section     - front, side, top
        //    triangular cross section  - front, side, top
        //    For 3D pie bar:           - front, side, top
        
        var sidx = dlo.getSeriesIndex() ;
        var gidx = dlo.getGroupIndex() ;
        if (! this._a3DCurBar) {
          this._a3DCurBar = [] ;       // start new array of bar faces (for the current bar)
        }
        
        var riser = this._3DBarCurRiser;
        // Create a new riser if the group / series index do not match the previous
        if (!riser || this._iCurBarSeries != sidx || this._iCurBarGroup != gidx) {
            var bInitFx = this._Chart.isInitFx();
            riser = ( ((bInitFx || this._Chart.isDataAnimation()) && ! bPieBar) ? new Dvt3DBarRiser(this._ctx) :
                                                                     new Dvt3DBar(this._ctx)) ;
            this._3DBarCurRiser = riser ;
            DvtGraphSysUtils.makeDetObject(riser, this._Chart) ;
        }
        this._iCurBarSeries = sidx ;
        this._iCurBarGroup = gidx ;

        if ((objId === DvtGraphObjType.BAR_RISER) || (objId === DvtGraphObjType.PIE_BAR_RISER)) {
        
            if (! this._3DBars) {                            // create an array (in series
              this._3DBars = [] ;                            // index order) of arrays for 
            }                                                // the 3 faces of each bar.
            this._a3DCurBar[0] = pg ;      // first face
        
            var ar2 =  this._3DBars[sidx] ;
            if (! ar2) {                       // if no 3D bar array for this series,
              ar2 = [] ;                       // create an array of entries per bar
              this._3DBars[sidx] = ar2 ;       // for series
            }
            ar2.push(this._a3DCurBar) ;        // add 3D bar to series array
        
            var face = pg ;                    // note this face polygon

            var riser = this._3DBarCurRiser;
            if (bInitFx && riser.setInitFx){
              riser.setInitFx(bInitFx);
            }
        
            riser.setFace(face) ;
            riser._Dlo.setObjId(DvtGraphObjType.BAR3D_RISER) ;
            riser._Dlo.setSeriesIndex(sidx) ;
            riser._Dlo.setSeriesId('' + face._Dlo.getSeriesId()) ;
            riser._Dlo.setGroupIndex(face._Dlo.getGroupIndex()) ;
            riser._Dlo.setGroupId('' + face._Dlo.getGroupId()) ;
            this._addRiser(riser) ;

            if (this._Chart.isDataAnimation() && (riser instanceof Dvt3DBarRiser)) {
              riser._Dlo._DCHandler = new DvtDC3DBar(this._ctx, riser) ;
            } 
            pg = riser;
            
        } else if ((objId === DvtGraphObjType.BAR_RISER_SIDE) || (objId === DvtGraphObjType.PIE_BAR_RISER_SIDE)) {
            this._a3DCurBar[1] = pg ;           // second face
            this._3DBarCurRiser.setFace(pg) ;
            pg = null ;
            
        } else if ((objId === DvtGraphObjType.BAR_RISER_TOP) || (objId === DvtGraphObjType.PIE_BAR_RISER_TOP)) {
            this._a3DCurBar[2] = pg ;           // third face
            this._3DBarCurRiser.setFace(pg) ;
            pg = null ;
            
        }
      }

   }       //  end if 3D
   
   if (objId === DvtGraphObjType.FRAME_SEPARATOR) {          // split frame separator
     this._FrameSep = pg ;
     this._Chart.setFrameSeparator(pg);
   }
   
   return pg ;
} ;


/*---------------------------------------------------------------------*/
/*   _parsePolyline()                                                  */
/*---------------------------------------------------------------------*/
/**
  *  Parses a &lt;pl&gt; polyline tag.
  *  @private
  *  @type DvtShape
  */
DvtGraphParser.prototype._parsePolyline = function(objId, node)
{
   //  <l p="29,273,313,273" O="O1AxisLine" c="#80a0a0a0"/>

   var  attr ;
   var  ar ;

//------------OLD
   attr = node.getAttribute("points") ;
   attr = attr.replace(/ /gi, ',')   // create array of coords
//------------NEW
// attr = node.getAttribute("p") ;
//-------------
   ar = attr.split(',') ;            // create array of coords
   DvtArrayUtils.toFloat(ar) ;

   var bRiser = (objId === DvtGraphObjType.LINE_RISER && this._Chart.isInitFx());
   
   var pl = bRiser?  new DvtLineRiser(this._ctx, ar) :
                     new DvtPolyline(this._ctx, ar) ;
   DvtGraphSysUtils.makeDetObject(pl, this._Chart, objId) ;

   this._parseBaseAttribs(pl, node, objId) ;

   if (this._workC) {
     var stroke = new DvtSolidStroke(this._workC, this._workCA, this._workLW) ;
     stroke.setLineJoin(DvtStroke.ROUND) ;
     stroke.setStyle(this._workLS, this.workDash); //Bug 12371788
     pl.setStroke(stroke) ;
   }
   
   if (this._workFC) {
       pl.setFill(null);
   }

   if (objId === DvtGraphObjType.LINE_RISER) {
     this._addRiser(pl) ;
     if (this._Chart.isDataAnimation()) {
        pl._Dlo._DCHandler = new DvtDCLineArea(this._ctx, pl) ;
     }
     pl._Dlo.setMultiRiser(true) ;
   }
   return pl ;
} ;


/*---------------------------------------------------------------------*/
/*   _parseRangeSlider()                                               */
/*---------------------------------------------------------------------*/
/**
  *  Parses a &lt;rs&gt; tag with range slider / scrollbar attributes.
  *  @private
  *  @type DvtScrollbar
  */
DvtGraphParser.prototype._parseRangeSlider = function(objId, node)
{
    // <rs startV="0.0" id="X1RS" zoomJS="AdfPage.PAGE.findComponent(&#39;graph1&#39;).getPeer().sendToServerPPR2({source:&#39;graph1&#39;,event:&#39;bi_gdse&#39;,bi_gdsA:&#39;X1&#39;,bi_gdsSt:&#39;__START__&#39;,bi_gdsSp:&#39;__SPAN__&#39;,bi_gdsC:&#39;__TOTAL__&#39;,bi_gdsT:&#39;__T__&#39;});"
    //  endV="45.0" isHoriz="t" tp="336,0" p="54.0,277.0,336.0,15.0"
    //  scrollJS="AdfPage.PAGE.findComponent(&#39;graph1&#39;).getPeer().sendToServerPPR2({source:&#39;graph1&#39;,event:&#39;bi_gdse&#39;,bi_gdsA:&#39;X1&#39;,bi_gdsSt:&#39;__START__&#39;,bi_gdsSp:&#39;__SPAN__&#39;,bi_gdsC:&#39;__TOTAL__&#39;,bi_gdsT:&#39;__T__&#39;});"/>
      
    var  attr ;
    var  ar ;
    var  isHoriz;

    attr = node.getAttribute('p') ;
    ar   = attr.split(',') ;
    ar   = DvtArrayUtils.toFloat(ar) ;

    attr = node.getAttribute('isHoriz');
    if (attr) {
        isHoriz = (attr === 't') ? true : false;
    }

    var uiId;
    var sId = node.getAttribute('id') ;
    var detObjType;
    
    switch(sId) {
        case 'X1RS' :   uiId = DvtScrollbar.ID_X1RS;
                        detObjType = DvtGraphObjType.X1_RANGE_SLIDER;
                        break;
        case 'Y1RS' :   uiId = DvtScrollbar.ID_Y1RS;
                        detObjType = DvtGraphObjType.Y1_RANGE_SLIDER;        
                        break;
        case 'Y2RS' :   uiId = DvtScrollbar.ID_Y2RS;
                        detObjType = DvtGraphObjType.Y2_RANGE_SLIDER;        
                        break;
        case 'O1RS' :   uiId = DvtScrollbar.ID_O1RS;
                        detObjType = DvtGraphObjType.O1_RANGE_SLIDER;        
                        break;
        default:        uiId = DvtScrollbar.ID_NONE;
    }
    
    this._parseFontId(node);

    var  rs = new DvtGraphScrollbar(this._ctx, uiId, ar[0], ar[1], ar[2], ar[3], isHoriz, !this._Chart.isAttachmentMode(), objId) ;
    rs.setLiveScroll(this._Chart.getZoomScrollBehavior() == DvtGraphCore.ZS_LIVE_SCROLL);
    DvtGraphSysUtils.makeDetObject(rs, this._Chart, detObjType) ;
    
    rs.setScrollEventJs(node.getAttribute('scrollJS'));
     // Do not allow zooming in attachment mode
    if (!this._Chart.isAttachmentMode())
      rs.setZoomEventJs(node.getAttribute('zoomJS'));

    // register ths scrollbar with the handle manager
    this._Chart.getHandleManager().addScrollbar(rs);

    var startV;
    var endV;

    attr = node.getAttribute('startV');  // data axis start value
    if (attr) {
        startV = parseFloat(attr);

        if (!isNaN(startV)) {
            attr = node.getAttribute('endV');    // data axis end value
            if (attr) {
                endV = parseFloat(attr);
                if (!isNaN(endV)) {
                    // get the value format object for the data axis
                    var vf = null;
                    if (this._Data != null) {
                        switch (uiId) {
                            case DvtScrollbar.ID_X1RS:
                                vf = this._Data.getXAVF();
                                break;
                            case DvtScrollbar.ID_Y1RS:
                                vf = this._Data.getYAVF();
                                break;
                            case DvtScrollbar.ID_Y2RS:
                                vf = this._Data.getY2AVF();
                                break;
                            default:
                                break;
                        }
                    }
                    rs.addDataAxis(startV, endV, vf);
                }
            }
        }
    }
    else {
        attr = node.getAttribute('startD');     // time axis start date
        if (attr) {
            startV = parseInt(attr);
            if (!isNaN(startV)) {
                attr = node.getAttribute('endD');  // time axis end date
                if (attr) {
                    endV = parseInt(attr);
                    if (!isNaN(endV)) {
                        // look for the time axis resources for l10n support
                        var timeAxisResources = null;
                        attr = node.getAttribute('taR');
                        if (attr)
                            timeAxisResources = attr.split(',');

                        // time zone support
                        var timeZoneOffset = Number.NaN;
                        attr = node.getAttribute('timeZone');
                        if (attr)
                            timeZoneOffset = parseInt(attr);

                        rs.addTimeAxis(startV, endV, timeAxisResources, timeZoneOffset);
                    }
                }
            }
        }
        else {
            if (uiId == DvtScrollbar.ID_O1RS && this._Data != null) {
                var grpLabels;
                grpLabels = this._Data.getO1GroupLabels();
                if (!grpLabels || grpLabels.length == 0) {
                    grpLabels = this._Data.getGroupLabels();
                }
                if (grpLabels && grpLabels.length > 0) {
                    rs.addCustomAxis(grpLabels);
                }

            }
        }
    }


    attr = node.getAttribute('tp');     // thmumb position [length, start]
    if (attr) {
        ar = attr.split(',') ;
        rs.setThumbStartAndLength(ar[1], ar[0]) ;
    }

    rs._thumb.updateTooltipText();
  
    return rs;
};

/*---------------------------------------------------------------------*/
/*   _parseRangeSliderZoomButton()                                               */
/*---------------------------------------------------------------------*/
/**
  *  Parses a &lt;rszb&gt; tag with range slider / scrollbar zoom button attributes.
  *  @private
  *  @type DvtZoomButton
  */
DvtGraphParser.prototype._parseRangeSliderZoomButton = function(node) 
{
    // Do not allow zooming in attachment mode
    if (this._Chart.isAttachmentMode())
      return null;
      
    var  attr ;
    var  ar ;
    var  isFlipped;
    var resources;

    attr = node.getAttribute('p') ;
    ar   = attr.split(',') ;
    ar   = DvtArrayUtils.toFloat(ar) ;

    resources = node.getAttribute('resources');
    
    attr = node.getAttribute('flipped');
    if (attr) {
        isFlipped = (attr === 'true') ? true : false;
    }
    
    var rszb = new DvtZoomButton(this._ctx, resources, null, isFlipped, DvtGraphObjType.RANGE_SLIDER_ZOOM_BUTTON) ;
    DvtGraphSysUtils.makeDetObject(rszb, this._Chart, DvtGraphObjType.RANGE_SLIDER_ZOOM_BUTTON) ;
    rszb.setTranslateX(ar[0]);
    rszb.setTranslateY(ar[1]);
 
    //  Get zooom/out event JS

    rszb.setX1ScrollInJS(node.getAttribute(DvtGraphParser.cATTR_X1IN)) ;
    rszb.setX1ScrollOutJS(node.getAttribute(DvtGraphParser.cATTR_X1OUT)) ;
    rszb.setY1ScrollInJS(node.getAttribute(DvtGraphParser.cATTR_Y1IN)) ;
    rszb.setY1ScrollOutJS(node.getAttribute(DvtGraphParser.cATTR_Y1OUT)) ;
    rszb.setY2ScrollInJS(node.getAttribute(DvtGraphParser.cATTR_Y2IN)) ;
    rszb.setY2ScrollOutJS(node.getAttribute(DvtGraphParser.cATTR_Y2OUT)) ;

    rszb.setX1ActualJS(node.getAttribute(DvtGraphParser.cATTR_X1ACTUAL)) ;
    rszb.setY1ActualJS(node.getAttribute(DvtGraphParser.cATTR_Y1ACTUAL)) ;
    rszb.setY2ActualJS(node.getAttribute(DvtGraphParser.cATTR_Y2ACTUAL)) ;
    rszb.setAllActualJS(node.getAttribute(DvtGraphParser.cATTR_ALLACTUAL)) ;

    // Create menu items for the JS events that are provided

    rszb.createMenu(this._Chart.isVertical());

    this._Chart.setZoomButton(rszb);
    
    // The zoom button is appended at the end in rendering in the chart

}


/*---------------------------------------------------------------------*/
/*   _parseRect()                                                      */
/*                                                                     */
/*   <r p="x,y,w,h" go="f" O="CB" G="" S="" ft="ftC" fc="#ffffff"      */
/*                                                     tt="f" sh=""/>  */
/*---------------------------------------------------------------------*/
/**
  *  Parses a &lt;r&gt; rectangle tag.
  *  @private
  *  @type DvtShape
  */
DvtGraphParser.prototype._parseRect = function(objId, node)
{
   var attr = node.getAttribute("p") ;
   var ar   = attr.split(',') ;
   ar = DvtArrayUtils.toFloat(ar) ;

   var rect ;
   var bImg = false ;

   attr = node.getAttribute('tURL') ;
   if (attr) {
     bImg = true ;
     rect = new DvtImage(this._ctx, attr, ar[0], ar[1], ar[2], ar[3]) ;
     }
   else {
     var bInitFx = (objId === DvtGraphObjType.BAR_RISER && this._Chart.isInitFx());
     var rect = (bInitFx || ((this._Chart.isDataAnimation() || this._Chart.isSelectionSupported()) &&
                            (objId === DvtGraphObjType.BAR_RISER             ||
                            objId === DvtGraphObjType.PIE_BAR_RISER         ||
                            objId === DvtGraphObjType.STOCK_RISING_RISER    ||
                            objId === DvtGraphObjType.STOCK_FALLING_RISER   ||
                            objId === DvtGraphObjType.STOCK_VOLUME_RISER)))?
                               new Dvt2DBarRiser(this._ctx, ar[0], ar[1], ar[2], ar[3]) :
                               new DvtRect(this._ctx, ar[0], ar[1], ar[2], ar[3]) ;
     if (bInitFx && rect.setInitFx) {
       rect.setInitFx(bInitFx);
     }
   }

   DvtGraphSysUtils.makeDetObject(rect, this._Chart, objId) ;

   this._parseBaseAttribs(rect, node, objId) ;

   if (this._Chart.isDataAnimation() && (rect instanceof Dvt2DBarRiser)) {
     rect._Dlo._DCHandler = new DvtDC2DBar(this._ctx, rect) ;
   } 
   
   // parse rx, ry values for DvtRect
   if (rect instanceof DvtRect) {
       var s = node.getAttribute("rx") ;
       if (s) {
           rect.setRx(parseFloat(s));
       }
       
       s = node.getAttribute("ry") ;
       if (s) {
           rect.setRy(parseFloat(s));
       }
   }

   //selection support - store related stock markers
   //so we can link them together later
   //NOTE: need to call _parseBaseAttribs before doing this 
   if (objId === DvtGraphObjType.STOCK_RISING_RISER  ||
       objId === DvtGraphObjType.STOCK_FALLING_RISER ||
       objId === DvtGraphObjType.STOCK_VOLUME_RISER)
   {
     this._storeStockMarker(rect._Dlo);
   }

   //  Save the ChartBackground & Frame rect in the Chart object for use by
   //  the DataZoom object for data zoom/scrolling when drawing the rubber
   //  band rect, and also for Drag and Drop.

   if (objId  === DvtGraphObjType.FRAME) {
      this._Chart.setFrameRect(ar[0], ar[1], ar[2], ar[3]) ;
   }
   else if (objId === DvtGraphObjType.CHARTBKGND)
   {
      this._Chart.setBGRect(ar[0], ar[1], ar[2], ar[3]) ;
   }
/*
   else if (objId == DvtGraphObjType.PLOT_AREA && (! m_bHavePlotArea))
   {
      chart.m_rectPlotX      = m_workRect.x ;
      chart.m_rectPlotY      = m_workRect.y ;
      chart.m_rectPlotWidth  = m_workRect.width ;
      chart.m_rectPlotHeight = m_workRect.height ;
      m_bHavePlotArea  = true ;
   }
*/
   else  if (objId === DvtGraphObjType.PIE_FRAME)
   {
     this._pieFrameX = ar[0] ;
     this._pieFrameY = ar[1] ;
     this._pieFrameW = ar[2] ;
     this._pieFrameH = ar[3] ;
   }

   if (objId === DvtGraphObjType.BAR_RISER) {
     this._addRiser(rect) ;
   }

   rect.setPixelHinting(objId === DvtGraphObjType.FRAME || 
                        objId === DvtGraphObjType.LEGEND_AREA         ||
                        objId === DvtGraphObjType.STOCK_RISING_RISER  ||
                        objId === DvtGraphObjType.STOCK_FALLING_RISER ||
                        objId === DvtGraphObjType.STOCK_VOLUME_RISER  ||
                        objId === DvtGraphObjType.BOX_BAR) ;
   
   if (objId === DvtGraphObjType.LEGEND_AREA) {
     this._Chart.setLegendRect(rect);
   }

   return rect ;
} ;



/*-------------------------------------------------------------------------------*/
/*  parseRectString()   Parses a string of the format "x,y,w,h" or "x1,y1,x2,y2" */
/*                      and returns the values in the supplied DvtRectangle or   */
/*                      the internal work rectangle if not.                      */
/*-------------------------------------------------------------------------------*/

/**  Parses a string of the format "x,y,w,h" or "x1,y1,x2,y2", and returns the
  *  values in the supplied DvtRectangle or the internal work rectangle if not.
  *  @private
  *  @param {String} s  The string to be parsed.
  *  @param {DvtRectangle} rect  The srectangle to updated. If omitted, the internal
  *  _workRect is updated.
  */
DvtGraphParser.prototype._parseRectString = function(s, rect)
{
   rect = rect? rect : this._workRect ;

   var ars   = s.split(DvtGraphParser.COMMA) ;
   var arf   = DvtArrayUtils.toFloat(ars) ;

   rect.x = arf[0];
   rect.y = arf[1];
   rect.w = arf[2];
   rect.h = arf[3];
};



/*-------------------------------------------------------------------*/
/*   _parseRefObjects()                                              */
/*-------------------------------------------------------------------*/
/**
  *  Parse the referenceObject declarations and chain to the chart.
  *  @private
  */
DvtGraphParser.prototype._parseRefObjects = function(obj, node)
{
   // Typical xml
   //    <m p="126,206,249,81,258,72" S="1" mt="c,8" fc="#cc3300">
   //      <ros> 
   //        <ro O="ROB" . . . M="2" /> 
   //        <ro M="3" /> 
   //      </ros>
   //    </m>


   var  aNodes = node.childNodes ;
   var  l      = aNodes.length ;
   var  i ;
   var  node ;
   var  mid ;
   var  s ;

   for (i = 0; i < l; i++)  {

      node = aNodes[i] ;
      if (node.nodeName !== 'ro')  {           // "refObj"
        continue ;
      }

      mid = this._parseMiscId(node) ;          // misc Id
      if (mid >= 0) {
        if (mid) {
          s = this._parseObjId(node) ;         // obj Id
          mid += s ;                           // concatenate objid and miscID

          this._addToReferredFrom(obj, mid) ;  // will resolve these at
                                               // completion of parsing
        }
      }
   }
};



//<scrollableLegend p="399.0,55.0,91.0,219.0" h="654"/>
DvtGraphParser.prototype._parseScrollableLegend = function(objId, node)
{
   var context = this._Chart.getCtx();
   var  attr ;
   var  ar ;

   attr = node.getAttribute('p') ;
   ar   = attr.split(',') ;
   ar   = DvtArrayUtils.toFloat(ar) ;

   var nLegendItems = (this._aLegends ? this._aLegends.length : 0) + (this._aMiscLegends ? this._aMiscLegends.length : 0);
   attr = node.getAttribute('h');
   var sl = new DvtScrollableLegend(context, ar[0], ar[1], ar[2], ar[3], (attr ? parseInt(attr) : 0), this._Chart._legendHandle, nLegendItems);
   if (DvtAgent.isTouchDevice())
    sl.setTouchManager(this._Chart.__getEventManager().getTouchManager());
   DvtGraphSysUtils.makeDetObject(sl, this._Chart, objId);
   this._Chart._scrollableLegend = sl;

   // set the series index of the scollable legend to -1, instead of the value set in parseBaseAttribs
   sl._Dlo.setSeriesIndex(-1);
   
   return sl ;
};

/*-------------------------------------------------------------------*/
/*    parseShapeAttributes()                                         */
/*-------------------------------------------------------------------*/
/**  Parse shape attributes into a ShapeAttribute object and chain to 
  *  the det object.
  *  @private
  */
DvtGraphParser.prototype._parseShapeAttributes = function(obj, node)
{
   if (! obj.isMouseEnabled())
     return ;

   var dlo = obj._Dlo ;
   
   // Parse into a DvtShapeAttr object and store on the detObj
   var sa = DvtShapeAttr.parse(this._Chart, node);
   dlo.setShapeAttr(sa);
   if (DvtShapeAttr.hasClickableShapeAttribute(sa)) {
       obj.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
   }
   
   // If we have alt text, use it as a tooltip. Note: alt text takes precedence
   // over any defined tooltip for the detobject.
   if(sa && sa.alt) {
     dlo.setTooltipText(sa.alt) ;
     dlo.setUseTooltip(true) ;
   }

   // If there is a link="t" attribute, then if the parent is a <text> tag will
   // change the text color to blue.
   if (sa.bLink && obj instanceof DvtText) {
     var alpha = obj.getFill() ? obj.getFill().getAlpha() : 1;
     obj.setSolidFill(this._linkColor, alpha);
   }
};


/*---------------------------------------------------------------------*/
/*   _parseShowPopupBehavior()                                         */
/*---------------------------------------------------------------------*/
/**
 * Parses a show popup behavior definition.
 */
DvtGraphParser.prototype._parseShowPopupBehavior = function(node) {
  // Parse the behavior and add it to the chart.  
  // Graph doesn't use the DvtXmlNode, so we need to call getAttribute on the DOM XML node.
  var popupId = node.getAttribute("popupId");
  var triggerType = node.getAttribute("triggerType");
  var alignId = node.getAttribute("alignId");
  var align = node.getAttribute("align");
  var spb = new DvtGraphShowPopupBehavior(popupId, triggerType, alignId, align);
  var behaviors = this._Chart.__getShowPopupBehaviors();
  behaviors.push(spb);
};


/*---------------------------------------------------------------------*/
/*   _resolveRefObjLinkage()                                           */
/*---------------------------------------------------------------------*/
/**
  *   Link referencing to referenced objects.
  *   @private
  */
DvtGraphParser.prototype._resolveRefObjLinkage = function()
{
  if ((! this._aRefFrom) || (! this._aRefTo)) {
    return ;
  }

  var  lFrom  = this._aRefFrom.length ;
  var  lTo    = this._aRefTo.length ;
  var  i ;
  var  j ;

  var  detFrom ;
  var  detTo ;
  var  objTo ;

  var  sIDFrom ;
  var  sIDTo ;
  var  aRO = null ;

  for (i = 0; i < lFrom; i++)  {

     detFrom  = this._aRefFrom[i++]._Dlo ;
     sIDFrom  = this._aRefFrom[i] ;

     if (! (aRO = detFrom.getRefObjs())) {
       aRO = [] ;
       detFrom.setRefObjs(aRO) ;
     }

     for (j = 0; j < lTo; j++)  {

        objTo =  this._aRefTo[j] ;
        detTo =  objTo._Dlo ;
        sIDTo =  '' + detTo.getMiscId() + detTo.getObjId() ;

        if (sIDFrom === sIDTo)  {

           aRO.push(objTo) ;                  // push ref obj in referers list
           detTo.setRefObj(true) ;            // mark referred to obj as a referenced obj
           break ;
        }
     }
  }

  //  Finished with the parser's ref ohject arrays

  this._aRefFrom = null ;
  this._aRefTo   = null ;

}


/*---------------------------------------------------------------------*/
/*  _parseSeriesIndex()                                                */
/*---------------------------------------------------------------------*/
/**
 *   Parses the node for a S= attribute, and updates the cache.
 *   If not found the current cached value is returned. Returns -1 if the
 *   the index is invalid. 
 *   @private
 *   @type number
 */
DvtGraphParser.prototype._parseSeriesIndex = function(node)
{
   var s = node.getAttribute("S") ;   // series index
   if (s !== null) {
     if (s.length === 0) {            // empty string means clear the cache
       s = null ;
       this._cacheSeriesIndex = -1 ;
       this._cacheSeriesId    = null ;
       return -1 ;
     }
   }
   else // new value not specified, used from cache
       return this._cacheSeriesIndex ;

   var n = parseInt(s, 10) ;
   if (isNaN(n)) {
     n = -1 ;
   }   

   this._cacheSeriesIndex = n ;                                    // cache new series index
   if ((n >= 0) && (n < this._Data.getSeriesCount())) {
         this._cacheSeriesId  = this._Data.getSeriesFromIndex(n) ; // cache new series str.
   }
   else {

       this._cacheSeriesIndex = n ;
       this._cacheSeriesId    = null ;
   }

   return n ;

};


/*---------------------------------------------------------------------*/
/*  _parseShadow()                                                     */
/*---------------------------------------------------------------------*/
/**
 *   Parses the node for a sh= attribute, and updates the cache.
 *   @private
 */
DvtGraphParser.prototype._parseShadow = function(node)
{
   var attr = node.getAttribute('sh') ;
   if (attr !== null)
   {
      if (attr.length === 0) {           // sh="" means clear the cache
        this._cacheShadow    = null ;
        this._cacheShadowObj = null ;
      }
      else if (attr !== this._cacheShadow) {   // new spec?
        this._cacheShadow    = attr ;          // cache the string & obj
        this._cacheShadowObj = this._parseShadowAttrs(attr) ;
      }
   }
};


/*---------------------------------------------------------------------------*/
/*   _parseShadowAttr()      Parse a dropshadow  sh= attribute               */
/*---------------------------------------------------------------------------*/
/*                                                                           */
/*  The following description is of the Flash DropShadowFilter values. Other */
/*  may need fewer values.                                                   */
/*                                                                           */
/*  Format of sh attribute                                                   */
/*  ----------------------                                                   */
/*                                                                           */
/*  sh="color/alpha,distance,strength,angle,blurX,blurY,quality,inner,       */
/*                                                            knockout,hide" */
/*                                                                           */
/*  where any of the individual values can be omitted as long as a separator */
/*  comma is still specified.                                                */
/*                                                                           */
/*  e.g.  sh="#59333333,,3"      here distance is omitted and will default.  */
/*                                                                           */
/*                                                                           */
/* Arguments                                                                 */
/* ---------                                                                 */
/*                                                                           */
/* color/alpha  - the color and transparency of the shadow.  Default is      */
/*                0x59333333.                                                */
/* distance     - the offset distance for the shadow, in pixels (float).     */
/*                Default is 4.                                              */
/* strength     - the strength of the imprint or spread (float). Default is  */
/*                1. Note: The higher the value, the more color is imprinted */
/*                and the stronger the contrast between the shadow and the   */
/*                background. Valid values are from 0 to 255.0               */
/* angle        - the angle of the shadow. (floating point). Valid values    */
/*                are 0 to 360.0 degrees.  Default is 45 deg.                */
/* blurX        - the amount of horizontal blur (float). Valid values are 0  */
/*                to 255.0  Default is 4.                                    */
/* blurY        - the amount of vertical blur (float). Valid values are 0 to */
/*                255.0  Default is 4.                                       */
/* quality      - the number of times to apply the effect (int). Valid       */
/*                values are 0 to 15.  Default is 1.  Note: shadows with     */
/*                lower values are rendered more quickly. For most applic-   */
/*                ations, a quality value of 1 (low), 2 (medium), or 3       */
/*                (high) is sufficient.  Although you can use additional     */
/*                numeric values up to 15 to achieve different effects,      */
/*                higher values are rendered more slowly. Instead of inc-    */
/*                reasing the value of quality, you can often get a similar  */
/*                effect, and with faster rendering, by simply increasing    */
/*                the values of the blurX and blurY properties.              */
/* inner        - boolean value indicating whether or not the shadow is an   */
/*                inner shadow. A true value of "t" or "1" specifies an      */
/*                inner shadow. Any other value (indicating false) specifies */
/*                an outer shadow (a shadow around the outer edges of the    */
/*                object).                                                   */
/* knockout     - boolean value that applies a knockout effect if true (that */
/*                is if  "t" or "1"), which effectively makes the object's   */
/*                fill transparent, and reveals the background color of the  */
/*                document.                                                  */
/* hide         - boolean value indicating whether or not the object is      */
/*                hidden. A true value of "t" or "1" indicates that the      */
/*                object itself is not drawn; only the shadow is visible.    */
/*                For any other value (which indicates false) the object is  */
/*                shown.                                                     */
/*---------------------------------------------------------------------------*/
/**
  *   Parses a shadow attribute sh= .
  *   @private
  */
DvtGraphParser.prototype._parseShadowAttrs = function(sh)
{
   if (! sh) {
     return new DvtShadow() ;
   }

   var rgba      = null ;
   var distance  = 4 ;
   var strength   = 1 ;
   var angle     = 45 ;      //BUG FIX #12661565: default angle should be 45
   var blurX     = 4 ;
   var blurY     = 4 ;
   var quality   = 1 ;
   var bInner    = false ;
   var bKnockout = false ;
   var bHide     = false ;

   var  ar  =  sh.split(',') ;
   var  len = ar.length ;
   var  n ;

   for (var i = 0; i < len; i++)
   {

     var  n ;
     var sVal = ar[i] ;
     if ((! sVal) || (sVal.length === 0)) {
       continue ;            // use default
     }

     switch(i)
     {
        case 0:rgba = DvtColorUtils.getRGBA(sVal) ; // arg1 is color (& optionally alpha)
                  break ;
        case 1:
                  n = parseFloat(sVal) ;  // arg 2 is distance
                  if (! isNaN(n)){
                    distance = n ;
                  }
                  break ;

        case 2:n = parseFloat(sVal) ;  // arg 3 is strength
                  if (! isNaN(n)) {
                    strength = n ;
                  }
                  break ;

        case 3:n = parseFloat(sVal) ;  // arg 4 is angle
                  if (! isNaN(n)) {
                    angle = n ;
                  }
                  break ;

        case 4:n = parseFloat(sVal) ;  // arg 5 is blurX
                  if (! isNaN(n)) {
                    blurX = n ;
                  }
                  break ;

        case 5:n = parseFloat(sVal) ; // arg 6 is blurY
                  if (! isNaN(n)) {
                    blurY = n ;
                  }
                  break ;

        case 6:n = parseInt(sVal, 10) ;  // arg 7 is quality
                  if (! isNaN(n)) {
                    quality = n ;
                  }
                  break ;

        case 7:   // arg 8 is inner shadow (boolean)
                  bInner = (sVal.charAt() == DvtGraphParser.TRUE || sVal.charAt() == "1") ;
                  break ;

        case 8:   // arg 9 is knockout (boolean)
                  bKnockout = (sVal.charAt() == DvtGraphParser.TRUE || sVal.charAt() == "1") ;
                  break ;

        case 9:   // arg 10 is hide (boolean)
                  bHide = (sVal.charAt() == DvtGraphParser.TRUE || sVal.charAt() == "1") ;
                  break ;
     }  // end switch
   }  // end for

   //alert("shadow clr = " + rgba + " dist=" + distance + " blurX=" + blurX  + " blurY=" + blurY ) ;

   return new DvtShadow(rgba, distance, blurX, blurY, angle, strength, quality, bInner, bKnockout, bHide) ;

} ;


/*---------------------------------------------------------------------*/
/*   _parseText()                                                      */
/*---------------------------------------------------------------------*/
/**
  *  Parses a &lt;t&gt; text tag.
  *  @private
  *  @type DvtShape
  */
DvtGraphParser.prototype._parseText = function(objId, node)
{
   // <t p="78,287,45" O="O1L" G="0" fid="1" a="0.0" j="c">Group A</t>

   //alert("parseText " + objId) ;

   var  attr ;
   var  ar ;
   var  sText ;
   var  color ;
   var  sWidth ;
   var  xPos ;

   attr   = node.getAttribute("p") ;
   ar     = attr.split(',') ;
   ar     = DvtArrayUtils.toFloat(ar) ;
   xPos   = ar[0] ;
   sWidth = ar[2] ;
   sText  = node.textContent ;

   attr = node.getAttribute("a") ;                // angle
   if (attr) {
     this._cacheTextAngle = (attr !== null ? parseFloat(attr) : 0) ;  // cache the new value
   }

   var fontId = this._parseFontId(node) ;
   var font   = this._Chart.getFont(fontId) ;
   var text   = new DvtText(this._ctx, sText, xPos, ar[1]) ;
   DvtGraphSysUtils.makeDetObject(text, this._Chart, objId) ;
   
   var dlo  = text._Dlo ;

   DvtTextUtils.applyFont(text, font);

   if (this._cacheTextAngle !== 0) {
     text.doNotUseSetAngle(this._cacheTextAngle) ;
   }

   var sAlign = node.getAttribute("j") ;       // justification
   if (sAlign) {
     if (this._bBiDi) {
         if (this._cacheTextAngle == 0) {
             if (sAlign == 'r') {
                 sAlign = 'l';
             } else if (sAlign == 'l') {
                 sAlign = 'r';
             }
         }
     }
     if (sAlign !== 'l' && sAlign !== 'c' && sAlign !== 'r') {
       sAlign = 'l' ;
     }
     this._cacheTextJustify = sAlign ;
   }
   else {
     sAlign = this._cacheTextJustify ;
   }

   //  Adjust x if text is justified
   if (sAlign !== 'l') {
    // Bypass the text size remediation for non-browser testing environments
    if(document.body != null) {
      var r = this._getTextSize(sText, font, new DvtRectangle()) ;
      if (sAlign === 'r') {
       xPos = xPos + sWidth - r.width ;
      }
      else {
       xPos = (xPos + sWidth/2) - (r.width/2)  ;
      }
    }
    
    text.setX(xPos) ;
   }

   //text.setAlign(sAlign) ;                //TDO

   if (this._cacheTextAngle !== 0) {

     var cx = node.getAttribute("cx") ;
     cx = (cx !== null ? parseFloat(cx) : 0) ;
     var cy = node.getAttribute("cy") ;
     cy = (cy !== null ? parseFloat(cy) : 0) ;
     text.doNotUseSetAngle(this._cacheTextAngle) ;
     text.doNotUseSetRotatePoint(cx, cy) ;
   }

   this._parseSeriesIndex(node) ;
   dlo.setSeriesIndex(this._cacheSeriesIndex) ;
   dlo.setSeriesId(this._cacheSeriesId) ;
   this._parseGroupIndex(node) ;
   dlo.setGroupIndex(this._cacheGroupIndex) ;
   dlo.setGroupId(this._cacheGroupId) ;

   var n = this._parseMiscId(node) ;          // misc Id
   if (n >= 0) {
     dlo.setMiscId(n) ;
   }

   this._checkTooltipTemplate(node) ;   // cache value if tt= attrb found.
   this._parseMouseTransparency(node) ;
   text.setMouseEnabled(! this._bCacheMouseTransparency) ;

   if (!this._bCacheMouseTransparency) { //Bug 12371782
        this._parseTooltip(node, text);
    }

   if (objId === DvtGraphObjType.LEGEND_TEXT) {
     this._addLegend(text) ;

     if (this._Chart.isDataAnimation()) {
       text._Dlo._DCHandler = new DvtDCText(this._ctx, text) ;
     }
   }
   


   //  Look for child nodes of the object, such as <ros> and <sa>.

   if (node.hasChildNodes()) {
     this._parseBaseChildNodes(node, text, true) ;
   }
   
   // Set drilling on a shape attribute
   attr = node.getAttribute('drillJs');
   if (attr) {
       this._parseDrill(text, attr);
   }
   
   // Parse user props
   attr = node.getAttribute("Z");
   if (attr) {
     dlo.setUserProps(attr);
   }

   //  Classify text types

   if (objId === DvtGraphObjType.O1_LABEL ||           // x-axis labels
       objId === DvtGraphObjType.X1_LABEL) {
     if (! this._aXLabels) {
       this._aXLabels = [] ;
     }
     this._aXLabels.push(text) ;
     if (this._Chart.isDataAnimation()) {
       text._Dlo._DCHandler = new DvtDCText(this._ctx, text) ;
     }
   }
   else if (objId === DvtGraphObjType.Y1_LABEL) {      // y1-axis labels
     if (! this._aY1Labels) {
       this._aY1Labels = [] ;
     }
     this._aY1Labels.push(text) ;
   }
   else if (objId === DvtGraphObjType.Y2_LABEL) {      // y2-axis labels
     if (! this._aY12abels) {
       this._aY2Labels = [] ;
     }
     this._aY2Labels.push(text) ;
   }
   else if (objId === DvtGraphObjType.LEGEND_TEXT && this._HSS) {
       text.setCursor(DvtSelectionEffectUtils.getSelectingCursor());
   }

   return text ;
} ;



/*---------------------------------------------------------------------*/
/*   _parseTimeSelector()                                              */
/*---------------------------------------------------------------------*/
/**
  *  Parses a &lt;r&gt; rect tag with time selector attributes.
  *  @private
  *  @type DvtTimeSelector
  */
DvtGraphParser.prototype._parseTimeSelector = function(objId, node)
{
   // <r p="69,46,151,216" go="f" O="TimeSelector" fc="#22007777" bc="#007777"
   //    tsJS="AdfPage.PAGE.findComponent(&#39;demoTemplate:masterGraph&#39;).getPeer().sendToServerPPR({source:&#39;demoTemplate:masterGraph&#39;,event:&#39;bi_gtse&#39;,bi_gtsS:&#39;__START_DATE__&#39;,bi_gtsE:&#39;__END_DATE__&#39;});"
   //    tasd="1.1096532E12" taed="1.146456E12" tasp="27" taep="326" tranS="Start: {0}"
   //    tranE="End: {0}" taR="Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec,AM,PM,f" 
   //    TZ="-240" t="Start: May 2005End: December 2005"/>


   var  attr ;
   var  ar ;

   attr = node.getAttribute('p') ;
   ar   = attr.split(',') ;
   ar   = DvtArrayUtils.toFloat(ar) ;

   var  ts = new DvtTimeSelector(this._ctx, ar[0], ar[1], ar[2], ar[3], objId) ;  // TODO: need something else for the Id?
   if (DvtAgent.isTouchDevice())
    ts.setTouchManager(this._Chart.__getEventManager().getTouchManager());
   DvtGraphSysUtils.makeDetObject(ts, this._Chart, objId) ;

   var dlo = ts._Dlo

   this._parseBaseAttribs(ts, node, objId) ;

   attr = node.getAttribute('tasd') ;
   if (attr) {
     ts.setTimeAxisStartDate(parseFloat(attr)) ;
   }
   attr = node.getAttribute('taed') ;
   if (attr) {
     ts.setTimeAxisEndDate(parseFloat(attr)) ;
   }

   attr = node.getAttribute('tasp') ;
   if (attr) {
     ts.setTimeAxisStartPixel(parseInt(attr)) ;
   }

   attr = node.getAttribute('taep') ;
   if (attr) {
     ts.setTimeAxisEndPixel(parseInt(attr)) ;
   }

   attr = node.getAttribute('taR');
   if (attr) {
     ts.setTimeAxisMonths(attr.split(','));
   }

   attr = node.getAttribute('timeZone');
   if (attr) {
     ts.setTimeZoneOffset(parseInt(attr));
   }

   ts.setTimeAxisEventJs(node.getAttribute('taJS')) ;
   ts.setTimeSelectorEventJs(node.getAttribute('tsJS')) ;
   ts.setTranslatedStart(node.getAttribute('tranS')) ;
   ts.setTranslatedEnd(node.getAttribute('tranE')) ;

   return ts ;
} ;


/*-------------------------------------------------------------------*/
/*   _parseTooltip()                                                 */
/*-------------------------------------------------------------------*/
/**
  *  Parse t= attribute, and update the object's DLO.  Also enables chart
  *  tooltip support if required.
  *  @private
  */
DvtGraphParser.prototype._parseTooltip = function (node, obj) {

  var  bNeedTooltips;
  var  dlo = obj._Dlo ;

  var attr = node.getAttribute("t") ;    // is a specific tooltip defined (t="tooltip text")
  if (attr) {
    if (attr.replace(new RegExp(DvtGraphParser.MD_TT_SEPARATOR, "g"), '').length > 0) {
        dlo.setTooltipText(attr.replace(new RegExp(DvtGraphParser.MD_TT_SEPARATOR, "g"), '\n')) ;
        dlo.setUseTooltip(true) ;
        bNeedTooltips = true ;
    }
  }
  else  {
     bNeedTooltips = this._bCacheUseTooltipTemplate ;
     dlo.setUseTooltip(bNeedTooltips) ;
  }
} ;


/*-------------------------------------------------------------------*/
/*   _parseTooltipFormat()                                           */
/*-------------------------------------------------------------------*/
/**
  *   @private
  */
DvtGraphParser.prototype._parseTooltipFormat = function(node)
{
  //  TDO   JRM

   var justify = 'l' ;                // default is left

   var s = node.getAttribute('j') ;   // justification
   if (s) {
     justify = s.charAt(0).toLowerCase() ;
   }
   this._cacheTextJustify = justify ;

   var fid = this._parseFontId(node) ;
   this._Chart.setTooltipFontId(fid);


/*
   s = node.attributes["fid"] ;        // is a font id specified
   if (s != null)
   {
      var  fid:uint = parseInt(s) ;

      if (fid < 0)
        fid = 0 ;                      // bad - use the first one.

      m_currentFontID = fid ;          // note current font
      fontObj = getFontObjectByID(fid) ;
   }
   else
   {
      if (m_currentFontID != -1)
      {
         fontObj = getFontObjectByID(m_currentFontID) ;
      }
      else
      {
         fontObj = new OraFont() ;

         // default it
         // the "spec" was not specified correctly - so a system device font is used here!

         fontObj.m_name      = "_sans" ;
         fontObj.m_size      = 10 ;
         fontObj.m_color     = 0x333333 ;
         fontObj.m_alpha     = 1 ;
         fontObj.m_style     = 0 ;
         fontObj.m_bEmbedded = false ;
      }
   }

   ttMgr.setFormat(fontObj, justify);
*/
};



/*--------------------------------------------------------------------------*/
/*   parseValueFormat()                                                     */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  e.g.        note: for clarity, each ~; separator is shown as |          */
/*                                                                          */
/*  XVF="0|.|,|K|M|B|T|Q|||(|)|3|2|2|1|100|0|t|0|NaN|f"                     */
/*                                                                          */
/*  XVF="0~;         [ 0]     scaling multiplier                            */
/*       .~;         [ 1]     decimal separator                             */
/*       ,~;         [ 2]     group separator                               */
/*       K~;         [ 3]     scaling symbol (thousands)                    */
/*       M~;         [ 4]       ..      ..   (millions)                     */
/*       B~;         [ 5]       ..      ..   (billions)                     */
/*       T~;         [ 6]       ..      ..   (trillions)                    */
/*       Q~;         [ 7]       ..      ..   (quadrillions)                 */
/*       ~;          [ 8]     positive prefix                               */
/*       ~;          [ 9]     positive suffix                               */
/*       (~;         [10]     negative prefix                               */
/*       )~;         [11]     negative suffix                               */
/*       3~;         [12]     groupSize                                     */
/*       2~;         [13]     minFractionLength                             */
/*       2~;         [14]     maxFractionLength                             */
/*       1~;         [15]     minIntegerLength                              */
/*       100~;       [16]     maxIntegerLength                              */
/*       0~;         [17]     zeroPadSymbol                                 */
/*       t~;         [18]     showScaleSymbol (boolean)                     */
/*       ~;          [19]     invalidValue                                  */
/*       ~;          [20]     base multiplier                               */
/*       ~;          [21]     isPowerPlusTwo (boolean)                      */
/*--------------------------------------------------------------------------*/
/**
  *  Parses a ValueFormat definition.  Always returns a ValueFormat even if
  *  the supplied string is null or empty (in which case the ValueFormat
  *  is marked as default).
  *  @private
  *  @param {String}  sAtb  the value format information to be parsed.
  *  @returns {DvtValueFormat}  a DvtValueFormat object.
  */
DvtGraphParser.prototype._parseValueFormat = function(sAtb)
{

  var vf ;

  if (sAtb == undefined || sAtb === null || sAtb.length === 0) {
     vf = new DvtValueFormat() ;  // use a default formatter.
  }
  else  {
     var  nMultiplier ;
     var  iGroupSize ;
     var  iMinFractLen ;
     var  iMaxFractLen ;
     var  iMinIntLen ;
     var  iMaxIntLen ;
     var  bShowScaleSymbol ;
     var  nBaseMultiplier ;
     var  bIsPowerPlusTwo ;

     var ar = sAtb.split(DvtGraphParser.MD_VF_SEPARATOR) ;

     while (ar.length < 22) {                  // bullet-proofing -
       ar.push('') ;                           // avoid array index errors
     }                                         // if any trailing values missing.

     nMultiplier      = parseFloat(ar[0]) ;    // note: NaN means SF_AUTO
     iGroupSize       = parseInt(ar[12], 10) ;
     iMinFractLen     = parseInt(ar[13], 10) ;
     iMaxFractLen     = parseInt(ar[14], 10) ;
     iMinIntLen       = parseInt(ar[15], 10) ;
     iMaxIntLen       = parseInt(ar[16], 10) ;
     bShowScaleSymbol = (ar[18].charAt(0)  === DvtGraphParser.TRUE) ;
     nBaseMultiplier  = parseFloat(ar[20]) ;
     bIsPowerPlusTwo  = (ar[21].charAt(0)  === DvtGraphParser.TRUE) ;

     vf = new DvtValueFormat(nMultiplier,  // scaling multiplier
                            ar[1],        // decimal separator symbol
                            ar[2],        // group separator symbol
                            ar[3],        // scaling K symbol
                            ar[4],        // scaling M symbol
                            ar[5],        // scaling B symbol
                            ar[6],        // scaling T symbol
                            ar[7],        // scaling Q symbol
                            ar[8],        // positive prefix
                            ar[9],        // positive suffix
                            ar[10],       // negative prefix
                            ar[11],       // negative suffix
                            iGroupSize,
                            iMinFractLen,
                            iMaxFractLen,
                            iMinIntLen,
                            iMaxIntLen,
                            ar[17],       // "zero" pad symbol
                            bShowScaleSymbol,
                            ar[19],       // invalid value symbol
                            nBaseMultiplier,
                            bIsPowerPlusTwo) ;
  }

  return vf ;
} ;


/*--------------------------------------------------------------------------*/
/*  _resolveClipRects()   Apply clip rects to any detobjects that requested */
/*                        clipping but for which the clipPath had not yet   */
/*                        been defined.                                     */
/*--------------------------------------------------------------------------*/
/**
  *   Apply clip rects to any det objects that requested clipping but for
  *   which the clipPath had not yet been defined.
  *   @private
  */
DvtGraphParser.prototype._resolveClipRects = function()
{
    if (this._aForClipResolution !== null)
    {
       var  ar  = this._aForClipResolution ;
       var  len = this._aForClipResolution.length ;

       for (var i = 0; i < len; i++) {

          // Array entries are in pairs: [0] = clipID, [1] = DetObj
          
          ar[1]._Dlo._clipPath = this._Chart.getClipPath(ar[0]) ;
          i++ ;
       }

       this._aForClipResolution = null ;      // done with this array.
    }   
};


/**
 * @private
 * Link related faces of 3D pie/ring slices together, for selection support.
 */
 DvtGraphParser.prototype._resolveRelatedPie3DFaces = function()
 {
   var key;
   for (key in this._pie3DFacesMap)
   {
     var pie3DFaceArray = this._pie3DFacesMap[key];
     
     for (var iFace = 0; iFace < pie3DFaceArray.length; iFace++)
     {
       var currIFace = pie3DFaceArray[iFace];
       
       for (var jFace = 0; jFace < pie3DFaceArray.length; jFace++)
       {
         if (iFace === jFace)
           continue;
           
           var currJFace = pie3DFaceArray[jFace];
           if (currIFace.addRelatedObject)
           {
             currIFace.addRelatedObject(currJFace);
           }
        }
     }
   }
 };


/**
 * @private
 * Link related parts of stock markers together, for selection support.
 */
DvtGraphParser.prototype._resolveRelatedStockMarkers = function()
{
  var key;
  for (key in this._stockMarkersMap)
  {
    var stockMarkerArray = this._stockMarkersMap[key];
    
    for (var iMarker = 0; iMarker < stockMarkerArray.length; iMarker++)
    {
      var currIMarker = stockMarkerArray[iMarker];

      for (var jMarker = 0; jMarker < stockMarkerArray.length; jMarker++)
      {
        if (iMarker === jMarker)
          continue;

        var currJMarker = stockMarkerArray[jMarker];
        if (currIMarker.addRelatedObject)
        {
          currIMarker.addRelatedObject(currJMarker);
        }
      }
    }
  }
};


/*--------------------------------------------------------------------------*/
/*   _setAnalyticLayerVals()                                                */
/*--------------------------------------------------------------------------*/
/**
  *   @private
  */
DvtGraphParser.prototype._setAnalyticLayerVals = function(dlo, layerType, layerColor)
{  
   var type = DvtMarker.convertShapeString(layerType);
   if (type) {
      dlo.setUnderlayType(type); 
        if (layerColor)  {
            dlo.setUnderlayFC(layerColor) // layerColor is #aarrggbb
        }
        else  {
            dlo.setUnderlayFC(DvtColorUtils.setAlpha(defAnLayColor, defAnLayalpha));
        }
      }
} ;


/**
 * @private
 */
 DvtGraphParser.prototype._storePie3DFace = function(detObj)
 {
   //key for the related faces map is a combination of group and series 
   var pie3DMapKey = detObj.getGroupId() + "," + detObj.getSeriesId();
   //see if we've already stored any of the faces of this slice;
   //if not, create a new array to hold them
   var array = this._pie3DFacesMap[pie3DMapKey];
   if (!array)
   {
     array = [];
   }
   //add this face to the array
  array.push(detObj.getDetObj());
  //store the array in the map, in case we created a new array
  this._pie3DFacesMap[pie3DMapKey] = array;
     
 };


/**
 * @private
 */
DvtGraphParser.prototype._storeStockMarker = function(detObj)
{
  //key for the related map is group
  //NOTE: can't use combination of group and series because stock volume
  //markers are in their own series, separate from other stock marker parts;
  //this should be okay because stock graphs are meant to only show
  //a single series of stock data anyway
  var mapKey = detObj.getGroupId();
  //see if we've already stored any of the objects;
  //if not, create a new array to hold them
  var array = this._stockMarkersMap[mapKey];
  if (!array)
  {
    array = [];
  }
  //add this face to the array
  array.push(detObj.getDetObj());
  //store the array in the map, in case we created a new array
  this._stockMarkersMap[mapKey] = array;
};


//BUG FIX #12427741: 
/**
 * @private
 * Store a shadow to apply after the object is added to the display list, 
 * because we need to be able to get the bounding box for the object.
 */
DvtGraphParser.prototype._storeShadow = function(detObj, shadow)
{
  if (!this._shadowMap)
  {
    this._shadowMap = [];
  }
  //add this obj,shadow pair to the array
  this._shadowMap.push(detObj);
  this._shadowMap.push(shadow);
};


//BUG FIX #12427741: 
/**
 * @private
 * Store a bevel to apply after the object is added to the display list, 
 * because we need to be able to get the bounding box for the object.
 */
DvtGraphParser.prototype._storeBevel = function(detObj, bevel)
{
  if (!this._bevelMap)
  {
    this._bevelMap = [];
  }
  //add this obj,shadow pair to the array
  this._bevelMap.push(detObj);
  this._bevelMap.push(bevel);
};


//  JRM. To be moved once I figure out what's going on with justified group text.

DvtGraphParser.prototype._getTextSize = function(sText, font, pRect) {

  var tmdiv = document.createElement('DvtTM');

  document.body.appendChild(tmdiv);

  var style = '' ;
 
   if (font.italic)  {
     style = 'font-style:italic; ' ;
   }
  else {
  style = 'font-style:normal; ' ;
  }
   if (font.bold)  {
     style += 'font-weight:bold; ' ;
   }
   if (font.underline)  {
     style += 'text-decoration:underline; ' ;
   }

  var name = font.name? font.name: "tahoma, sans-serif";
  style += 'font-family:' + name + ';';

  tmdiv.style.cssText          =  style ;
  tmdiv.style.fontSize = '' + font.size + 'px';
  tmdiv.style.position = 'absolute';
  tmdiv.style.left     = -5000;
  tmdiv.style.top      = -5000;
  tmdiv.style.width    = 'auto';
  tmdiv.innerHTML      = sText;

  if (! pRect) {
    pRect = new DvtRectangle() ;
  }
  pRect.width  = tmdiv.clientWidth ;
  pRect.height = tmdiv.clientHeight ;

  document.body.removeChild(tmdiv);

  return pRect ;
};

DvtGraphParser.prototype._parseDataCursor = function(node)
{
    var chart = this._Chart;
    var dc = chart.getDataCursor();
    var behavior = node.getAttribute('behavior');
    if (behavior) {
        dc.setBehavior(behavior);        
    }
    var display = node.getAttribute('display');
    if (display)
        chart.setDataCursorDisplay(display);
    var lineColor = node.getAttribute('lineFillColor');
    if (lineColor)
        dc.setLineColor(lineColor);
};

DvtGraphParser.prototype._parseMagnifyLens = function(node)
{
    var chart = this._Chart;
    var magnifyLens =  this._ctx.getCustomTooltipManager().getMagnifyLens();
    var display = node.getAttribute('display');
    if (display)
        chart.setMagnifyLensDisplay(display);
    var borderColor = node.getAttribute('rimFillColor');
    if (borderColor)
        magnifyLens.setRimFillColor(borderColor);
};

DvtGraphParser.prototype._parseResources = function()
{
    var chart = this._Chart;
    var resources = new Object();
    resources[DvtGraphCore.HIDE_KEY] = "Hide";
    resources[DvtGraphCore.SHOW_KEY] = "Show";
    resources[DvtGraphCore.CLEAR_SELECTION_KEY] = "Clear Selection";
    chart.setTextResources(resources);
};

/**
 * Converts the internal line breaks used by the graph into real line breaks.
 * @param {string} str
 * @return {string}
 * @private
 */
DvtGraphParser.convertLineBreaks = function(str) {
  if(str)
    return str.replace(new RegExp(DvtGraphParser.NEW_LINE, "g"), '\n');
}
// Copyright (c) 2008, 2011, Oracle and/or its affiliates. 
// All rights reserved. 
/*---------------------------------------------------------------------*/
/*  DvtBaseAxis            Base axis utility object                    */
/*---------------------------------------------------------------------*/
/**
 *  Base class for axis objects used by DvtScrollbar
 *  @extends DvtObj
 *  @class DvtBaseAxis is the base class for all axis objects (derived from {@link DvtBaseAxis}).
 *  Must be superclassed  - do not use directly.
 *  <code>
 *  @constructor
 */
var DvtBaseAxis = function (axisMinCoord, axisMaxCoord, width, height, isHoriz) {
  this.Init(axisMinCoord, axisMaxCoord, width, height, isHoriz);
};

DvtObj.createSubclass(DvtBaseAxis, DvtObj, "DvtBaseAxis");

/**
  *  Object initializer.
  *  @protected 
  */
DvtBaseAxis.prototype.Init = function (axisMinCoord, axisMaxCoord, width, height, isHoriz) {
/*
  this._impl.setObj(this);   // point implementation back to the real
  // controlling object.
  this._context = context;
*/
    this._axisMinCoord = axisMinCoord;
    this._axisMaxCoord = axisMaxCoord;
    this._axisLength = axisMaxCoord - axisMinCoord;
    this._width = width;
    this._height = height;
    this._isHoriz = isHoriz;

};


/**
 *  Returns the ratio along the axis for the specified coordinate.
 *
 *  @param  coord  the coordinate on the axis
 *  @return the ratio along the axis (0..1)
 */
DvtBaseAxis.prototype.calcRatio = function(coord) {
    
    var ratio = (coord - this._axisMinCoord) / (this._axisMaxCoord - this._axisMinCoord);

    if (ratio < 0.0)
        ratio = 0.0;
    else if (ratio > 1.0)
        ratio = 1.0;
        
    // BUG #12805191: ratio needs to be inverted in vertical axes due to reversal in y coordinate systems
    if (!this._isHoriz)
        ratio = 1 - ratio;
    return ratio;
}
// Copyright (c) 2008, 2011, Oracle and/or its affiliates. 
// All rights reserved. 
/*---------------------------------------------------------------------*/
/*  DvtCustomAxis            Custom axis object                        */
/*---------------------------------------------------------------------*/
/**
 *  An axis for displaying custom values.
 *  @extends DvtBaseAxis
 *  @class DvtCustomAxis is the class for displaying custom values.
 *  <code>
 *  @constructor
 */
var DvtCustomAxis = function (customLabels, axisMinCoord, axisMaxCoord,
                              width, height, isHoriz) {
    this.Init(customLabels, axisMinCoord, axisMaxCoord, width, height, isHoriz);
};

DvtObj.createSubclass(DvtCustomAxis, DvtBaseAxis, "DvtCustomAxis");


/**
  *  Object initializer.
  *  @protected
  */
DvtCustomAxis.prototype.Init = function (labels, axisMinCoord, axisMaxCoord, width, height, isHoriz) {

    DvtCustomAxis.superclass.Init.call(this, axisMinCoord, axisMaxCoord, width, height, isHoriz);

    this._labels = labels;

}


/**
 * Returns the label at the specified index.
 *
 * @param  index  the index of the label
 * @return the string for the label at the specified index
 */
DvtCustomAxis.prototype.getLabelAt = function(index) {
    if (this._labels && this._labels.length > index)
        return this._labels[index];
    else
        return "";
}


/**
 * Returns the number of labels in this DvtCustomAxis object.
 */
DvtCustomAxis.prototype.getNumLabels = function() {
    if (!this._labels) {
        return 0;
    }
    return this._labels.length;
}
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*  DvtDataAxis            Data axis object                            */
/*---------------------------------------------------------------------*/
/**
 *  An axis for displaying numerical values.
 *  @extends DvtBaseAxis
 *  @class DvtDataAxis is the class for displaying numerical values.
 *  <code>
 *  @constructor
 */
var DvtDataAxis = function (axisMinValue, axisMaxValue,
                            axisMinCoord, axisMaxCoord,
                            width, height, isHoriz, valueFormat) {
  this.Init(axisMinValue, axisMaxValue, axisMinCoord, axisMaxCoord, width, height, isHoriz, valueFormat);
};

DvtObj.createSubclass(DvtDataAxis, DvtBaseAxis, "DvtDataAxis");


/**
  *  Object initializer.
  *  @protected
  */
DvtDataAxis.prototype.Init = function (axisMinValue, axisMaxValue, axisMinCoord, axisMaxCoord, width, height, isHoriz, valueFormat) {

    DvtDataAxis.superclass.Init.call(this, axisMinCoord, axisMaxCoord, width, height, isHoriz);

    this._axisMinValue = axisMinValue;
    this._axisMaxValue = axisMaxValue;

    this._valueFormat = valueFormat;

    this._axisRange = axisMaxValue - axisMinValue;
    this._axisSize = axisMaxCoord - axisMinCoord;

}


/**
 * Returns the label at the specified coordinate along the axis.
 *
 * @param  coord  the coordinate on the axis
 * @return a string representing the value on the location along the axis
 */
DvtDataAxis.prototype.getLabelAt = function(coord) {

    var ratio = this.calcRatio(coord);
    var value;

    value = (this._axisMaxValue - this._axisMinValue) * ratio + this._axisMinValue;
    if (DvtAgent.isRightToLeft() && this._isHoriz){
        value = (this._axisMaxValue - this._axisMinValue) * -ratio + this._axisMaxValue;
    }
 
 
// TODO: convert value to label using given value format
    var sValue;

    if (this._valueFormat) {
        sValue = this._valueFormat.getFormattedValue(value, this._axisMinValue, this._axisMaxValue);
    }
    else {
        sValue = value;
    }

    return sValue;
}
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*  DvtTimeAxis            Time axis object                            */
/*---------------------------------------------------------------------*/
/**
 *  An axis for displaying numerical values.
 *  @extends DvtBaseAxis
 *  @class DvtTimeAxis is the class for displaying time/date values.
 *  <code>
 *  @constructor
 */
var DvtTimeAxis = function (axisMinValue, axisMaxValue,
                            axisMinCoord, axisMaxCoord,
                            width, height,
                            timeAxisResources, timeZoneOffset,
                            isHoriz) {
  this.Init(axisMinValue, axisMaxValue, axisMinCoord, axisMaxCoord, width, height,
                    timeAxisResources, timeZoneOffset, isHoriz);
};

DvtObj.createSubclass(DvtTimeAxis, DvtBaseAxis, "DvtTimeAxis");


// ------------------------
// Constants
//
DvtTimeAxis.TIME_SECOND      = 1000;
DvtTimeAxis.TIME_MINUTE      = 60 * DvtTimeAxis.TIME_SECOND;
DvtTimeAxis.TIME_HOUR        = 60 * DvtTimeAxis.TIME_MINUTE;
DvtTimeAxis.TIME_DAY         = 24 * DvtTimeAxis.TIME_HOUR;
DvtTimeAxis.TIME_MONTH       = (365/12) * DvtTimeAxis.TIME_DAY;  // approx.
DvtTimeAxis.TIME_YEAR        = 365 * DvtTimeAxis.TIME_DAY;

DvtTimeAxis.INTERVAL_MILLISECONDS = 0;
DvtTimeAxis.INTERVAL_SECONDS      = 1;
DvtTimeAxis.INTERVAL_MINUTE       = 2;
DvtTimeAxis.INTERVAL_HOUR         = 3;
DvtTimeAxis.INTERVAL_DAY          = 4;
DvtTimeAxis.INTERVAL_MONTH        = 5;
DvtTimeAxis.INTERVAL_YEAR         = 6;

DvtTimeAxis.AM_INDEX                 = 12;
DvtTimeAxis.PM_INDEX                 = 13;
DvtTimeAxis.AMPM_BEFORE_INDEX        = 14;
DvtTimeAxis.DMY_ORDER_INDEX          = 15;
DvtTimeAxis.YEAR_TRAILING_CHAR_INDEX = 16;
DvtTimeAxis.DAY_TRAILING_CHAR_INDEX  = 17;


/**
  *  Object initializer.
  *  @protected
  */
DvtTimeAxis.prototype.Init = function (axisMinValue, axisMaxValue,
                            axisMinCoord, axisMaxCoord,
                            width, height,
                            timeAxisResources, timeZoneOffset, isHoriz) {

    DvtTimeAxis.superclass.Init.call(this, axisMinCoord, axisMaxCoord, width, height, isHoriz);

    this._axisMinValue = axisMinValue;
    this._axisMaxValue = axisMaxValue;

    this._axisRange = axisMaxValue - axisMinValue;
    this._innerIncrement = this._calcInnerIncrement(axisMinValue, axisMaxValue);

    // find the size in pixels of the increments
    this._axisSize = axisMaxCoord - axisMinCoord;
    this._incrSize = this._axisSize / (this._axisRange / this._innerIncrement);

    this._timeAxisResources = (timeAxisResources) ? timeAxisResources : new Object();

    // figure out the timeZoneOffset
    var date = new Date();
    var clientOffset = -1 * date.getTimezoneOffset();

    if (!isNaN(timeZoneOffset))
        this._timeZoneOffset = (timeZoneOffset - clientOffset) * 60 * 1000;
    else
        this._timeZoneOffset = 0;
}


/**
 * Given the min and max values on the scale, calculates and returns the inner
 * grip step increments.
 *
 * @param scaleMin  the minimum value on the scale
 * @param scaleMax  the maximum value on the scale
 */
DvtTimeAxis.prototype._calcInnerIncrement = function(scaleMin, scaleMax) {

    var spread = scaleMax - scaleMin;
    var intervalSize;

    if (spread < DvtTimeAxis.TIME_MINUTE)
        intervalSize = DvtTimeAxis.TIME_MINUTE;
    else if (spread < DvtTimeAxis.TIME_HOUR)
        intervalSize = DvtTimeAxis.TIME_HOUR;
    else if (spread < DvtTimeAxis.TIME_DAY)
        intervalSize = DvtTimeAxis.TIME_DAY;
    else if (spread < DvtTimeAxis.TIME_MONTH)
        intervalSize = DvtTimeAxis.TIME_MONTH;
    else if (spread < DvtTimeAxis.TIME_YEAR)
        intervalSize = DvtTimeAxis.TIME_YEAR;
    else 
        intervalSize = DvtTimeAxis.TIME_YEAR;

    return intervalSize;
}

/**
 * Given the min and max values on the scale, calculates and returns the outer grip
 * step increments
 *
 * @param scaleMin  the minimum value on the scale
 * @param scaleMax  the maximum value on the scale
 * @param innerIncrement  the value of the increments on the inner layer
 *                        of hierarchical labels
 */
DvtTimeAxis.prototype._calcOuterIncrement = function(scaleMin, scaleMax, innerIncrement) {

    var spread = scaleMax - scaleMin;
    var outerIncrement = 0;

    if (innerIncrement <= DvtTimeAxis.TIME_HOUR)
        outerIncrement = DvtTimeAxis.TIME_DAY;
    else if (innerIncrement ==DvtTimeAxis.TIME_DAY)
        outerIncrement = DvtTimeAxis.TIME_MONTH;
    else if (innerIncrement == DvtTimeAxis.TIME_MONTH)
        outerIncrement = DvtTimeAxis.TIME_YEAR;

    // if the outer increment is valid, return it (TODO: use smarter logic here)
    if (outerIncrement > 0 && spread > 1 * outerIncrement)
        return outerIncrement;
    else
        return 0;
}


DvtTimeAxis.prototype.getLabelAt = function (coord, diff) {

    // calculate the actual value from its coordinate along the axis
    var ratio = this.calcRatio(coord);
    var value = (this._axisMaxValue - this._axisMinValue) * ratio + this._axisMinValue;
    if (DvtAgent.isRightToLeft()) {
        value = (this._axisMaxValue - this._axisMinValue) *  - ratio + this._axisMaxValue;
    }
    
    if (diff != null) {
        var ratioDiff = this.calcRatio(coord + diff);
        var secondBound = (this._axisMaxValue - this._axisMinValue) * ratioDiff + this._axisMinValue;
        if (DvtAgent.isRightToLeft()) {
            secondBound = (this._axisMaxValue - this._axisMinValue) *  - ratioDiff + this._axisMaxValue;
        }
        return this._formatLabel(value, Math.abs(secondBound - value));
    }
    // get the properly formatted string
    return this._formatLabel(value);
}



DvtTimeAxis.prototype._formatLabel = function (axisValue, diff) {
    // First convert to a date object
    var date = new Date(axisValue + this._timeZoneOffset);
    //set innerIncrement to range of selected area if possible
    if (diff != null) {
        this._innerIncrement = diff;
    }
    
    if (this._innerIncrement <= DvtTimeAxis.TIME_DAY)
        return this._formatTime(date);

    var dateStr = date.getDate();
    var yearStr = date.getFullYear();
    var monthStr;
    if (this._timeAxisResources && this._timeAxisResources.length >= 12)
        monthStr = this._timeAxisResources[date.getMonth()];
    else 
        monthStr = dateToString.substring(0, currIndex);

    // Add the day and year trailing characters if needed
    if (this._timeAxisResources && this._timeAxisResources.length > DvtTimeAxis.DAY_TRAILING_CHAR_INDEX) {
        var yearChar = this._timeAxisResources[DvtTimeAxis.YEAR_TRAILING_CHAR_INDEX];
        var dayChar = this._timeAxisResources[DvtTimeAxis.DAY_TRAILING_CHAR_INDEX];
        // These will be "" if not needed 
        yearStr += yearChar;
        dateStr += dayChar;
    }

    // Process the DMY Order
    var isDateMonth = true;
    var isMonthYear = true;
    if (this._timeAxisResources && this._timeAxisResources.length > DvtTimeAxis.DMY_ORDER_INDEX) {
        var DMY = this._timeAxisResources[DvtTimeAxis.DMY_ORDER_INDEX];
        isDateMonth = (DMY.indexOf("D") < DMY.indexOf("M"));
        isMonthYear = (DMY.indexOf("M") < DMY.indexOf("Y"));
    }
    //if selected area covers less then a week, include time and date if possible
    if (this._innerIncrement <= DvtTimeAxis.TIME_DAY * 7 && this._timeAxisResources != null)
        return this._formatTime(date) + " " + dateStr + " " + monthStr;
        
    if (this._innerIncrement <= DvtTimeAxis.TIME_YEAR) {
        // return Date
        if (isDateMonth)
            return dateStr + " " + monthStr;
        else 
            return monthStr + " " + dateStr;
    }

    if (this._innerIncrement >= DvtTimeAxis.TIME_YEAR) {
        // return Month
        if (isMonthYear)
            return monthStr + " " + yearStr;
        else 
            return yearStr + " " + monthStr;
    }

    return yearStr;
}


/**
  * Returns the time as a String, formatted in the form HH:MM:SS AM/PM.
  */
DvtTimeAxis.prototype._formatTime = function(date) {

    var hours = date.getHours();
    var mins = date.getMinutes();
    var secs = date.getSeconds();

    var am = "";
    var pm = "";
    var ampmBefore = false;

    if (this._timeAxisResources != null && this._timeAxisResources > DvtTimeAxis.AMPM_BEFORE_INDEX) {
        am = this._timeAxisResources[DvtTimeAxis.AM_INDEX];
        pm = this._timeAxisResources[DvtTimeAxis.PM_INDEX];
        ampmBefore = this._timeAxisResources[DvtTimeAxis.AMPM_BEFORE_INDEX] == "t";
    }

    var b12HFormat = (am != "" && pm != "");
    var ampm;

    if (b12HFormat) {
        ampm = pm;
        if (hours > 12) {
            hours -= 12;
            ampm = pm;
        }
        else if (hours == 0) {
            ampm = am;
            hours = 12;
        }
        else if (hours < 12) {
            ampm = am;
        }
    }

    var timeLabel = this._doubleDigit(hours) + ":" + this._doubleDigit(mins) + ":" + this._doubleDigit(secs);

    if (b12HFormat) {
        if (ampmBefore)
            return ampm + " " + timeLabel;
        else
            return timeLabel + " " + ampm;
    }
    else {
        return timeLabel;
    }
}


DvtTimeAxis.prototype._doubleDigit = function(num) {
    if (num < 10) {
        return "0" + num;
    }
    return "" + num;
}
/*--------------------------------------------------------------------------*/
/*     Class LineMarker       Line marker.  A lightweight invisible object  */
/*                            with a mouse listener - acts as a convenient  */
/*                            way to provide a larger hit detection area    */
/*                            than a point such as the intersection of two  */
/*                            line segments in a line graph.                */
/*--------------------------------------------------------------------------*/
/*  History:                                                                */
/*  --------                                                                */
/*--------------------------------------------------------------------------*/
/**
  *   @class DvtLineMarker
  *   @constructor
  */
var  DvtLineMarker = function(chart)
{
   this._Init(chart) ;
} ;

DvtObj.createSubclass(DvtLineMarker, DvtObj, "DvtLineMarker");

DvtLineMarker.prototype._Init = function(chart)
{
   this._Chart    = chart ;

   this._arCoords = null ;
   this._Chart    = chart ;
    
   this._sidx  = -1;
   this._gidx  = -1 ;
   this._handleId = null;

   this._xMin  = Number.MAX_VALUE ;   //  ) Bounding
   this._yMin  = Number.MAX_VALUE;    //  ) rectangle.
   this._xMax  = Number.MIN_VALUE;    //  )
   this._yMax  = Number.MIN_VALUE;    //  )
};


// Returns the array of coordinates for the markers. 

DvtLineMarker.prototype.getCoords = function()
{
    return this._arCoords ;
} ;
    
DvtLineMarker.prototype.setCoords = function(ar)
{
    this._arCoords = ar ;
} ;
    

// The series index of the markers contained within this object.

DvtLineMarker.prototype.getSeriesIndex = function()
{
    return  this._sidx ;
} ;
DvtLineMarker.prototype.setSeriesIndex = function(sidx)
{
    this._sidx = sidx ;
} ;
    
//  The group index of the first marker contained within this object.
//  All markers belong to consecutive groups.

DvtLineMarker.prototype.getGroupIndex = function()
{
   return this._gidx;
} ;
DvtLineMarker.prototype.setGroupIndex = function(gidx)
{
   this._gidx = gidx;
} ;
    

//  Returns the tooltip for the given series and group indices. 

DvtLineMarker.prototype.getTooltip = function(sidx, gidx)
{
   return  this._Chart.makeTooltipFromSeriesGroup(sidx, gidx);
} ;


//  The handle id
DvtLineMarker.prototype.getHandleId = function()
{
   return this._handleId;
} ;

DvtLineMarker.prototype.setHandleId = function(id)
{
   this._handleId = id;
} ;
// Copyright (c) 2008, 2013, Oracle and/or its affiliates. 
// All rights reserved. 
/*---------------------------------------------------------------------*/
/*    DvtShapeAttr                                                     */
/*---------------------------------------------------------------------*/
/**
 *   Shape Attributes support
 *   @class  DvtShapeAttr
 *   @constructor
 */
var DvtShapeAttr = function () {
  this._Init();
};

DvtObj.createSubclass(DvtShapeAttr, DvtObj, "DvtShapeAttr");

//  clickEventJS template
/**  @final  @type String  @private */
DvtShapeAttr.CLICK_EVENT_JS = "AdfPage.PAGE.findComponent('X-ID-X').getPeer().sendToServerPPR({source:'X-ID-X',event:'bi_click',bi_handle:'X-HAND-X',bi_series:'X-SERIES-X',bi_group:'X-GROUP-X',bi_row:'X-ROW-X',bi_column:'X-COL-X',bi_comp_id:'X-CID-X'});";

// Handle types
/**  @final  @type String  @private */
DvtShapeAttr.SERIES_HANDLE = "bi_series_handle";
/**  @final  @type String  @private */
DvtShapeAttr.GROUP_HANDLE = "bi_group_handle";
/**  @final  @type String  @private */
DvtShapeAttr.DATA_HANDLE = "bi_data_handle";
/**  @final  @type String  @private */
DvtShapeAttr.ANNOTATION_HANDLE = "bi_annotation_handle";
/**  @final  @type String  @private */
DvtShapeAttr.NONDATA_HANDLE = "bi_non_data_handle";
/**  @final  @type String  @private */
DvtShapeAttr.UNKNOWN_HANDLE = "";

DvtShapeAttr.prototype._clear = function () {
  this._chart = null;
  this.clickAction = null;
  this.clickEventJs = null;
  this.clickListener = null;
  this.clickable = null;
  this.href = null;
  this.onClick = null;
  this.onDblClick = null;
  this.onMouseDown = null;
  this.onMouseMove = null;
  this.onMouseOut = null;
  this.onMouseOver = null;
  this.onMouseUp = null;
  this.target = null;
  this.drillJS = null;
  this.linkRef = null;

  //  The following shape attributes are not parsed because
  //  they are currently not used in the flash engine
  //this.onFocus       = null ;
  //this.onKeyDown     = null ;
  //this.onKeyPress    = null ;
  //this.onKeyUp       = null ;
  //this.nohref        = null ;
  //this.onBlur        = null ;
  //this.title         = null ;
  //this.tabIndex      = null ;
};


/**
 *  @private
 */
DvtShapeAttr.prototype._Init = function () {
  this._clear();
  this._bClickListenerFired = false;// indicates to shape attr event
  // handler that this event has already
  // been fired by somebody else (the
  // SelectionEventHandler)
};

/**
 * Parses a shape attributes XML node and returns a new DvtShapeAttr.
 * @param {DvtGraphCore} chart 
 * @return {DvtShapeAttr}
 */
DvtShapeAttr.parse = function(chart, node) {
  // The following shape attributes are not parsed because they
  // are currently not supported in the flash engine
  // node.attributes["onFocus"] ;
  // node.attributes["onKeyDown"] ;
  // node.attributes["onKeyPress"] ;
  // node.attributes["onKeyUp"] ;
  // node.attributes["nohref"] ;
  // node.attributes["onBlur"] ;
  // node.attributes["title"] ;
  // node.attributes["tabindex"] ;

  var  sa = new DvtShapeAttr();
  sa._chart        =  chart;
  sa.alt           =  node.getAttribute('alt');
  sa.clickAction   =  node.getAttribute('clickAction');
  sa.clickEventJs  =  node.getAttribute('Ce');
  sa.clickListener =  node.getAttribute('clickListener');
  sa.clickable     =  node.getAttribute('clickable');
  sa.href          =  node.getAttribute('href');
  sa.onClick       =  node.getAttribute('onClick');
  sa.onDblClick    =  node.getAttribute('onDblClick');
  sa.onMouseDown   =  node.getAttribute('onMouseDown');
  sa.onMouseMove   =  node.getAttribute('onMouseMove');
  sa.onMouseOut    =  node.getAttribute('onMouseOut');
  sa.onMouseOver   =  node.getAttribute('onMouseOver');
  sa.onMouseUp     =  node.getAttribute('onMouseUp');
  sa.target        =  node.getAttribute('target');
  sa.drillJS       =  node.getAttribute('drillJs');
  sa.bLink         =  node.getAttribute('link') == 't';
  sa.linkColor     =  null; // Will be set by the parser if needed
  return sa;
}

/**
 *   Returns a copy of this object.
 *   @return {DvtShapeAttr} a copy of this object.
 */
DvtShapeAttr.prototype.clone = function () {
  var sa = new DvtShapeAttr();
  sa._chart = this._chart;
  sa.clickAction = this.clickAction
  sa.clickEventJs = this.clickEventJs
  sa.clickListener = this.clickListener
  sa.clickable = this.clickable
  sa.href = this.href
  sa.onClick = this.onClick
  sa.onDblClick = this.onDblClick
  sa.onMouseDown = this.onMouseDown
  sa.onMouseMove = this.onMouseMove
  sa.onMouseOut = this.onMouseOut
  sa.onMouseOver = this.onMouseOver
  sa.onMouseUp = this.onMouseUp
  sa.target = this.target
  sa.drillJS = this.drillJS
  sa.linkRef = this.linkRef
  sa.bLink = this.bLink;
  sa.linkColor = this.linkColor;
  return sa;
};

/*---------------------------------------------------------------------*/
/*   fireClickListener()                                               */
/*---------------------------------------------------------------------*/
/**
 *  Execute the clickListener JS.  This is called by the selection event handler
 *  because it needs to emit the clickListener event straight after the
 *  selectioninfo event, and can't wait for the shape attribute event handler
 *  to get its turn.
 */
DvtShapeAttr.prototype.fireClickListener = function () {
  if (this.clickListener) {
    this._Chart.callExternal(this.clickListener);

    this._bClickListenerFired = true;// dont want the shape attr event
    // handler to fire this again, when
    // it gets its turn.
  }
};

/*---------------------------------------------------------------------*/
/*  getClickEventJS()                                                  */
/*---------------------------------------------------------------------*/
/**
 *   Static function to construct javascript for a click event, suitable
 *   for dispatch to the ADF framework.
 *   @param {String}  id the id of the object with the supplied shape attributes.
 *   @returns {String}  constructed Javascript.
 */
DvtShapeAttr.getClickEventJS = function (sa, id) {
  //   Format of the clickEventJs string is :
  //
  //  "handle-type,series,group,row,column,compId"
  var js = sa.clickEventJs;

  var ar = js.split(",");
  var h = ar[0];

  if (h === 's')
    h = DvtShapeAttr.SERIES_HANDLE;
  else if (h === 'g')
    h = DvtShapeAttr.GROUP_HANDLE;
  else if (h === 'd')
    h = DvtShapeAttr.DATA_HANDLE;
  else if (h === 'a')
    h = DvtShapeAttr.ANNOTATION_HANDLE;
  else if (h === 'n')
    h = DvtShapeAttr.NONDATA_HANDLE;
  else 
    h = DvtShapeAttr.UNKNOWN_HANDLE;

  var s;

  s = '' + DvtShapeAttr.CLICK_EVENT_JS;// modify a copy
  s = s.replace("X-ID-X", id);// (2  id
  s = s.replace("X-ID-X", id);// occurrences)
  s = s.replace("X-HAND-X", h);
  s = s.replace("X-SERIES-X", ar[1]);
  s = s.replace("X-GROUP-X", ar[2]);
  s = s.replace("X-ROW-X", ar[3]);
  s = s.replace("X-COL-X", ar[4]);
  s = s.replace("X-CID-X", ar[5]);

  return s;
};

/**
 *    Returns true if this ShapeAttributes has a clickListener attribute.
 */
DvtShapeAttr.prototype.hasClickListener = function () {
  return (this.clickListener !== null);
};

/**
 *  Static function -  returns true if the supplied DvtShapeAttrs has at
 *  least one clickable attribute.
 *  @param {DvtShapeAttr}  sa  the shape attribute object to be tested.
 *  @returns {boolean} true if the supplied DvtShapeAttrs has at
 *  least one clickable attribute.
 */
DvtShapeAttr.hasClickableShapeAttribute = function (sa) {
  return (sa.clickListener || sa.clickAction || sa.clickEventJs || sa.onClick || sa.drillJS || sa.onDblClick || sa.href || sa.linkRef);
};

/**
 * Processes the shape attributes for the specified object and event type.
 * @param {object} dlo The logical object on which the event was fired.
 * @param {DvtDisplayable} target The displayable on which the event was fired.
 * @param {string} type The event type
 */
DvtShapeAttr.processEvent = function (dlo, target, type) {
  if(!dlo || (dlo.isInitFxInProgress && dlo.isInitFxInProgress()))
    return;

  var sa = dlo.getShapeAttr(target);
  if(!sa)
    return;

  var js = null;

  switch(type) {
    case DvtMouseEvent.MOUSEMOVE:
      js = sa.onMouseMove;
      break;
    case DvtMouseEvent.MOUSEOVER:
      js = sa.onMouseOver;
      break;
    case DvtMouseEvent.MOUSEOUT:
      js = sa.onMouseOut;
      break;
    case DvtMouseEvent.CLICK:
      if (sa.drillJS)
        js = sa.drillJS;
      else if (sa.clickListener) {
        if (!sa._bClickListenerFired) {
          // if Sel event handler
          js = sa.clickListener;// has not already done it,
        }
        // we fire it here.
        else 
          sa._bClickListenerFired = false;// or just clear the flag.
      }
      else if (sa.clickAction)
        js = sa.clickAction;
      else if (sa.clickEventJs)
        js = DvtShapeAttr.getClickEventJS(sa, sa._chart.getId());
      else if (sa.onClick)
        js = sa.onClick;
      else if (sa.onMouseDown)
        js = sa.onMouseDown;
      break;
    case DvtMouseEvent.DBLCLICK:
      js = sa.onDblClick;
      break;
    case DvtMouseEvent.MOUSEDOWN:
      js = sa.onMouseDown;
      break;
    case DvtMouseEvent.MOUSEUP:
      if (sa.onMouseUp)
        js = sa.onMouseUp;
      else {
        if (sa.href)
          js = sa.href;
        else if (sa.linkRef)
          js = sa.linkRef;
        if (js) {
          sa._chart.activateLink(js, sa.target);
          return;// links don't result in the JS calls below
        }
      }
      break;
    default :
      break;
  }

  // If a match was found, execute the JS
  if(js)
    sa._chart.callJs(js);
};
// Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*  DvtGraphAutomation         Graph/Gauge Automation Services         */
/*---------------------------------------------------------------------*/
/**
 *  Provides automation services for graph and gauge.  To obtain a
 *  DvtGraphAutomation object for a specific DvtGraphCore use {@link DvtGraphCore#getAutomation}.
 *  @class  DvtGraphAutomation
 *  @extends DvtObj
 *  @param {DvtGraphCore} graphCore
 *  @constructor
 *
 */
var DvtGraphAutomation = function (graphCore) {
  this._Init(graphCore)
};

DvtObj.createSubclass(DvtGraphAutomation, DvtObj, "DvtGraphAutomation");

/**
 * Private initialization method
 * @param {DvtGraphCore} graphCore
 * @private
 */
DvtGraphAutomation.prototype._Init = function (graphCore) {
  this._graphCore = graphCore;
  this._sLastError = null;
};

DvtGraphAutomation.AUTOMATION_NO_EVENT =  - 1;
DvtGraphAutomation.AUTOMATION_MOUSE_CLICK = 0;
DvtGraphAutomation.AUTOMATION_MOUSE_DOWN = 1;
DvtGraphAutomation.AUTOMATION_MOUSE_MOVE = 2;
DvtGraphAutomation.AUTOMATION_MOUSE_OUT = 3;
DvtGraphAutomation.AUTOMATION_MOUSE_OVER = 4;
DvtGraphAutomation.AUTOMATION_MOUSE_UP = 5;
DvtGraphAutomation.AUTOMATION_KEY_DOWN = 6;
DvtGraphAutomation.AUTOMATION_KEY_UP = 7;
DvtGraphAutomation.AUTOMATION_MOUSE_DRAG = 8;// new
/**
 *   Returns the value of the subElementId. Valid subElementIds include
 *   <ul>
 *   <li> title </li>
 *   <li> seriesCount </li>
 *   <li> groupCount </li>
 *   <li> data[seriesIdx][groupIdx]#valueY </li>
 *   <li> data[seriesIdx][groupIdx]#tooltip </li>
 *   <li> data[seriesIdx][groupIdx]#color </li>
 *   <li> data[seriesIdx][groupIdx]#text </li>
 *   <li> axis[axisId]#title </li>
 *   </ul>
 *
 *   @param {String} subId The subElementId of interest.
 *   @param {Number} event An optional argument to indicate if an event is fired on the subElementId
 *   @param {String} params An optional argument to indicate additional parameters for the event.
 *                          For example, params could be the keycode, if the event was a keydown or keyup event
 *   @return String
 */
DvtGraphAutomation.prototype.processSubId = function (subId, event, params) {
  if (event === undefined) {
    event = DvtGraphAutomation.AUTOMATION_NO_EVENT;
  }

  if (subId == null) {
    this._sLastError = "subId is null";
    return null;
  }

  var primaryId;
  var subSubId;
  var seriesIdx =  - 1;
  var groupIdx =  - 1;
  var miscId =  - 1;

  var array = DvtGraphAutomation._convertSubIdToArray(subId);

  primaryId = array[0];
  seriesIdx = array[1];
  groupIdx = array[2];
  subSubId = array[array.length - 1];

  var str;// return value
  var bIsEvent = (event != DvtGraphAutomation.AUTOMATION_NO_EVENT);

  if (primaryId == "error") {
    return this._sLastError;// return result of last call
  }
  this._sLastError = null;// can now clear the last error.
  // Get the internal detobject object id e.g. Marker -> LM, or bar -> BR
  var objId = null;

  if (primaryId == "data") {
    // In the Flash implementation, the objId was obtained in two separate steps
    // First by converting the "data" primaryId to a string indicating the type
    // of marker (e.g., "bar" or "line") and then this string is converted to the
    // the internal object id (a constant from DvtGraphObjType).
    // In HTML5, we combine these separte steps into one method call.    
    objId = this._convertDataSubIdToObjectId(seriesIdx, groupIdx);
  }
  else if (primaryId == "_Object") {
    // subid is of the form _Object[internal det obj id][seriesIdx][groupIdx][miscId]
    objId = array[1];
    seriesIdx = array[2];
    groupIdx = array[3];

    if (array.length >= 5)
      miscId = array[4];
  }

  //------------------------------------------------------//
  //  Retrieve the property specified by the subSubId     //
  //  from a Marker, bar, line, area, pieslice, indicator //
  //------------------------------------------------------//
  var data;
  data = this._graphCore.getDataObj();// get the graph
  if (objId) {
    var detObj = this._processDetObj("_Object[" + objId + "][" + seriesIdx + "][" + groupIdx + "][" + miscId + "]", event, params);

    if (detObj && !bIsEvent) {
      //  Get a specific object property.
      str = this._processDataMarkerInfo(detObj, subSubId, seriesIdx, groupIdx);
    }
  }
  else if (primaryId == "title") {
    // Note that in Flash, the misc id is 0, not -1
    str = this._processDetObjString("_Object[Title][-1][-1][-1]");
  }
  else if (primaryId == "legendTitle") {
    // Note that in Flash, the misc id is 0, not -1
    str = this._processDetObjString("_Object[Other][-1][-1][-1]");
  }

  // In the Flash version, we had support for returning versioning info about 
  // the Graph.  Not sure if that support is available in HTML5, and even if
  // it is, it is not being used by automation, so it is not ported over
  //          else if (primaryId == "version") { ... }
  //          else if (primaryId == "verNum") { ... }
  //          else if (primaryId == "verDate") { ... }
  else if (primaryId == "axis") {
    //-------------//
    // Axis values //
    //-------------//
    if (subSubId == "#title") {
      // Note that in Flash, the misc id is 0, not -1
      str = this._processDetObjString("_Object[" + (array[1]).toUpperCase() + "Title][-1][-1][-1]");
    }

    // NOTE: in the Flash implementation, there is support for returning the minimum and maximum 
    // values of the axis. However, this support is not exposed in the Automation API, so that
    // is not ported over to HTML5
    //else if (subSubId == "#minValue") { ... }
    //else if (subSubId == "#maxValue") { ... }
  }
  else if (primaryId == "seriesCount") {
    str = data.getSeriesCount().toString();
  }
  else if (primaryId == "groupCount") {
    str = data.getGroupCount().toString();
  }

  // NOTE: in the Flash implementation, there is support for returning the color and label for series
  // and label for groups. However, this support is not exposed in the Automation API, so that
  // is not ported over to HTML5
  //else if (primaryId == "series") { ... }
  //else if (primaryId == "group") { ... }
  return str;
}

/*---------------------------------------------------------------------*/
/*    _convertSubIdToArray()                                            */
/*---------------------------------------------------------------------*/
/*    Format of subid is:                                              */
/*                                                                     */
/*    "primaryId[n1][n2][n3]...[n]#subSubId"    where [n1]...[n3] and  */
/*                                              subSubId are optional. */
/*                                                                     */
/*    Returns  array[0]     =  primaryId                               */
/*             array[1]     =  n1                                      */
/*             array[2]     =  n2                                      */
/*             array[3 - n] =  if specified                            */
/*             array[last]  =  #subSubId if specified                  */
/*                                                                     */
/*    the minimum array length returned is 3.                          */
/*                                                                     */
/*    Note:  if either/both of the series/group info is not specified, */
/*           the corresponding array entries will be set to -1.        */
/*           thus "seriesCount" will be treated the same as            */
/*           "seriesCount[-1][-1]", and "series[2]#color" will be      */
/*           treated the same as "series[2][-1]#color", and "title"    */
/*           be treated the same as "title[-1][-1]".                   */
/*---------------------------------------------------------------------*/

/**
 * @param {String} subId The subId of interest.
 * @return {Array}
 * @private
 */
DvtGraphAutomation._convertSubIdToArray = function (subId) {
  var array = subId.split("\[");

  // Retrieve and remove the subSubid from the array
  var subSubId = DvtGraphAutomation._parseSubSubId(array);

  var len = array.length;

  for (var i = 1;i < len;i++) {
    var elem = array[i];
    var tempId = elem.substr(0, elem.length - 1);// remove trailing "]"
    tempIdAsNumber = parseFloat(tempId);
    tempId = isNaN(tempIdAsNumber) ? tempId : tempIdAsNumber;
    array[i] = tempId;
  }

  //  If we didn't received a series or group, set to -1
  if (len == 2) {
    array[2] =  - 1;
    len++;
  }
  if (len == 1) {
    array[1] =  - 1;
    array[2] =  - 1;
    len += 2;
  }

  if (subSubId)// if a subSubId was found, add it to
  {
    // the end of the array
    array[len] = subSubId;
  }

  return array;// array is guaranteed to have at least 3 entries
}

/*---------------------------------------------------------------------*/
/*   _parseSubSubId()    Looks at the last entry in the array.  If it  */
/*                       contains a subSubId string of #?????, the     */
/*                       subSubId is removed from the last array entry */
/*                       and the subSubId is returned.                 */
/*---------------------------------------------------------------------*/

/**
 * Identifies a subsubid using the # character and appends a new array
 * item for it.
 *
 * @param {Array} array The parsed subId of interest.
 * @return {String}
 * @private
 */
DvtGraphAutomation._parseSubSubId = function (array) {
  var sRet = null;

  if (array && array.length >= 0) {
    // Special case where subsubid is present
    var target = array.length - 1;
    var elem = array[target];// get last entry in array
    var sepIdx = elem.indexOf("#");

    if (sepIdx > 0) {
      array[target] = elem.substr(0, sepIdx);// remove subSubId from array entry
      sRet = elem.substr(sepIdx);// isolate the subSubId
    }
  }

  return sRet;// the subSubId
}

/*---------------------------------------------------------------------*/
/*   _processDetObjString()    Returns the label if a DvtText, or a    */
/*                             string representation of the DvtDetObj. */
/*---------------------------------------------------------------------*/

/**
 * Returns the label if a DvtText, or a string representation of the DvtDetObj.
 *
 * @param  {String} internalId
 * @return {String}
 * @private
 */
DvtGraphAutomation.prototype._processDetObjString = function (internalId) {
  var sRet = null;

  this._sLastError = null;

  var detObj = this._processDetObj(internalId, DvtGraphAutomation.AUTOMATION_NO_EVENT);

  if (detObj) {
    var owningDetObj = detObj.getDetObj();
    if (owningDetObj instanceof DvtText) {
      var dt = owningDetObj;
      sRet = dt.getTextString();
      // IMPORTANT NOTE: The DvtText class doesn't keep track of the original, untruncated
      //                 text, unlike the Flash DetText class.  So in HTML5, we can only
      //                 return the result from getTextString()
      //      if (dt.m_untruncated != null)
      //        sRet = dt.m_untruncated ;
      //      else if (dt.m_txtLabel != null)
      //        sRet = dt.m_txtLabel.text ;
      //      else
      //        sRet = dt.m_label;
    }
    else 
      sRet = owningDetObj.toString();
  }

  return sRet;
}

/*----------------------------------------------------------------------*/
/*   _processDetObj()    Returns a DetObject or Fires a Selenium event  */
/*----------------------------------------------------------------------*/

/**
 * Process an internal action on a DvtDetObj.  The arg internalId is a
 * string of the format:
 * <br>
 * "_Object[BR][series][group][0]"  <br>
 * Returns a DvtDetObj or null if not found.
 *
 * @param {String} internalId
 * @param {Number} event
 * @param {String} params
 * @return {DvtDetObj}
 * @private
 */
DvtGraphAutomation.prototype._processDetObj = function (internalId, event, params) {
  this._sLastError = null;
  var det = this._getDetObject(internalId);

  if (event == DvtGraphAutomation.AUTOMATION_NO_EVENT) {
    if (det instanceof DvtDetObj)
      return det;
  }
  else {
    var eventType = null;

    if (event == DvtGraphAutomation.AUTOMATION_MOUSE_CLICK)
      eventType = DvtMouseEvent.CLICK;
    else if (event == DvtGraphAutomation.AUTOMATION_MOUSE_OVER)
      eventType = DvtMouseEvent.MOUSEOVER;
    else if (event == DvtGraphAutomation.AUTOMATION_MOUSE_OUT)
      eventType = DvtMouseEvent.MOUSEOUT;
    else if (event == DvtGraphAutomation.AUTOMATION_MOUSE_MOVE)
      eventType = DvtMouseEvent.MOUSEMOVE;
    else if (event == DvtGraphAutomation.AUTOMATION_KEY_DOWN)
      eventType = DvtKeyboardEvent.KEYDOWN;
    else if (event == DvtGraphAutomation.AUTOMATION_KEY_UP)
      eventType = DvtKeyboardEvent.KEYUP;

    if (det instanceof DvtDetObj) {
      var displayable = det.getDetObj();
      var array = DvtGraphAutomation._convertSubIdToArray(internalId);
      if (displayable) {
        if (displayable instanceof DvtPieChart) {
          var seriesIdx =  - 1;

          if (array && array.length > 3) {
            seriesIdx = array[2];
          }
          // delegate to the PieChart to dispatch the event
          displayable.__dispatchTestEvent(eventType, seriesIdx);
        }

        // in this case, the det object is a rangeSlider and scrollableLegend; i.e., something that has subcomponents
        else if (displayable.dispatchTestEvent) {
          var subComponent;
          var miscId;

          if (array && array.length > 0) {
            // _Object[objId][seriesId][grpId][miscId]
            if (array[0] == "_Object" && array.length == 5) {
              miscId = array[4];
              displayable.dispatchTestEvent(miscId, eventType, params);
            }
          }
        }

        else {
          displayable.dispatchDisplayableEvent(eventType, params);
        }
      }
    }
    // end if (det instanceof DvtDetObj) 
  }

  return null;
}

/*---------------------------------------------------------------------*/
/* _getDetObject()       Returns a det object based on the             */
/*                       objId/series/group in the request string.     */
/*---------------------------------------------------------------------*/

/**
 * Returns a det object based on the objId/series/group in the request string. This is a convenience
 * method that parses the internalId passed in and then calls _automationGetDetObject
 *
 * @param {String} internalId The internalId is a string of the form: "_Object[BR][series][group][0]"
 * @return {Object}
 * @private
 */
DvtGraphAutomation.prototype._getDetObject = function (internalId) {
  var ret;// returned object
  this._sLastError = null;

  if (internalId) {
    var array = DvtGraphAutomation._convertSubIdToArray(internalId);

    var objId;
    var seriesId;
    var groupId;
    var miscId;

    if (array && array.length > 0) {
      // _Object[objId][seriesId][grpId][miscId]
      if (array[0] == "_Object" && array.length == 5) {
        objId = array[1];
        seriesId = array[2];
        groupId = array[3];
        miscId = array[4];
      }
    }

    ret = this._automationGetDetObject(objId, seriesId, groupId, miscId);
  }

  return ret;
}

/*-----------------------------------------------------------------------*/
/*   _automationGetDetObject    Returns a Det Object based on the object */
/*                              id and series/group/miscId.              */
/*  In the Flash graph Automation.as class, this method was called       */
/*  seleniumGetDetObject                                                 */
/*-----------------------------------------------------------------------*/

/**
 * Returns a det object based on the objId, series, group, and misc Ids
 *
 * @param {String} objId
 * @param {Number} seriesId
 * @param {Number} groupId
 * @param {Number} miscId
 * @return {Object}
 * @private
 */
DvtGraphAutomation.prototype._automationGetDetObject = function (objId, seriesId, groupId, miscId) {
  this._sLastError = null;

  // In Flash, we used an iterator - m_chart.getDetListIterator(true) ;
  var dloArray = this._graphCore.getDLOs();
  var obj;
  var currentObjId;

  // if the objId is a pie slice, return the pie chart, since that is the only item on the det list
  // (in Flash, each individual slice was an item on the det list; in HTML5, only the chart itself is on 
  // the det list)
  if (objId == DvtGraphObjType.PIE_SLICE)
    objId = DvtGraphObjType.PIE_CHART;

  for (var i = 0;i < dloArray.length;i++) {
    obj = dloArray[i];

    currentObjId = obj.getObjId();

    if (currentObjId) {
      if (objId == "stock")// generic stock obj ?
      {
        if (DvtGraphCore.isStockObjId(currentObjId)) {
          if (obj.getSeriesIndex() == seriesId) {
            if (obj instanceof DvtDetObj && obj.getMiscId() == miscId)
              return obj;
          }
        }
      }
      else if (currentObjId == objId) {
        if (objId == DvtGraphObjType.INDICATOR)
          return obj;
        else if (objId == DvtGraphObjType.PIE_CHART) {
          // return this DvtPieChart if the group matches
          var targetGroupId = this._graphCore.getDataObj().getGroupFromIndex(groupId);
          var currentPieId = obj.getDetObj().getPieInfo().getId();
          if (targetGroupId == currentPieId)
            return obj;
        }
        else if (obj.getSeriesIndex() == seriesId) {
          if (obj.getDetObj && obj.getDetObj().getSubcomponent) {
            // in this case, the det object is a rangeSlider and scrollableLegend; i.e., something that has subcomponents
            return obj;
          }

          if (currentObjId == DvtGraphObjType.LINE_MARKER || currentObjId == DvtGraphObjType.LINE_RISER || currentObjId == DvtGraphObjType.AREA_RISER || obj.getGroupIndex() == groupId) {
            return obj;
          }
        }
      }
      // end if (objId matches
    }
    // end if objId != null
  }
  // end while
  return null;
}

/*---------------------------------------------------------------------*/
/*   _convertDataSubIdToObjectId()                                     */
/* This method is a combination of the _determineSpecificMarkerType    */
/* and _convertSubIdToObjectId() methods from the graph Automation.as  */
/*---------------------------------------------------------------------*/

/**
 * Converts a "data" primary id to the corresponding internal DetObject id
 * e.g., "data" -> DvtGraphObjType.BAR_RISER for bar graphs
 *
 * @param {Number} seriesIdx
 * @param {Number} groupIdx
 * @return {String} A constant defined in DvtGraphObjType
 */

DvtGraphAutomation.prototype._convertDataSubIdToObjectId = function (seriesIdx, groupIdx) {
  var sRet = null;
  var graphType = this._graphCore.getType();

  if (graphType.indexOf('BAR') >= 0)
    sRet = DvtGraphObjType.BAR_RISER;
  else if (graphType.indexOf('LINE') >= 0 || graphType.indexOf('AREA') >= 0)
    sRet = DvtGraphObjType.LINE_MARKER;
  else if (graphType.indexOf('PIE') >= 0) {
    // We return PIE_SLICE, just as we did in Flash
    // However, we have to keep in mind that in HTML5, only DvtPieChart objects  are in the detlist
    sRet = DvtGraphObjType.PIE_SLICE;
  }
  else if (this._graphCore.isBubbleChart() || this._graphCore.isScatterChart())
    sRet = DvtGraphObjType.LINE_MARKER;
  else if (graphType.indexOf("STOCK") >= 0)
    sRet = "stock";// generic market type (could be StockHighLine, StockLowLine,
  // StockRisingRiser, StockFallingRiser, StockRiser, StockOpenTick,
  // StockCloseTick)
  else if (this._graphCore.isGauge() || (graphType.indexOf("LED") >= 0))
    sRet = DvtGraphObjType.INDICATOR;
  else if (graphType.indexOf("FUNNEL") >= 0)
    sRet = DvtGraphObjType.FUNNEL_SLICE;
  else if (graphType.indexOf('COMBINATION') >= 0) {
    // Note that in Flash, the misc id is 0, not -1
    var obj = this._automationGetDetObject(DvtGraphObjType.BAR_RISER, seriesIdx, groupIdx,  - 1);

    if (obj)
      sRet = DvtGraphObjType.BAR_RISER;
    else {
      obj = this._automationGetDetObject(DvtGraphObjType.LINE_MARKER, seriesIdx, groupIdx,  - 1);
      if (obj)
        sRet = DvtGraphObjType.LINE_MARKER;
    }
  }

  return sRet;
}

/*--------------------------------------------------------------------------*/
/*   _processDataMarkerInfo()                                               */
/*--------------------------------------------------------------------------*/
/**
 * Return a det object property value.
 *
 * @param {DvtDetObj} detObj
 * @param {String} subSubId
 * @param {Number} seriesIdx
 * @param {Number} groupIdx
 * @return {String}
 */
DvtGraphAutomation.prototype._processDataMarkerInfo = function processMarkerInfo(detObj, subSubId, seriesIdx, groupIdx) {
  var result = null;
  var dv;

  this._sLastError = null;

  var owningDetObject = detObj.getDetObj();

  if (detObj.getObjId() == DvtGraphObjType.INDICATOR) {
    // In the Flash implementation, the getGaugeProperty() method provides support for the following attributes:
    // tooltip, color, value, minValue, maxValue, threshMinValue, threshMaxValue, threshColor, metricLabel
    // topLabel, and bottomLabel.
    // However, in the automation API, we only expose value, so instead of porting getGaugeProperty(), just 
    // port support for returning the gauge value
    if (subSubId == "#value") {
      var gaugeData = this._graphCore.getGaugeData();
      return gaugeData.getValue().toString();
    }
    else {
      // no other sub-subids currently supported in HTML5
      return null;
    }
  }

  if (owningDetObject instanceof DvtPieChart) {
    if (subSubId == "#tooltip") {
      return owningDetObject.getSliceTooltip(seriesIdx);
    }
    else if (subSubId == "#color") {
      return owningDetObject.getSliceColor(seriesIdx);
    }
    else if (subSubId == "#text") {
      return owningDetObject.getSliceText(seriesIdx);
    }
  }

  var data = this._graphCore.getDataObj();

  if (subSubId == "#tooltip") {
    if (groupIdx !=  - 1) {
      //  Check for area and line which hold tooltip info in <m> or <lm> tags
      if (detObj.isMultiRiser()) {
        result = this._graphCore.makeTooltipFromSeriesGroup(seriesIdx, groupIdx);
      }
      else {
        if (detObj.hasTooltipText())// is there a specifically defined tooltip
          result = detObj.getTooltipText();
        else 
          result = this._graphCore.makeTooltipFromSeriesGroup(seriesIdx, groupIdx);
      }
    }
    else 
      result = detObj.getTooltipText();

    if (result) {
      result = DvtGraphParser.convertLineBreaks(result);
    }

    return result;
  }
  else if (subSubId == "#valueX") {
    if (groupIdx !=  - 1) {
      result = data.getValue(DvtGraphData.DV_X, seriesIdx, groupIdx).toString();
      return (result != "NaN" ? result : null);
    }
  }
  else if (subSubId == "#valueY") {
    if (groupIdx !=  - 1) {
      result = data.getValue(DvtGraphData.DV_Y, seriesIdx, groupIdx).toString();
      return (result != "NaN" ? result : null);
    }
  }
  else if (subSubId == "#valueZ") {
    if (groupIdx !=  - 1) {
      result = data.getValue(DvtGraphData.DV_Z, seriesIdx, groupIdx).toString();
      return (result != "NaN" ? result : null);
    }
  }

  // NOTE: in the Flash implementation, there is support for returning the marker label
  // However, this support is not exposed in the Automation API, so that is not ported
  // over to HTML5
  //else if (subSubId == "#label" && (detObj.getDetObj() instanceof DvtMarkerRiser))  { ... }
  else if (subSubId == "#color") {
    if (seriesIdx !=  - 1) {
      if (data.isSeriesColor(seriesIdx))
        return data.getSeriesColor(seriesIdx);
    }
  }
  else if (subSubId == "#text") {
    var dvtText = null;

    // Note that in Flash, the misc id is 0, not -1
    var dvtTextDetObjWrapper = this._automationGetDetObject("DataText", seriesIdx, groupIdx,  - 1);
    if (dvtTextDetObjWrapper)
      dvtText = dvtTextDetObjWrapper.getDetObj();

    if (dvtText)
      result = dvtText.getTextString();

    return result;
  }

  else if (this._graphCore.getType().indexOf("STOCK") >= 0)// stock chart ?
  {
    if (subSubId == "#valueHigh")
      dv = DvtGraphData.DV_HV;
    else if (subSubId == "#valueLow")
      dv = DvtGraphData.DV_LV;
    else if (subSubId == "#valueOpen")
      dv = DvtGraphData.DV_OV;
    else if (subSubId == "#valueClose")
      dv = DvtGraphData.DV_CV;
    else if (subSubId == "#valueVolume")
      dv = DvtGraphData.DV_VV;
    else 
      return null;

    result = data.getValue(dv, seriesIdx, groupIdx).toString();
    return (!isNaN(result) ? result : null);
  }
  else if (this._graphCore.getType().indexOf("FUNNEL") >= 0)// funnel chart ?
  {
    if (subSubId == "#valueTarget")
      dv = DvtGraphData.DV_TV;
    else if (subSubId == "#valueActual")
      dv = DvtGraphData.DV_AV;
    else if (subSubId == "#unformattedValueActual")
      dv = DvtGraphData.DV_AUV;
    else if (subSubId == "#unformattedValueTarget")
      dv = DvtGraphData.DV_TUV;
    else 
      return null;

    result = data.getValue(dv, seriesIdx, groupIdx).toString();
    return (!isNaN(result) ? result : null);
  }

  return null;

}

DvtGraphAutomation.prototype.getSubIdFromEvent = function (event) {
  return this.getSubId(event.pageX, event.pageY, event.target);
}

DvtGraphAutomation.prototype.getSubId = function (pageX, pageY, target) {
  var logicalObj = this._graphCore.getLogicalObject(target);

  if (logicalObj instanceof DvtPieSlice) {
    var seriesId = logicalObj.getSliceInfo().getId();
    var groupId = logicalObj.getPieChart().getPieInfo().getId();
    var data = this._graphCore.getDataObj();
    var seriesIndex = data.getIndexFromSeries(seriesId);
    var groupIndex = data.getIndexFromGroup(groupId);
    return "data[" + seriesIndex + "][" + groupIndex + "]";
  }

  var objId = logicalObj.getObjId();
  if (objId == DvtGraphObjType.AREA_RISER || objId == DvtGraphObjType.AREA_RISER_BOTTOM || objId == DvtGraphObjType.AREA_RISER_TOP || objId == DvtGraphObjType.BAR_RISER || objId == DvtGraphObjType.BAR_RISER_SIDE || objId == DvtGraphObjType.BAR_RISER_TOP || objId == DvtGraphObjType.LINE_MARKER || // scatter, bubble, and line markers
  objId == DvtGraphObjType.LINE_RISER || objId == DvtGraphObjType.LINE_RISER_SIDE || objId == DvtGraphObjType.LINE_RISER_TOP || objId == DvtGraphObjType.PIE_SLICE || objId == DvtGraphObjType.FUNNEL_SLICE || DvtGraphCore.isStockObjId(objId)) {
    return "data[" + logicalObj.getSeriesIndex() + "][" + logicalObj.getGroupIndex() + "]";
  }
  return null;
}
/**
 * Fires a click event on the specified sub-component
 * @param {String} subId
 * @export
 */
DvtGraphAutomation.prototype.click = function (subId) {
  this.fireEvent(DvtGraphAutomation.AUTOMATION_MOUSE_CLICK, subId);
}

/**
 * Fires an event on the given graph subelement
 * @param {Number} eventType
 * @param {String} subId
 * @param {String} params
 * @export
 */
DvtGraphAutomation.prototype.fireEvent = function (eventType, subId, params) {
  this.processSubId(subId, eventType, params);
}

/**
 * Fires a mouseover event on the specified sub-component
 * @param {String} subId
 * @export
 */
DvtGraphAutomation.prototype.mouseOver = function (subId) {
  this.fireEvent(DvtGraphAutomation.AUTOMATION_MOUSE_OVER, subId);
}

/**
 * Fires a mouseout event on the specified sub-component
 * @param {String} subId
 * @export
 */
DvtGraphAutomation.prototype.mouseOut = function (subId) {
  this.fireEvent(DvtGraphAutomation.AUTOMATION_MOUSE_OUT, subId);
}

/**
 * Fires a mousemove event on the specified sub-component
 * @param {String} subId
 * @export
 */
DvtGraphAutomation.prototype.mouseMove = function (subId) {
  this.fireEvent(DvtGraphAutomation.AUTOMATION_MOUSE_MOVE, subId);
}

/**
 * Fires a keydown event on the specified sub-component
 * @param {String} subId
 * @export
 */
DvtGraphAutomation.prototype.keyDown = function (subId, keycode) {
  this.fireEvent(DvtGraphAutomation.AUTOMATION_KEY_DOWN, subId);
}

/**
 * Fires a keyup event on the specified sub-component
 * @param {String} subId
 * @export
 */
DvtGraphAutomation.prototype.keyUp = function (subId, keycode) {
  this.fireEvent(DvtGraphAutomation.AUTOMATION_KEY_UP, subId);
}
// Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------*/
/*   DvtGraphContext                                                   */
/*---------------------------------------------------------------------*/
/**
  *  Chart context
  *  @class   DvtGraphContext
  *  @extends DvtObj
  *  @constructor
  *  @param {DvtDetObj} obj
  *  @export
  */
var DvtGraphContext = function (obj) {
    this.Init(obj);
} ;

//Constants for component objects
/**   @final @type String  @export */
DvtGraphContext._PIE_SLICE_LABEL_INTERNAL = "PSLI" ;

DvtObj.createSubclass(DvtGraphContext, DvtObj, "DvtGraphContext") ;

/**
 * Initializes the graph context with the object
 * Applications should never instantiate the graph context object
 * @protected
 */
DvtGraphContext.prototype.Init = function (obj) {
    if (!obj || !((obj.getChart && obj.getChart() != null) || (obj.getPieChart && obj.getPieChart() != null)) )
      return;
    this._objId = null;
    this._seriesId = null;
    this._groupId = null;
    this._seriesIndex = null;
    this._groupIndex = null;
    this._userProps = null;
    this._dataColor = null;
    this._bbox = null;
    this._tooltip = null;
    var objId;
    if (obj.getChart && obj.getChart().getGaugeData && obj.getChart().getGaugeData() != null){
        this._objId = obj.getObjId();
        this._bbox = obj.getDetObj().getDimensions(obj.getChart().getCtx().getStage());
        this._tooltip = obj.getDatatip();
        this._userProps = obj.getUserProps();
        this._dataColor = "#000000";
    }
    else if (obj.getPieChart && obj.getPieChart() != null) {
        objId = obj.getSliceInfo().getObjId();
        if (objId == DvtStandaloneGraph.OBJ_SLICE)
            this._objId = objId;   
        this._seriesId = obj.getSliceInfo().getId();
        this._groupId = obj.getPieChart().getPieInfo().getId();
        var dataObj = obj.getPieChart()._Dlo.getChart().getDataObj();
        this._seriesIndex = dataObj.getIndexFromSeries(this._seriesId);
        this._groupIndex = dataObj.getIndexFromGroup(this._groupId);
        this._userProps = obj.getSliceInfo().getUserProps();
        if (this._seriesIndex > -1 && this._groupIndex > -1){
            this._dataColor = obj.getSliceInfo().getFillColor();
            this._bbox = obj.getKeyboardBoundingBox();
            this._tooltip = obj.getSliceInfo().getTooltip();
        }
    } else {
        objId = obj.getObjId();
        if (objId == DvtStandaloneGraph.OBJ_LEGEND_AREA ||
            objId == DvtStandaloneGraph.OBJ_LEGEND_MARKER ||
            objId == DvtStandaloneGraph.OBJ_LEGEND_TEXT ||
            objId == DvtStandaloneGraph.OBJ_LEGEND_TITLE ||
            objId == DvtStandaloneGraph.OBJ_LINE_RISER ||
            objId == DvtStandaloneGraph.OBJ_LINE_RISER_SIDE ||
            objId == DvtStandaloneGraph.OBJ_LINE_RISER_TOP ||
            objId == DvtStandaloneGraph.OBJ_O1_TITLE ||
            objId == DvtStandaloneGraph.OBJ_X1_TITLE ||
            objId == DvtStandaloneGraph.OBJ_Y1_TITLE ||
            objId == DvtStandaloneGraph.OBJ_Y2_TITLE) {
            this._objId = objId;
        } else if (objId == DvtStandaloneGraph.OBJ_SLICE || 
                   objId == DvtGraphObjType.PIE_SLICE_LABEL ||
                   objId == DvtGraphContext._PIE_SLICE_LABEL_INTERNAL) {
            // For multi pie 
            objId = DvtStandaloneGraph.OBJ_SLICE;
            this._objId = objId;
        }
        this._seriesId = obj.getSeriesId();
        this._groupId = obj.getGroupId();
        this._seriesIndex = obj.getSeriesIndex();
        this._groupIndex = obj.getGroupIndex();
        this._userProps = obj.getUserProps();
        if (this._seriesIndex > -1 && this._groupIndex > -1){
            this._dataColor = obj.getChart().getSeriesColor(this._seriesIndex);
            if (obj._objId && obj._objId.indexOf("BR") == 0) {
                var color = obj.getDetObj().getImpl().getFill();
                if (color instanceof DvtSolidFill)
                    this._dataColor = color.getColor();
                else if (color instanceof DvtGradientFill && DvtArrayUtils.getIndex(color.getColors(), this._dataColor) < 0) 
                    this._dataColor = color.getColors()[0];
            }
            this._bbox = obj.getDetObj().getDimensions(obj.getChart().getCtx().getStage());
            this._tooltip = obj.getDatatip();
        }

    }
    
} ;

/**
 * The object ID of the object
 * @export
 */
DvtGraphContext.prototype.getObjId = function () {
    return this._objId;
} ;

/**
 * The group ID of the object
 * @export
 */
DvtGraphContext.prototype.getGroupId = function () {
    return this._groupId;
} ;

/**
 * The group index of the object
 * @export
 */
DvtGraphContext.prototype.getGroupIndex = function () {
    return this._groupIndex;
} ;

/**
 * The series ID of the object
 * @export
 */
DvtGraphContext.prototype.getSeriesId = function () {
    return this._seriesId;
} ;

/**
 * The series index of the object
 * @export
 */
DvtGraphContext.prototype.getSeriesIndex = function () {
    return this._seriesIndex;
} ;

/**
 * The user defined property of the object
 * @export
 */
DvtGraphContext.prototype.getUserProps = function () {
    return this._userProps;
} ;

/**
 * The data color of the object
 * @export
 */
DvtGraphContext.prototype.getDataColor = function () {
    return this._dataColor;
} ;

/**
 * The bounding box of the object in graph coordinates.
 * @export
 */
DvtGraphContext.prototype.getBBox = function () {
    return this._bbox;
} ;

/**
 * The tooltip of the object
 * @export
 */
DvtGraphContext.prototype.getTooltip = function () {
    return this._tooltip;
} ;
/**
 *  Standalone graph for use outside of ADF.
 *  @param {DvtContext} context The rendering context.
 *  @param {string} id The id of the graph.
 *  @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.
 *  @class
 *  @constructor
 *  @extends {DvtGraph}
 *  @export
 */
var DvtStandaloneGraph = function (context, id, callback, callbackObj) {
  this._Init(context, id, callback, callbackObj);
};

//Constants for component objects
/**   @final @type String  @export */
DvtStandaloneGraph.OBJ_LEGEND_AREA              = DvtGraphObjType.LEGEND_AREA ;
/**   @final @type String  @export */
DvtStandaloneGraph.OBJ_LEGEND_MARKER            = DvtGraphObjType.LEGEND_MARKER ;
/**   @final @type String  @export */
DvtStandaloneGraph.OBJ_LEGEND_TEXT              = DvtGraphObjType.LEGEND_TEXT ;
/**   @final @type String  @export */
DvtStandaloneGraph.OBJ_LEGEND_TITLE             = DvtGraphObjType.LEGEND_TITLE ;
/**   @final @type String  @export */
DvtStandaloneGraph.OBJ_LINE_RISER               = DvtGraphObjType.LINE_RISER ;
/**   @final @type String  @export */
DvtStandaloneGraph.OBJ_LINE_RISER_SIDE          = DvtGraphObjType.LINE_RISER_SIDE ;
/**   @final @type String  @export */
DvtStandaloneGraph.OBJ_LINE_RISER_TOP           = DvtGraphObjType.LINE_RISER_TOP ;
/**   @final @type String  @export */
DvtStandaloneGraph.OBJ_O1_TITLE                 = DvtGraphObjType.O1_TITLE ;
/**   @final @type String  @export */
DvtStandaloneGraph.OBJ_X1_TITLE                 = DvtGraphObjType.X1_TITLE ;
/**   @final @type String  @export */
DvtStandaloneGraph.OBJ_Y1_TITLE                 = DvtGraphObjType.Y1_TITLE ;
/**   @final @type String  @export */
DvtStandaloneGraph.OBJ_Y2_TITLE                 = DvtGraphObjType.Y2_TITLE ;
/**   @final @type String  @export */
DvtStandaloneGraph.OBJ_SLICE                    = DvtGraphObjType.SLICE ;


//Constants for graph/gauge types
/**   @final @type String  @export */
DvtStandaloneGraph.TYPE_LINE_VERT_ABS           = DvtGraphType.LINE_VERT_ABS ; 
/**   @final @type String  @export */
DvtStandaloneGraph.TYPE_PIE                     = DvtGraphType.PIE ; 
/**   @final @type String  @export */
DvtStandaloneGraph.TYPE_DIAL                    = DvtGraphType.DIAL ;
/**   @final @type String  @export */
DvtStandaloneGraph.TYPE_LED                     = DvtGraphType.LED ; 
/**   @final @type String  @export */
DvtStandaloneGraph.TYPE_STATUSMETER             = DvtGraphType.STATUSMETER ;
/**   @final @type String  @export */
DvtStandaloneGraph.TYPE_VERTICALSTATUSMETER     = 'VERTICALSTATUSMETER' ;


DvtObj.createSubclass(DvtStandaloneGraph, DvtGraph, "DvtStandaloneGraph");

/**
  *  Parses and renders the supplied graph or gauge XML.
  *  @param {string} xml The graph or gauge XML.
  *  @export
  */
DvtStandaloneGraph.prototype.setXML = function(xml) {
  DvtStandaloneGraph.superclass.setXML.call(this, xml);
};


/**
  *   Returns the chart type.
  *   @type String
  *   @export
  */
DvtStandaloneGraph.prototype.getType = function () {
   var type = DvtStandaloneGraph.superclass.getType.call(this);
   if (type == DvtStandaloneGraph.TYPE_LINE_VERT_ABS ||
       type == DvtStandaloneGraph.TYPE_PIE ||
       type == DvtStandaloneGraph.TYPE_DIAL ||
       type == DvtStandaloneGraph.TYPE_LED ||
       type == DvtStandaloneGraph.TYPE_STATUSMETER ||
       type == DvtStandaloneGraph.TYPE_VERTICALSTATUSMETER) {
         return type;
       } else {
         return null;
       }
};

/**
 * Returns the graph context object for the given target of an event.
 * @export
 */
DvtStandaloneGraph.prototype.getGraphContext = function(target) {
  var chart = this.getChart();
  if (chart) {
    var targetObj = chart.getLogicalObject(DvtSvgBaseEvent.FindDisplayable(target));
    return chart.getGraphContext(targetObj);
  }
};


/**
 *  Enables or disables tooltips display.
 *  @param {Boolean} bEnable 
 *  @export
 */
DvtStandaloneGraph.prototype.setTooltipDisplay = function(bEnable)
{
  var chart = this.getChart();
  if (chart) {
    chart.enableTooltips(bEnable);
  }
};

/**
 * Returns an Array of graph context objects for the current selection.
 * @export
 */
DvtStandaloneGraph.prototype.getSelectionContext = function () {
  var chart = this.getChart();
  if (chart) {
    return chart.getSelectionContext();
  }
};


/**
 * Returns the selection state as string
 * @export
 */
DvtStandaloneGraph.prototype.getClientProperties = function () {
    var chart = this.getChart();
    if (chart) {
        return chart.getSelectionState();
    }
    return "";
}

/**
 * Explodes all slices of a pie
 * @export
 */
DvtStandaloneGraph.prototype.explodePieAll = function () {
  this.__explodeAll();
};

/**
 * Unites all slices of a pie
 * @export
 */
DvtStandaloneGraph.prototype.unitePieAll = function () {
  this.__uniteAll();
};

/**
 * returns true if the chart/pie has all its slices exploded
 * @export
 */
DvtStandaloneGraph.prototype.isPieAllExploded = function () {
  return this.__isPieAllExploded();
};

/**
 * returns true if the chart/pie has all its slices united (imploded, not exploded)
 * @export
 */
DvtStandaloneGraph.prototype.isPieAllUnited = function () {
  return this.__isPieAllUnited();
};

/**
 * returns the plot area bounds for this graph.
 * @export
 */
DvtStandaloneGraph.prototype.getPlotAreaBounds = function () {
  var chart = this.getChart();
  if (chart) {
    return chart.getFrameRect();
  }
};
/**
 * @export
 * @return {number}
 */
DvtStandaloneGraph.prototype.getGroupCount = function () {
  return parseFloat(this.getAutomation().processSubId("groupCount"));
}

/**
 * @export
 * @return {DvtGraphAxis}
 */
DvtStandaloneGraph.prototype.getO1Axis = function () {
  return new DvtStandaloneGraphAxis("O1", this);
}

/**
 * @export
 * @return {number}
 */
DvtStandaloneGraph.prototype.getSeriesCount = function () {
  return parseFloat(this.getAutomation().processSubId("seriesCount"));
}

/**
 * @export
 * @return {DvtGraphDataAxis}
 */
DvtStandaloneGraph.prototype.getX1Axis = function () {
  return new DvtStandaloneGraphAxis("X1", this);
}

/**
 * @export
 * @return {DvtGraphDataAxis}
 */
DvtStandaloneGraph.prototype.getY1Axis = function () {
  return new DvtStandaloneGraphAxis("Y1", this);
}

/**
 * @export
 * @return {DvtGraphDataAxis}
 */
DvtStandaloneGraph.prototype.getY2Axis = function () {
  return new DvtStandaloneGraphAxis("Y2", this);
}

/**
 * @export
 * @return {string}
 */
DvtStandaloneGraph.prototype.getTitle = function () {
  return this.getAutomation().processSubId("title");
}

/**
 * @export
 * @return {string}
 */
DvtStandaloneGraph.prototype.getLegend = function () {
  return new DvtStandaloneGraphLegend(this);
}

/**
 * @export
 * @param series
 * @param group
 * @return {string}
 */
DvtStandaloneGraph.prototype.getDataMarker = function (series, group) {
  return new DvtStandaloneGraphDataMarker(series, group, this);
}
/**
 * Automation object exposing properties for the graph axis.
 * @class
 * @constructor
 * @param axisId
 * @param graph
 * @export
 */
var DvtStandaloneGraphAxis = function (axisId, graph) {
  this._graph = graph;
  this._axisId=axisId;
}

DvtObj.createSubclass(DvtStandaloneGraphAxis, DvtObj, "DvtStandaloneGraphAxis");

/**
 * Returns the title of the Axis, if one exists
 * @export
 * @return {string}
 */
DvtStandaloneGraphAxis.prototype.getTitle = function () {
  return this._graph.getAutomation().processSubId("axis[" + this._axisId+ "]#title");// call into flash using existing api from Teck which uses subids.
}
/**
 * Automation object exposing properties for the graph data marker.
 * @class
 * @constructor
 * @param {number} series
 * @param {number} group
 * @param graph
 * @export
 */
function DvtStandaloneGraphDataMarker(series, group, graph) {
  this._series = series;
  this._group = group;
  this._graph = graph;
  this._subId ="data[" + series +"][" + group +"]";
}
DvtObj.createSubclass(DvtStandaloneGraphDataMarker, DvtObj, "DvtStandaloneGraphDataMarker");
/**
 * @export
 */
DvtStandaloneGraphDataMarker.prototype.getColor = function () {
  return this._graph.getAutomation().processSubId(this._subId + "#color");
}

/**
 * @export
 * @return {string}
 */
DvtStandaloneGraphDataMarker.prototype.getText = function () {
  return this._graph.getAutomation().processSubId(this._subId + "#text");
}

/**
 * @export
 */
DvtStandaloneGraphDataMarker.prototype.getTooltip = function () {
  return this._graph.getAutomation().processSubId(this._subId + "#tooltip");
}

/**
 * @export
 * @return Actual value for funnel graphs.  For all other graphs, returns null.
 */
DvtStandaloneGraphDataMarker.prototype.getValueActual = function () {
  return parseFloat(this._graph.getAutomation().processSubId(this._subId + "#unformattedValueActual"));
}

/**
 * @export
 * @return Close value for stock graphs.  For all other graphs, returns null.
 */
DvtStandaloneGraphDataMarker.prototype.getValueClose = function () {
  return parseFloat(this._graph.getAutomation().processSubId(this._subId + "#valueClose"));
}

/**
 * @export
 * @return High value for stock graphs.  For all other graphs, returns null.
 */
DvtStandaloneGraphDataMarker.prototype.getValueHigh = function () {
  return parseFloat(this._graph.getAutomation().processSubId(this._subId + "#valueHigh"));
}

/**
 * @export
 * @return Low value for stock graphs.  For all other graphs, returns null.
 */
DvtStandaloneGraphDataMarker.prototype.getValueLow = function () {
  return parseFloat(this._graph.getAutomation().processSubId(this._subId + "#valueLow"));
}

/**
 * @export
 * @return Open value for stock graphs.  For all other graphs, returns null.
 */
DvtStandaloneGraphDataMarker.prototype.getValueOpen = function () {
  return parseFloat(this._graph.getAutomation().processSubId(this._subId + "#valueOpen"));
}

/**
 * @export
 * @return Target value for funnel graphs.  For all other graphs, returns null.
 */
DvtStandaloneGraphDataMarker.prototype.getValueTarget = function () {
  return parseFloat(this._graph.getAutomation().processSubId(this._subId + "#unformattedValueTarget"));
}

DvtStandaloneGraphDataMarker.prototype.getValueX = function () {
  return parseFloat(this._graph.getAutomation().processSubId(this._subId + "#valueX"));
}

/**
 * @export
 */
DvtStandaloneGraphDataMarker.prototype.getValueY = function () {
  return parseFloat(this._graph.getAutomation().processSubId(this._subId + "#valueY"));
}

/**
 * @export
 * @return z-value for bubble graphs.  For all other graphs, returns null.
 */
DvtStandaloneGraphDataMarker.prototype.getValueZ = function () {
  return parseFloat(this._graph.getAutomation().processSubId(this._subId + "#valueZ"));
}
/**
 * Automation object exposing properties for the graph legend.
 * @class
 * @constructor
 * @export
 */
var DvtStandaloneGraphLegend = function (graph) {
  this._graph = graph;
}

DvtObj.createSubclass(DvtStandaloneGraphLegend, DvtObj, "DvtStandaloneGraphLegend");

/**
 * Returns the title of the legend, if one exists
 * @return {string}
 * @export
 */
DvtStandaloneGraphLegend.prototype.getTitle = function () {
  return this._graph.getAutomation().processSubId("legendTitle");
}
// Function wrapper to prevent naming collisions between graph and shared toolkit code.

// Export necessary variables that are accessed by the peer or non-ADF customers.
if(typeof(window) != undefined) {
  window['DvtGraph'] = DvtGraph;
  window['DvtGraphClipPath'] = DvtGraphClipPath;
  window['DvtStandaloneGraph'] = DvtStandaloneGraph;
  
  // Ideally these DvtSvg* could be renamed, but they are public and used by BI.
  window['DvtSvgContext'] = DvtSvgContext;
  window['DvtSvgUtils'] = DvtSvgUtils;
  
  // Events
  window['DvtGraphActionEvent'] = DvtGraphActionEvent;
  window['DvtGraphContextMenuEvent'] = DvtGraphContextMenuEvent;
  window['DvtGraphContextMenuSelectEvent'] = DvtGraphContextMenuSelectEvent;
  window['DvtGraphHidePopupEvent'] = DvtGraphHidePopupEvent;
  window['DvtGraphSelectionEvent'] = DvtGraphSelectionEvent;
  window['DvtGraphShowPopupEvent'] = DvtGraphShowPopupEvent;
  window['DvtGraphShowPopupBehavior'] = DvtGraphShowPopupBehavior;
}

})();
