/********************************************************************************
  Copyright, 2005, Intergraph Corporation. All Rights Reserved.
  The product licensee is granted permission to copy and distribute 
  this script only to support the product SVG output and licensee's 
  associated web sites, and to prepare derivative works solely for 
  internal use. All other rights reserved.
**********************************************************************************/


/*********************************************************/
//---  BASIC FUNCTIONS  --- 
// Requires:
//   -global CORE FUNCTIONS object
//   -global BASIC MOUSE EVENTS object
/*********************************************************/


//---BASIC OBJECT/INTERFACE------------------------------
//--------------------------------------------------------
function _gwmBasicFunctionsObject()
{
  //--METHODS-----
  this.applyPrecision       = _gwmApplyPrecision;
  this.attachActiveCursor   = _gwmAttachActiveCursor;
  this.calcDistance         = _gwmCalcDistance;
  this.clearCircle          = _gwmClearCircle;
  this.clearPath            = _gwmClearPath;
  this.clearPoint           = _gwmClearPoint;
  this.clearRectangle       = _gwmClearRectangle; 
  this.clearMultiple        = _gwmClearMultiple;  
  this.cmdCaptureDone       = _gwmCmdCaptureDone; 
  this.cmdCaptureInit       = _gwmCmdCaptureInit; 
  this.cmdCaptureInterrupt  = _gwmCmdCaptureInterrupt;
  this.cmdCaptureReset      = _gwmCmdCaptureReset;
  this.cmdViewNavDone       = _gwmCmdViewNavDone;
  this.cmdViewNavInit       = _gwmCmdViewNavInit;

  this.cmdPanInit           = _gwmCmdPanInit;
  this.cmdPanDone           = _gwmCmdPanDone;
  
  this.drawCircle           = _gwmDrawCircle;
  this.drawPath             = _gwmDrawPath;
  this.drawPoint            = _gwmDrawPoint;
  this.drawRectangle        = _gwmDrawRectangle;
  this.drawText             = _gwmDrawText;
  
  this.getAllGraphicsLayer  = _gwmGetAllGraphicsLayer;
  this.getDynamicLineStyle  = _gwmGetDynLineStyle;
  this.getDynamicShapeStyle = _gwmGetDynShapeStyle; 
  
  this.getEleChild          = _gwmGetEleChild;
  this.getEleLayer          = _gwmGetEleLayer;
  this.getElement           = _gwmGetElement; 
  this.getLayerAttribute    = _gwmGetLayerAttribute;
  this.getLayerByName       = _gwmGetLayerByName;
  this.getLayerElements     = _gwmGetLayerElements;
  this.getLayerNames        = _gwmGetLayerNames;
  this.getMetadata          = _gwmGetMetadataImp;
  this.getPntViewCtrlPixDim = _gwmGetPntViewCtrlPixDim;
  this.getSVGPntViewCtrlPixDim = _gwmGetSVGPntViewCtrlPixDim;
  this.getTextEleText       = _gwmGetTextEleText;

  this.getViewRange         = _gwmGetViewRange;
  
  this.findElementInGroup   = _gwmFindElementInGroup;
  
  this.initMeasure              = _gwmInitMeasure;
  this.initGWMSVG               = initializeGWMSVG;
  this.insertLayerWithPriority  = _gwmInsertLayerWithPriority;
  this.isEleHotspotEnabled      = _gwmIsEleHotspotEnabled;
  this.isEleLayerEnabled        = _gwmIsEleLayerEnabled;
  this.isEleTooltipEnabled      = _gwmIsEleTooltipEnabled;
  this.isValidFunction          = _gwmIsValidFunction;

  this.moveObject               = _gwmMoveObject;
  
  this.resetProperty        = _gwmResetBASProperty;
  this.replaceNLMarker      = _gwmReplaceNLMarker;
  
  this.setActiveCursor         = _gwmSetActiveCursor;
  this.setCalloutTextPlacement = _gwmSetCalloutTextPlacement;
  this.setDrawingState         = _gwmSetDrawingState;
  this.setDynamicLineStyle     = _gwmSetDynLineStyle;
  this.setDynamicShapeStyle    = _gwmSetDynShapeStyle; 
  this.setGlobalCoordArray     = _gwmSetGlobalCoordArray;
  this.setGlobalCoordlist      = _gwmSetGlobalCoordlist;
  this.setLayerAttribute       = _gwmSetLayerAttribute;
  this.shiftViewProcess        = _gwmShiftViewProcess;
  
  this.transGlobalPnts                  = _gwmTransGlobalPnts;
  this.transGlobalPntsMeasureToViewer   = _gwmTransGlobalPntsMeasureToViewer;
  this.transGlobalPntsReadoutToViewer   = _gwmTransGlobalPntsReadoutToViewer;
  this.transGlobalPntsStorageToViewer   = _gwmTransGlobalPntsStorageToViewer;
  this.transGlobalPntsToViewer          = _gwmTransGlobalPntsToViewer;
  this.transGlobalPntsViewerToMeasure   = _gwmTransGlobalPntsViewerToMeasure;
  this.transGlobalPntsViewerToReadout   = _gwmTransGlobalPntsViewerToReadout;
  this.transGlobalPntsViewerToStorage   = _gwmTransGlobalPntsViewerToStorage;
  
  this.transXCoordFromViewer            = _gwmTransXCoordFromViewer;
  this.transXCoordMeasureToViewer       = _gwmTransXCoordMeasureToViewer;
  this.transXCoordReadoutToViewer       = _gwmTransXCoordReadoutToViewer;
  this.transXCoordStorageToViewer       = _gwmTransXCoordStorageToViewer;
  this.transXCoordToViewer              = _gwmTransXCoordToViewer;
  this.transXCoordViewerToMeasure       = _gwmTransXCoordViewerToMeasure;
  this.transXCoordViewerToReadout       = _gwmTransXCoordViewerToReadout;
  this.transXCoordViewerToStorage       = _gwmTransXCoordViewerToStorage;
  
  this.transYCoordFromViewer            = _gwmTransYCoordFromViewer;
  this.transYCoordMeasureToViewer       = _gwmTransYCoordMeasureToViewer;
  this.transYCoordReadoutToViewer       = _gwmTransYCoordReadoutToViewer;
  this.transYCoordStorageToViewer       = _gwmTransYCoordStorageToViewer;
  this.transYCoordToViewer              = _gwmTransYCoordToViewer;
  this.transYCoordViewerToMeasure       = _gwmTransYCoordViewerToMeasure;
  this.transYCoordViewerToReadout       = _gwmTransYCoordViewerToReadout;
  this.transYCoordViewerToStorage       = _gwmTransYCoordViewerToStorage;
      
  //--PROPERTIES-----
  this.viewCtrl = null;

  //--functions
  this.captureCallback       = _gwmCmdCaptureCB; //placeholder

  this.panCallback           = _gwmCmdPanCB; //placeholder
  this.evtHandlerGeomCancel  = null; 
  this.evtHandlerClick       = null; 
  // PMW 11/16/05 - SVGScroll event handling
  this.evtHandlerSVGScroll = null;
  // pmw 11/18/05 - SVGZoom event handling
  this.evtHandlerSVGZoom = null;
  
  //--Settings, Modes---
  this.bApplyCoordPrecision = true;
  this.sListSeparator  = "|";     
  this.iCoordPrecision = 8;      
  this.iCoordUnits = 0;  //0=Readout; 1=Storage; 2=Distance,Measure; 100=viewer       
  this.bAutoRep    = false;         
  this.sUserInfo   = ""; 

  //--Measure and Coord Transform Related---
  this.sCoordList = ""; 
  this.ptArray = new Array();
  this.sCmdCaptureCoordList = "";  // see SetCmdCaptureCoordList
  this.ptMapCenter = null;
  this.mtxHolder = null;  //trans Matrix

  this.fStorageOffsetX = 0.0;  
  this.fStorageOffsetY = 0.0; 
  this.fReadoutOffsetX = 0.0; 
  this.fReadoutOffsetY = 0.0; 
  this.fStorageToReadoutScaleX = 0.0; 
  this.fStorageToReadoutScaleY = 0.0; 
  this.fStorageToDistanceScale = 1.0;  
  this.fDisplayToStorageScale  = 0.0; 
  this.fRotation = 0.0;
  this.sReadoutUnit  = "m"; 
  this.sDistanceUnit = "m";  // Map Units of Measure

  //--Drawing, Temp Graphics Related---
  this.sEleDrawPoint        = "";
  this.sEleDrawLine         = ""; 
  this.sEleDrawPolyline     = ""; 
  this.sEleDrawPolygon      = ""; 
  this.sEleDrawRect         = ""; 
  this.sEleDrawCircle       = ""; 
  this.sEleDrawText         = ""; 
  this.sEleDrawCallout      = "";
  this.sEleDrawCalloutLine  = "";
  this.sEleDrawTextKeyin    = ""; //Group Ele
  this.sEleDrawTextKeyinT   = ""; //Virtual Text Ele
  this.sEleDrawTextKeyinR   = ""; //Virtual Rect Ele
  
  this.sDrawText            = "";
  this.activeCursorEle      = null; 
  this.activeCursorTrans    = ""; 
  this.sNewlineMarker       = "GWMNL";
  this.bUseNLMarker         = false; //for misc text ele processing.

  
  //space to offset text from callout's last point
  this.CALLOUTTEXTOFFSET = 130;

  // for any dynamic text (callouts, keyins, etc.)
  this.sDynTxtAnchor = "left";
  this.sDynTxtAlign  = "middle";
  this.fDynTxtLocPntX = 0.0;
  this.fDynTxtLocPntY = 0.0;
  this.dynTxtAnchorPt = null;
  this.iDynTxtW = 0;  //txt BBox Width
  this.iDynTxtH = 0;  //txt BBox Height
  this.iDynTxtNumLines = 0;
  this.iDynTxtHgtPerLine = 0;
  this.iDynTxtStrokeW = 0;
  //this.iDynTxtFontSize = 0;
  this.fDynTxtOffsetRefPntX = 0.0;
  this.fDynTxtOffsetRefPntY = 0.0;
  this.sDynTxtVectorDirX = "";
  this.sDynTxtVectorDirY = "";
  this.fDynTxtVectorSlope = 0.0;


  //--CONSTANTS-----
  
  // newline Regular Expression Pattern;
  // newline=x0a(10)  carriage return/linefeed=x0d (13)
  this.NLREPATTERN = String.fromCharCode(13, 10) + "|" + String.fromCharCode(10, 13);
  this.NL = String.fromCharCode(13, 10);

  this.ELEID_DRAWPOINT    = "gwmdynpoint";
  this.ELEID_DRAWLINE     = "gwmdynline"; 
  this.ELEID_DRAWPOLYLINE = "gwmdynpolyline"; 
  this.ELEID_DRAWPOLYGON  = "gwmdynpolygon"; 
  this.ELEID_DRAWRECT     = "gwmdynrect"; 
  this.ELEID_DRAWCIRCLE   = "gwmdyncircle"; 
  this.ELEID_DRAWTEXT     = "gwmdyntext"; 
  
  //Define Cursors
  this.ELEID_CURSCROSSHAIR  = "gwmcursorcrosshair";
  this.ELEID_CURSPANARRW    = "gwmcursorpanarrows";
  
  this.COORDUNIT_READOUT = 0;  
  this.COORDUNIT_STORAGE = 1; 
  this.COORDUNIT_MEASURE = 2; 
  this.COORDUNIT_VIEWER  = 100; 

  //defaults
  this.DEF_NEWLINEMARKER    = "GWMNL";
  
  //other,debugging,testing
  this.DBGTEXT = "";

}
// Global (g) BasicAPIFunctions (BAF) Object Instance
var _gwmBas = new _gwmBasicFunctionsObject();

//=======================================================
function _gwmPoint()
{
  this.x = 0.00000000;
  this.y = 0.00000000;
}

/*********************************************************/
/*** VIEW CONTROL AND API INIT FUNCTIONS  ****************/     
/*********************************************************/

//=========================================================
function initializeGWMSVG( extViewCtrl, bInitAPI, bInitRLAPI )
{
//alert("initializeGWMSVG");

  if( null == extViewCtrl || extViewCtrl == "" ) return _gwm_E_PARAMNULL;
 
  try
  {
    //--July 2005  Use offsetWidth/Height to insure getting pixel values, especially
    //  in the case of the use of a percent for width/height in the embed tag.
    //  These numeric properties specify the physical coordinates and dimensions 
    //  of the object relative to the object's offset parent (containing element).
    //  REF: MSDN HTML and DHTML Reference
    //  http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/reference/properties.asp
    /***
    _gwmCore.iViewCtrlWidth  = extViewCtrl.width;
    _gwmCore.iViewCtrlHeight = extViewCtrl.height;
    ***/
    _gwmCore.iViewCtrlWidth  = extViewCtrl.offsetWidth;
    _gwmCore.iViewCtrlHeight = extViewCtrl.offsetHeight;    
  }
  catch(e)
  {
    return _gwm_E_UNKNOWN;
  }
  
  _gwmBas.viewCtrl = extViewCtrl;

  // Set Event Handlers if they are not currently set.   
  try
  {
    if( null == _gwmCore.svgDoc )
    {
      // indicates no _gwmSVGOnLoad function was called
      var iRet = _gwmCore.init(extViewCtrl.getSVGDocument());
      if( iRet < 0 ) return iRet;
    }
    
    if( "" == _gwmCore.svgRoot.getAttributeNS(null,"onclick") )
    {
      _gwmCore.svgRoot.setAttributeNS(null, "onclick", "_gwmMouseClick(evt);");
    }
    if( "" == _gwmCore.svgRoot.getAttributeNS(null,"onmouseover") )
    {
      _gwmCore.svgRoot.setAttributeNS(null, "onmouseover", "_gwmMouseOver(evt);");
    }
    if( "" == _gwmCore.svgRoot.getAttributeNS(null,"onmouseout") )
    {
      _gwmCore.svgRoot.setAttributeNS(null, "onmouseout",  "_gwmMouseOut(evt);");
    }
    if( "" == _gwmCore.svgRoot.getAttributeNS(null,"onmousemove") )
    {
      _gwmCore.svgRoot.setAttributeNS(null, "onmousemove", "_gwmMouseMove(evt);");
    }
    if( "" == _gwmCore.svgRoot.getAttributeNS(null,"onmousedown") )
    {
      _gwmCore.svgRoot.setAttributeNS(null, "onmousedown", "_gwmMouseDown(evt);");
    }
    if( "" == _gwmCore.svgRoot.getAttributeNS(null,"onmouseup") )
    {
      _gwmCore.svgRoot.setAttributeNS(null, "onmouseup", "_gwmMouseUp(evt);");
    }
    // PMW 11/16/05 - add SVGScroll event
    _gwmCore.svgRoot.addEventListener("SVGScroll", _gwmOnSVGScroll, false);
    // pmw 11/18/05 - add SVGZoom event
    _gwmCore.svgRoot.addEventListener("SVGZoom", _gwmOnSVGZoom, false);

  }
  catch(e)
  {
    return _gwm_E_UNKNOWN; 
  }

  _gwmInitMeasure();

  if( bInitAPI ) // Func implementation in APIFunctions.js
    _gwmAPI.initAPI( _gwmBas.viewCtrl );

  if( bInitRLAPI ) // Func implementation in RedlineAPIFunctions.js
    _gwmRL.initAPI( _gwmBas.viewCtrl );

  return _gwm_E_SUCCESS;
}

//=========================================================
function _gwmInitMeasure()
{
var sBuf="";
  
  _gwmBas.fStorageOffsetX = parseFloat(_gwmGetMetadataImp(_gwmCore.ns + ":storageoffsetx"));
  _gwmBas.fStorageOffsetY = parseFloat(_gwmGetMetadataImp(_gwmCore.ns + ":storageoffsety"));
  _gwmBas.fStorageToReadoutScaleX = parseFloat(_gwmGetMetadataImp(_gwmCore.ns + ":storagetoreadoutscale"));
  _gwmBas.fReadoutOffsetX = parseFloat(_gwmGetMetadataImp(_gwmCore.ns + ":readoutoffsetx"));
  _gwmBas.fStorageToReadoutScaleY = _gwmBas.fStorageToReadoutScaleX;
  _gwmBas.fReadoutOffsetY = parseFloat(_gwmGetMetadataImp(_gwmCore.ns + ":readoutoffsety"));
  _gwmBas.sReadoutUnit = _gwmGetMetadataImp(_gwmCore.ns + ":readoutunit");
  _gwmBas.fStorageToDistanceScale = parseFloat(_gwmGetMetadataImp(_gwmCore.ns + ":storagetodistancescale"));
  _gwmBas.sDistanceUnit = _gwmGetMetadataImp(_gwmCore.ns + ":distanceunit");
  _gwmBas.fDisplayToStorageScale = parseFloat(_gwmGetMetadataImp(_gwmCore.ns + ":displaytostoragescale"));
  
  //--Implement support for MapSvr Rotation setting
  sBuf = _gwmGetMetadataImp(_gwmCore.ns + ":rotationangle");
  if( sBuf != "" ) 
  {
    _gwmBas.fRotation = parseFloat(sBuf);
    _gwmSetMapCenterPnt();
  }      
}

//=========================================================
// DEPRECATED
// This function uses the deprecated method of reading transformation
// parameters from the BGROUND sheet name. 
function _gwmInitMeasureBGROUND()
{
var sBuf = "";

  sBuf = _gwmGetLayerNames(32);

  if( sBuf == "" ) return;

  var iTmp = sBuf.indexOf( "BGROUND" );
  if( iTmp < 0 ) return; //error in format

  iTmp = iTmp + 7;
  var sLenHolder = String(sBuf);
  sBuf = sBuf.substring( iTmp, sLenHolder.length );

  //--Get array of info; Trans params delimited by semi-colon
  var sArray = sBuf.split( ";" );

  //Skip IP entry at [0]

  var fCGMScaleModeVal = parseFloat(sArray[1]);

  _gwmBas.fStorageOffsetX = parseFloat(sArray[2]);
  _gwmBas.fStorageOffsetY = parseFloat(sArray[3]);

  var fCGMScale = parseFloat(sArray[4]);

  _gwmBas.fStorageToReadoutScaleX = parseFloat(sArray[5]);
  _gwmBas.fReadoutOffsetX = parseFloat(sArray[6]);
  _gwmBas.fStorageToReadoutScaleY = parseFloat(sArray[7]);
  _gwmBas.fReadoutOffsetY = parseFloat(sArray[8]);
  _gwmBas.sReadoutUnit = sArray[9];
  _gwmBas.fStorageToDistanceScale = parseFloat(sArray[10]);
  _gwmBas.sDistanceUnit = sArray[11];

  //Calculate Viewer-to-Map-Server-Storage Scale
  _gwmBas.fDisplayToStorageScale = fCGMScaleModeVal/fCGMScale;

/***
  sBuf = "svg:initMeasure():\n";
  sBuf = sBuf + "DisplayToStorageScale = " + _gwmBas.fDisplayToStorageScale;
  sBuf = sBuf + "\nStorageToReadoutScale = " + _gwmBas.fStorageToReadoutScaleX;
  sBuf = sBuf + "\nStorageToDistanceScale = " + _gwmBas.fStorageToDistanceScale;
  sBuf = sBuf + "\nStorageOffsetX = " + _gwmBas.fStorageOffsetX;
  sBuf = sBuf + "\nStorageOffsetY = " + _gwmBas.fStorageOffsetY;
  sBuf = sBuf + "\nReadoutOffsetX = " +  _gwmBas.fReadoutOffsetX;
  sBuf = sBuf + "\nReadoutOffsetY = " +  _gwmBas.fReadoutOffsetY;
  sBuf = sBuf + "\nDistance Unit = " +  _gwmBas.sDistanceUnit;
  sBuf = sBuf + "\nReadout Unit = " +  _gwmBas.sReadoutUnit;
  window.alert(sBuf);
***/
}


/**********************************************************/
/*** FUNCTIONS TO ACCESS SVG NODES, ATTRIBS ***************/
/**********************************************************/


  //-----------------------------
  /*** LAYERS ******************/
  //-----------------------------

//=========================================================
function _gwmGetAllGraphicsLayer()
{
  return _gwmCore.svgDoc.getElementById("_GWMAll"); 
}


//=========================================================
function _gwmGetLayerFirstSubGroup(sLayer)
{
  var children = _gwmGetLayerSubGroups(sLayer);
  if( null == children || "" == children ) return null; 
  var child = null;
  for( var w = 0; w < children.length; ++w )
  {
    child = children.item(w);    
    //child.nodeType=1=SVGGElement
    if( null != child && "" != child && child.nodeType == 1)
    {
      // EX: <g id="gwmg4" class="d0Q2N5o2n8">
      if( child.getAttribute('id').indexOf("gwmg") != -1 )
        return child;
    }
    child = null;
  }
  return null;
}

//=========================================================
function _gwmGetLayerSubGroups(sLayer)
{
  // 22-Mar-2006: CDF-GWM6.1-Implemented for: starting 6.0, Layer Style Class now on subgroups.
  var ele = _gwmGetLayerByName(sLayer);
  if (null == ele || "" == ele) return null; 
  return ele.childNodes;
}

//=========================================================
function _gwmGetLayerByName(sLayer)
{
  var ele = _gwmCore.svgDoc.getElementById(sLayer);
  
//window.alert("_gwmGetLayerByName (" + sLayer + ") =" + ele );

  if (null == ele || "" == ele)
    return null; // return null if there is no element with id = requested layer name
    
  if (ele.getAttribute(_gwmCore.ns + ":typ") != "l")
    return null; // return null if the element found is not a layer
  
  return ele; // otherwise, we found the layer
}

//=========================================================
function _gwmGetLayerElements()
{
  var list = _gwmGetAllGraphicsLayer().childNodes;
  if (null == list)
    return null;
  
  var ele = null;
  var eleArray = new Array();
  var j = 0;

  for( var i = 0; i < list.length; i++ ) 
  {
    ele = list.item(i);
    if ("g" == ele.nodeName)
    {
      if( "l" == ele.getAttribute(_gwmCore.ns + ":typ") ) // is this a layer?
      {
        eleArray[ j++ ] = ele;
      }
    }
  }
  
  return eleArray;
}

//=========================================================
function _gwmGetLayerNames(layerTypeMask)
{
var eleArray = _gwmGetLayerElements();
var ele = null;
var sBuf = "";
var sLayerNames = "";
var bAdd = false;

  if (null == layerTypeMask || "" == layerTypeMask)
  {
    layerTypeMask = 63;
  }
  else
  {
    layerTypeMask = parseInt(layerTypeMask);
    if (isNaN(layerTypeMask) || layerTypeMask > 63 || layerTypeMask < 1)
      layerTypeMask = 63;
  }
    
  
  for( var i = 0; i < eleArray.length; i++ ) 
  {
    bAdd = false;
    ele = eleArray[ i ];
    sBuf = ele.getAttribute("id"); // get the layer name
    // check the mask
    if (layerTypeMask & 0x01)// 1 - user-defined
    {
      if ("_GWMNoName" != sBuf && "_GWMLogo" != sBuf &&
          "_GWMImage" != sBuf && sBuf.indexOf("BGROUND") < 0 &&
          "gwmselected" != sBuf)
      {
        bAdd = true;
      }
    }
    if (layerTypeMask & 0x04)// 4 - gwm default
    {
      if ("_GWMNoName" == sBuf)
        bAdd = true;
    }
    if (layerTypeMask & 0x08)// 8 - image layer
    {
      if ("_GWMImage" == sBuf)
        bAdd = true;
    }
    if (layerTypeMask & 0x10)// 16 - logo layer (doesn't exist?)
    {
      if ("_GWMLogo" == sBuf)
        bAdd = true;
    }
    if (layerTypeMask & 0x20)// 32 - bground layer
    {
      if (sBuf.indexOf("BGROUND") > -1)
        bAdd = true;
    }
    if (bAdd)
    {
      if( sLayerNames != "" )
        sLayerNames = sLayerNames + _gwmBas.sListSeparator + sBuf;
      else
        sLayerNames = sBuf;
    }
  }
  return sLayerNames;
}

//=========================================================
function _gwmGetEleLayer( ele )
{
  if( ele == null || ele == '' ) return null;
  if (ele.correspondingUseElement > "")
    ele = ele.correspondingUseElement;
  
  var par=ele.parentNode;

  while( par.getAttribute("id") != "gwmroot" && 
         par.getAttribute(_gwmCore.ns + ":typ") != "l" )
  {
    par=par.parentNode;
  }

  if( par.getAttribute("id") == "gwmroot" ) return null;
  return par;
}

//=========================================================
function _gwmInsertLayerWithPriority(layer, priority)
{
  if (null == layer)
    return null; // TODO: throw an error here?
  if (null == priority || "" == priority)
    return null; // TODO: throw an error here?
    
  var newpri = parseInt(priority);
  var allgraphics = _gwmGetAllGraphicsLayer();
  // set the priority on the new layer
  layer.setAttribute(_gwmCore.ns + ":pri", newpri);
  // set the layer gwm type attribute, in case it hasn't been set already
  layer.setAttribute(_gwmCore.ns + ":typ", "l");
  var len = allgraphics.childNodes.length;
  // loop through all layers, finding where this one goes
  var curlayer = null;
  var insertbefore = null;
  for(var i=0;i<len;i++)
  {
    curlayer = allgraphics.childNodes.item(i);
    if( curlayer.nodeType == 1 && 
        curlayer.nodeName == "g" && 
        curlayer.getAttribute(_gwmCore.ns + ":typ") == "l" ) // is a layer
    {
      var pri = parseInt(curlayer.getAttribute(_gwmCore.ns + ":pri"));
      if (pri < newpri)
      {
        insertbefore = curlayer;
        break;
      }
    }
  }
  if (null == insertbefore)
    allgraphics.appendChild(layer);
  else
    allgraphics.insertBefore(layer, insertbefore);
  return layer;
}

//=========================================================
function _gwmGetLayerAttribute(sLayer, sAttributeName)
{
  var layer = _gwmGetLayerByName(sLayer);
  
//window.alert("_gwmGetLayerAttribute (" + sLayer + ") =" + layer );

  if (null == layer)
    return null; // TODO: throw error?
  
  return layer.getAttribute(sAttributeName);
}

//=========================================================
function _gwmSetLayerAttribute(sLayer, sAttributeName, sVal)
{
  var layer = _gwmGetLayerByName(sLayer);
  if (null == layer) return _gwm_E_GETLAYER;
 
  layer.setAttribute(sAttributeName, sVal);
  return _gwm_E_SUCCESS;
}

//=========================================================
function _gwmIsEleLayerEnabled( ele )
{
var bEna = false;
var lyrEle = null;

  lyrEle = _gwmGetEleLayer( ele );  //layer node
  if( lyrEle == null ) return true;
  else
  {
    var sLyrEna = lyrEle.getAttribute(_gwmCore.ns + ":layerenabled");
    if( (sLyrEna == "true" || sLyrEna == "") )
      bEna = true; 
  }
  return bEna;
}

//=========================================================
function _gwmIsEleHotspotEnabled( ele )
{
var bEna = false;
var lyrEle = null;

  lyrEle = _gwmGetEleLayer( ele );  //layer node
  if( lyrEle == null ) return true;
  else
  {
    var sLyrEna = lyrEle.getAttribute(_gwmCore.ns + ":layerenabled");
    var sHspEna = lyrEle.getAttribute(_gwmCore.ns + ":hotspotsenabled");

    if( (sLyrEna == "true" || sLyrEna == "") &&
        (sHspEna == "true" || sHspEna == "") )
      bEna = true; 
  }
  return bEna;
}

//=========================================================
function _gwmIsEleTooltipEnabled( ele )
{
var bEna = false;
var lyrEle = null;

  lyrEle = _gwmGetEleLayer( ele );  //layer node
  if( lyrEle == null ) return true;
  else
  {
    var sLyrEna = lyrEle.getAttribute(_gwmCore.ns + ":layerenabled");
    var sTtpEna = lyrEle.getAttribute(_gwmCore.ns + ":tooltipsenabled");

    if( (sLyrEna == "true" || sLyrEna == "") &&
        (sTtpEna == "true" || sTtpEna == "") )
      bEna = true; 
  }
  return bEna;
}


  //---------------------------------
  /*** ELEMENTS, NODES *************/
  //---------------------------------


//========================================================
function _gwmCreateTspan( sTxt, offset)
{
var doc = _gwmCore.svgDoc;

  var svgns = "http://www.w3.org/2000/svg";
  var tspan  = doc.createElementNS(svgns, "tspan");
  var data   = doc.createTextNode(sTxt);

  if( null == offset || "" == offset ) offset = "1em";
  
  tspan.setAttributeNS(null, "x", 0);
  tspan.setAttributeNS(null, "dy", offset);
  tspan.appendChild(data);
  return tspan;
}

//=========================================================
function _gwmGetElement(sLayer, sID)
{
  var layer = _gwmGetLayerByName(sLayer);
  if (null == layer)
    return null; // return null if we can't find the layer
    
  return _gwmFindElementInGroup(layer, sID);
}

//=========================================================
function _gwmFindElementInGroup(group, id)
{
  // find the element
  var children = group.childNodes;
  var len = children.length;
  var child = null;
  for(var i=0;i<len;i++)
  {
    child = children.item(i);
    if (child.nodeType != 1)
      continue;
    if (child.nodeName == "g")
    {
      // a subgroup (a separate feature set defined in mapsvr with the same
      // sheet name)
      var temp = _gwmFindElementInGroup(child, id);
      if (null != temp && "" != temp)
        return temp;
    }
    else if (child.getAttribute("id") == id)
    {
      break;
    }
  }
  if (i == len)
    child = null;
  
  return child; // child will either be the requested element or null if not found
}

//=========================================================
function _gwmGetTextEleText( ele )
{
  if( !ele ) return "";
  if( !ele.hasChildNodes() ) return "";
  if( ele.firstChild.nodeType != 3 ) return "";
    
  var children = ele.childNodes;
  var child = null;
  var sBuf = "";
 
  for( var w = 0; w < children.length; ++w )
  {
    child = children.item(w);      
    if( child.nodeName == 'tspan' )
    {
      if( !_gwmBas.bUseNLMarker )
        sBuf = sBuf + _gwmBas.NL;
      else
        sBuf = sBuf + _gwmBas.sNewlineMarker;
      
      sBuf = sBuf + child.firstChild.nodeValue;
    }
    else
      sBuf = sBuf + child.nodeValue;
  }
    
  return sBuf;
}
  
//=========================================================
function _gwmGetTextEleNumLines( ele )
{
  if( !ele ) return _gwm_E_GETELEMENT;
  var cnt = 0;

  if( ele.hasChildNodes()  
      && ele.firstChild.nodeType == 3 )
  {
    ++cnt;
    var children = ele.childNodes;
    var child = null;
    for( var w = 0; w < children.length; ++w )
    {
      child = children.item(w);
      if( child.nodeName == 'tspan' ) ++cnt;
    }
  }

  return cnt;
}

//=========================================================
function _gwmGetEleChild( ele, iChildType, sChildID )
{
  if( !ele ) return null;

  var children = ele.childNodes;
  var child = null;
  for( var w = 0; w < children.length; ++w )
  {
    child = children.item(w);
    if( child.nodeType == iChildType && 
        child.getAttribute('id') == sChildID )
      return child;
  }
  return null;
}

  
/****************************************/
/*** COMMAND, INTERACTIVE PROCESSING  ***/
/****************************************/


//=========================================================
function _gwmCmdCaptureCB()
{
  //a stub/placeholder for this.captureCallback
  return _gwm_E_SUCCESS;
}

//=========================================================
function _gwmCmdPanCB()
{
  //a stub/placeholder for this.panCallback
  return _gwm_E_SUCCESS;
}

//=========================================================
function _gwmCmdCaptureInit( iState, Callback, sInfo )
{
  //-clear other states and init Globals

  _gwmSetDrawingState( false );

  _gwmBasME.iListenerState      = iState;
  _gwmBasME.onCompletionHandler = _gwmCmdCaptureDone;
  _gwmBasME.onInterruptHandler  = _gwmCmdCaptureInterrupt;

  _gwmBas.captureCallback  = Callback;  //func pointer

  _gwmBas.sUserInfo = sInfo;
  _gwmBas.sCoordList = ""; 
  _gwmBas.ptArray = null;
  _gwmBas.ptArray = new Array(); 

  _gwmBas.sEleDrawLine     = _gwmBas.ELEID_DRAWLINE;
  _gwmBas.sEleDrawPolyline = _gwmBas.ELEID_DRAWPOLYLINE;
  _gwmBas.sEleDrawPolygon  = _gwmBas.ELEID_DRAWPOLYGON;
  _gwmBas.sEleDrawRect     = _gwmBas.ELEID_DRAWRECT;
  _gwmBas.sEleDrawCircle   = _gwmBas.ELEID_DRAWCIRCLE;

  _gwmSetDrawingState( true );
}

//=========================================================
function _gwmCmdCaptureDone()
{
var bCb=false;

  _gwmTransGlobalPnts(true); 
  _gwmSetGlobalCoordlist();

  if( _gwmIsValidFunction(_gwmBas.captureCallback) )
  {
    //store variables before resetting.
    var cl = _gwmBas.sCoordList;
    var ui = _gwmBas.sUserInfo;
    bCb = true;
  }

  //reset before callback; else we're waiting on callback to return before we can finish.
  _gwmCmdCaptureReset( false );
  
  if( bCb ) _gwmBas.captureCallback(cl, ui);

  return;
}

//=========================================================
function _gwmCmdCaptureInterrupt()
{
  _gwmCmdCaptureReset( true );
  if( _gwmIsValidFunction(_gwmBas.evtHandlerGeomCancel) )
    _gwmBas.evtHandlerGeomCancel( _gwmBas.sUserInfo );
}

//=========================================================
function _gwmCmdCaptureReset( bInterrupted )
{
  _gwmSetDrawingState( false );

  //--In autorep mode, don't start it again until user has initiated it.
  if( bInterrupted || !_gwmBas.bAutoRep )
  {
    _gwmBasME.iListenerState = _gwmBasME.IDLE; 
  }
  else if( _gwmBas.bAutoRep )
    _gwmSetDrawingState( true );

  _gwmBas.ptArray = null;
  _gwmBas.ptArray = new Array();
  _gwmBas.sCoordList = "";
}

//=========================================================
function _gwmCmdViewNavInterrupt()
{
  _gwmBasME.iListenerState = _gwmBasME.IDLE;
}


//=========================================================
function _gwmCmdPanInit( UserCallback, sUserInfo )
{
  _gwmBasME.onCompletionHandler = _gwmCmdPanDone;
  _gwmBas.panCallback = UserCallback;
  _gwmBas.sUserInfo = sUserInfo;
  _gwmBas.bApplyCoordPrecision = false;
  _gwmBas.sCoordList = ""; 
  _gwmBas.ptArray = null;
  _gwmBas.ptArray = new Array(); 
  _gwmBasME.onInterruptHandler = _gwmCmdPanInterupt;
  //_gwmSetActiveCursor( 'gwmcursorpanarrows', true );
  _gwmSetActiveCursor( 'gwmcursorcrosshair', true );
  //_gwmSetActiveCursor( "", false );
  _gwmBasME.iListenerState = _gwmBasME.PANREADY;
}


//=========================================================
function _gwmCmdPanDone()
{
  if( !(_gwmBas.bAutoRep) )
    _gwmCmdPanReset(false);

  if( _gwmIsValidFunction(_gwmBas.panCallback) )
  {
    _gwmTransGlobalPnts(true); 
    _gwmBas.bApplyCoordPrecision = true;
    _gwmSetGlobalCoordlist();
    _gwmBas.panCallback(_gwmBas.sCoordList, _gwmBas.sUserInfo);
  }

  if( _gwmBas.bAutoRep )
  {
    _gwmBas.sCoordList = ""; 
    _gwmBas.ptArray = null;
    _gwmBas.ptArray = new Array(); 
    _gwmBasME.iListenerState = _gwmBasME.PANREADY;
  }

  return;
}
  
//=========================================================
function _gwmCmdPanInterupt()
{
  _gwmCmdPanReset(true);
}

//=========================================================
function _gwmCmdPanReset(bDoCB)
{
  _gwmBasME.iListenerState = _gwmBasME.IDLE;
  _gwmSetActiveCursor( "", false );
  _gwmBasME.onCompletionHandler = null;
  _gwmBasME.onInterruptHandler = null;
  _gwmBas.activeCursorTrans = "";
  if( bDoCB ) 
  {
    _gwmBas.panCallback = null;
    _gwmBas.sUserInfo = "";
  }
  return;
}

//=========================================================
function _gwmCmdViewNavInit( iState, callback, sUserInfo )
{
  _gwmBasME.iListenerState      = iState;
  _gwmBasME.onCompletionHandler = _gwmCmdViewNavDone;
  _gwmBas.captureCallback = callback;
  _gwmBas.sUserInfo = sUserInfo;
  _gwmBasME.onInterruptHandler = _gwmCmdViewNavInterrupt;
}

//=========================================================
function _gwmCmdViewNavDone( ele )
{
  if( _gwmBasME.iListenerState == _gwmBasME.IDLE ) return;

  if( _gwmBasME.iListenerState == _gwmBasME.GETELEMENT )
  {
    if (null == ele || "" == ele)
    {
      // background click or something
      if( !(_gwmBas.bAutoRep) )
        _gwmBasME.iListenerState = _gwmBasME.IDLE;
      return null;
    }

    if( _gwmIsValidFunction(_gwmBas.captureCallback) )
      _gwmBas.captureCallback(ele, _gwmBas.sUserInfo);
  }

  if( !(_gwmBas.bAutoRep) )
    _gwmBasME.iListenerState = _gwmBasME.IDLE;
  return;
}
 
//=========================================================
function _gwmShiftViewProcess(sCoords)
{
  try
  {
    // get the shift coords. [0] is num of verts; ignore.
    var coords = sCoords.split(_gwmBas.sListSeparator);
    var x1 = coords[1];
    var y1 = coords[2];
    var x2 = coords[3];
    var y2 = coords[4];
    
    // get the current view coords
    var viewcoords = _gwmBas.getViewRange();
    coords = viewcoords.split(_gwmBas.sListSeparator);
    var vx1 = coords[0];
    var vy1 = coords[1];
    var vx2 = coords[2];
    var vy2 = coords[3];

    // calculate the shift and perform it
    var fx = ((x2-x1) / (vx2-vx1));
    var fy = (-(y2-y1) / (vy2-vy1));  

    _gwmShiftView(fx, fy);
    return _gwm_E_SUCCESS;
  }
  catch(e)
  {
    return _gwm_E_UNKNOWN;
  }
}


//=========================================================
function _gwmShiftView(factorX, factorY)
{
  try
  {
      // verify inputs
    if( _gwmIsNull(factorX) || _gwmIsNull(factorY) )
      return _gwm_E_PARAMNULL;
      
    // get the pixel dimensions
    var root = _gwmCore.svgRoot;
    var pnt  = _gwmBas.getPntViewCtrlPixDim();
  
    // get the pan distance and update
    var panx = pnt.x * factorX;
    var pany = pnt.y * factorY;

/***
_gwmBas.DBGTEXT = _gwmBas.DBGTEXT
  + "\n   ShiftView by factor on Root " 
  + "\n   ViewCtrlPixDim x,y=" + pnt.x+", "+pnt.y
  + "\nx,y shift amount=" + panx+", "+pany
  ;
***/
    root.currentTranslate.x += panx;
    root.currentTranslate.y -= pany;
    return _gwm_E_SUCCESS;
  }
  catch(e)
  {
/***  
_gwmBas.DBGTEXT = _gwmBas.DBGTEXT
  + "\n   ShiftView CATCHES ERR="+e
***/  
    return _gwm_E_UNKNOWN;
  }
}

//=========================================================
function _gwmMoveObject(loc, bIsGroup, objID)
{
  try
  {
    var ele = document.getElementById(objID);
    if( !ele ) return;
    if( bIsGroup )
    {
      ele.setAttribute('transform', 'translate(' + loc.x + ',' + loc.y +')');
    }
    else
    {
      ele.setAttribute('x', loc.x);
      ele.setAttribute('y', loc.y);
    }
  }
  catch(e)
  {
    return;
  }

  return;
}



/****************************************/
/*** COORDS, TRANSFORMATION FUNCTIONS ***/
/****************************************/


//=========================================================
function _gwmGetViewRange(bNoTrans)
{
  try
  {
    var x1, x2, y1, y2;
    var root = _gwmCore.svgRoot;
    
    // get the pixel dimensions
    var pixeldims = _gwmBas.getSVGPntViewCtrlPixDim();
    
    // get the svg/viewer points for top left & bottom right corners
    var matrix = _gwmCore.getSVGTransMatrix();
    var viewPoint1 = root.createSVGPoint();
    var viewPoint2 = root.createSVGPoint();
    viewPoint2 = pixeldims.matrixTransform(matrix);
    pixeldims.x = 0;
    pixeldims.y = 0;
    viewPoint1 = pixeldims.matrixTransform(matrix);
    
    if( bNoTrans == undefined )
    {
      // translate the input coords to current coord units
      x1 = _gwmBas.applyPrecision(_gwmBas.transXCoordFromViewer(viewPoint1.x, _gwmBas.iCoordUnits));
      y1 = _gwmBas.applyPrecision(_gwmBas.transYCoordFromViewer(viewPoint1.y, _gwmBas.iCoordUnits));
      x2 = _gwmBas.applyPrecision(_gwmBas.transXCoordFromViewer(viewPoint2.x, _gwmBas.iCoordUnits));
      y2 = _gwmBas.applyPrecision(_gwmBas.transYCoordFromViewer(viewPoint2.y, _gwmBas.iCoordUnits));
    }  
    else
    {
      x1 = viewPoint1.x;
      y1 = viewPoint1.y;
      x2 = viewPoint2.x;
      y2 = viewPoint2.y;
    }
    var listsep = _gwmGetListSeparator();
    return x1 + listsep + y1 + listsep + x2 + listsep + y2;
  }
  catch(e)
  {
    return _gwm_E_UNKNOWN;
  }
}

//=========================================================
function _gwmApplyPrecision(num)
{
  var s = String(num);
  var sint = "";
  var sfraction = "";
  if (_gwmBas.iCoordPrecision == 0)
  {
  	// integers
  	s = String(parseInt(num));
  }
  else if (_gwmBas.iCoordPrecision > 0)
  {
    // first check for a decimal, return the whole number if not found
    if (s.indexOf(".") == -1)
      return s;
  	// get the integer & fraction parts of the number
  	sfraction = s.substring(s.indexOf(".")+1, s.length);
  	sint = s.substring(0, s.indexOf("."));
  	numdeci = sfraction.length;
    
  	if (numdeci > _gwmBas.iCoordPrecision)
  	{
    	// if we have more precision than is required, truncate
  		sfraction = sfraction.substr(0, _gwmBas.iCoordPrecision);
  	}
  	else
  	{
  		// otherwise, pad with 0
  		for(var i = 0;i<(_gwmBas.iCoordPrecision - numdeci);i++)
  		{
  			sfraction += "0";
  		}
  	}
  	s = sint + "." + sfraction;
  }
  return s;
}

//=========================================================
function _gwmSetGlobalCoordlist()
{
var pts = _gwmBas.ptArray;

  if( pts.length < 1 ) return;

  var p = null;
  var sBuf = "";
  var sep  = _gwmBas.sListSeparator;
  var sx = "";
  var sy = "";
  var x0 = ""; //first x, y
  var y0 = "";
  
  for( var i = 0; i < pts.length; ++i )
  {
    p = pts[ i ];
    
    if (_gwmBas.bApplyCoordPrecision)
    {
    	sx = _gwmApplyPrecision(p.x);
    	sy = _gwmApplyPrecision(p.y);
    }
    else
    {
    	sx = p.x;
    	sy = p.y;
    }
    if( sBuf != "" )
      sBuf = sBuf + sep + sx + sep + sy;        
    else //process 1st pt
    {
      sBuf = sx + sep + sy;
      x0 = sx; // store for polygons
      y0 = sy;
    }
  }

  if( _gwmBasME.iListenerState != _gwmBasME.POLYGONMODE )
    sBuf = pts.length + sep + sBuf;
  else
  {
    // add last point same as first to close poly
    sBuf = (pts.length+1) + sep + sBuf;
    sBuf = sBuf + sep + x0 + sep + y0; 
  }

  _gwmBas.sCoordList = sBuf;
  return;
}

//=========================================================
function _gwmSetGlobalCoordArray( sCoordList )
{

  if( sCoordList == "" ) return sCoordList;
  
var iTmp, iNumPts;
var sBuf, sLenHolder;
var fX, fY, p;
var sep = _gwmBas.sListSeparator;

  sBuf = sCoordList; 

  //--Number of points 
  iTmp = sBuf.indexOf( sep );  
  iNumPts = parseInt(sBuf.substring(0,iTmp));
  if( iNumPts < 1 ) return "";

  sLenHolder = String(sBuf);
  sBuf = sBuf.substring( iTmp+1, sLenHolder.length );  //Skip over previous

  _gwmBas.ptArray = null;
  _gwmBas.ptArray = new Array();

  for( i = 0; i < iNumPts; ++i )
  {
    //--X
    iTmp = sBuf.indexOf( sep );
    fX = parseFloat(sBuf.substring(0,iTmp));
    //--Y
    sLenHolder = String(sBuf);
    sBuf = sBuf.substring( iTmp+1, sLenHolder.length );  //skip over previous
    iTmp = sBuf.indexOf( sep );  //separate Coord pairs 
    if( iTmp != -1 )
    {  
      fY = parseFloat(sBuf.substring(0,iTmp));    
      sLenHolder = String(sBuf);
      sBuf = sBuf.substring( iTmp+1, sLenHolder.length );  //Skip over previous
    }
    else
    {
      //--At end of string
      sLenHolder = String(sBuf);      
      fY = parseFloat(sBuf.substring(0,sLenHolder.length)); 
      sBuf = "";  
    }
    
    p = new _gwmPoint();
    p.x = fX;
    p.y = fY;
    _gwmBas.ptArray[ i ] = p;
  }

}

//=========================================================
function _gwmSetMapCenterPnt()
{
  // set MapCenterPnt in ViewBox coords  
  _gwmBas.ptMapCenter = null; //reset
  _gwmBas.ptMapCenter = new _gwmPoint(); 
  _gwmBas.ptMapCenter.x = _gwmCore.iViewW/2;
  _gwmBas.ptMapCenter.y = _gwmCore.iViewH/2;
}

//=========================================================
function _gwmTransGlobalPntsStorageToViewer()
{
  if( _gwmBas.ptArray.length <= 0 ) return;
  
var pts = _gwmBas.ptArray;
var p, p2;

  for( var i = 0; i < pts.length; ++i )
  {
    p = pts[ i ];
    // Create new Point for storage. Must use diff var or ret values get rounded up.
    p2 = new _gwmPoint();
    p2.x = _gwmTransXCoordStorageToViewer( p.x );
    p2.y = _gwmTransYCoordStorageToViewer( p.y );
    pts[ i ] = p2;
  }
  _gwmBas.ptArray = pts;
}

//=========================================================
function _gwmTransGlobalPntsMeasureToViewer()
{
  if( _gwmBas.ptArray.length <= 0 ) return;
  
var pts = _gwmBas.ptArray;
var p, p2;

  for( var i = 0; i < pts.length; ++i )
  {
    p = pts[ i ];
    // Create new Point for storage. Must use diff var or ret values get rounded up.
    p2 = new _gwmPoint();
    p2.x = _gwmTransXCoordMeasureToViewer( p.x );
    p2.y = _gwmTransYCoordMeasureToViewer( p.y );
    pts[ i ] = p2;
  }
  _gwmBas.ptArray = pts;
}

//=========================================================
function _gwmTransGlobalPntsReadoutToViewer()
{
  if( _gwmBas.ptArray.length <= 0 ) return;
  
var pts = _gwmBas.ptArray;
var p, p2 = null;

  for( var i = 0; i < pts.length; ++i )
  {
    p = pts[ i ];
    p2 = new _gwmPoint();
    p2.x = _gwmTransXCoordReadoutToViewer( p.x );
    p2.y = _gwmTransYCoordReadoutToViewer( p.y );
    pts[ i ] = p2;
  }
  _gwmBas.ptArray = pts;
}

//=========================================================
function _gwmTransGlobalPntsToViewer()
{
  if( _gwmBas.iCoordUnits == _gwmBas.COORDUNIT_READOUT ) 
    _gwmTransGlobalPntsReadoutToViewer();
  else if( _gwmBas.iCoordUnits == _gwmBas.COORDUNIT_STORAGE ) 
    _gwmTransGlobalPntsStorageToViewer();
  else if( _gwmBas.iCoordUnits == _gwmBas.COORDUNIT_MEASURE ) 
    _gwmTransGlobalPntsMeasureToViewer();
}

//=========================================================
function _gwmTransGlobalPntsUnRotate()
{
  // Applies an un-rotation to Global Coordinates
  
  if( _gwmBas.fRotation == 0.0 || _gwmBas.ptMapCenter == null ) return;
  if( _gwmBas.ptArray.length <= 0 ) return;

  var pts = _gwmBas.ptArray;
  var p, p2;
  var fMapCx, fMapCy;  
  var cX = _gwmBas.ptMapCenter.x;
  var cY = _gwmBas.ptMapCenter.y;

  //--Translate MapCenter to current coord units
  if( _gwmBas.iCoordUnits == _gwmBas.COORDUNIT_READOUT )
  {
    fMapCx = _gwmTransXCoordViewerToReadout(cX);
    fMapCy = _gwmTransYCoordViewerToReadout(cY);
  }
  else if( _gwmBas.iCoordUnits == _gwmBas.COORDUNIT_STORAGE ) 
  {
    fMapCx = _gwmTransXCoordViewerToStorage(cX);
    fMapCy = _gwmTransYCoordViewerToStorage(cY);
  }
  else if( _gwmBas.iCoordUnits == _gwmBas.COORDUNIT_MEASURE ) 
  {
    fMapCx = _gwmTransXCoordViewerToMeasure(cX);
    fMapCy = _gwmTransYCoordViewerToMeasure(cY);
  }
  else
  {
    fMapCx = cX;
    fMapCy = cY;
  }
  
  for( var i = 0; i < pts.length; ++i )
  {
    p = pts[ i ];  
    // Create new Point for storage. Must use diff var or JS Modifies.
    p2 = new _gwmPoint();
    p2.x = _gwmUnRotateCoord( fMapCx, fMapCy, p.x, p.y, true  );
    p2.y = _gwmUnRotateCoord( fMapCx, fMapCy, p.x, p.y, false );
    pts[ i ] = p2;
  }
  _gwmBas.ptArray = pts;
}

//=========================================================
function _gwmUnRotateCoord(cX, cY, rX, rY, bDoX)
{
  //--Convert from rotated to non-rotated coordinate in Storage(CoordUnit) space.
  //  Original Map Center X,Y (cX,cY) transformed to current Coord Units.
  //  Rotated X,Y (rX,rY) coords transformed to Coord Units.
  //  If UnRotating X, then bDoX is set to True.
  
  //Sine and Cosine of rotation angle
  var sinAng = Math.sin(_gwmBas.fRotation * Math.PI/180);
  var cosAng = Math.cos(_gwmBas.fRotation * Math.PI/180);  
  var fResult = 0.0; 
  var sRxMinCx = rX-cX;
  var sRyMinCy = rY-cY;

  if( bDoX )
  {
    fResult = cX + ((rX-cX)*cosAng) + ((rY-cY)*sinAng);            
  }
  else
  {
    //must consider Y direction in SVG is diff (increase as you go down)
    fResult = cY - ((rX-cX)*sinAng) + ((rY-cY)*cosAng);
  }

  return( fResult );
}


//=========================================================
function _gwmTransGlobalPntsViewerToStorage()
{
  if( _gwmBas.ptArray.length <= 0 ) return;
  
var pts = _gwmBas.ptArray;
var p, p2;

  for( var i = 0; i < pts.length; ++i )
  {
    p = pts[ i ];
    // Create new Point for storage. Must use diff var or ret values get rounded up.
    p2 = new _gwmPoint();
    p2.x = _gwmTransXCoordViewerToStorage( p.x );
    p2.y = _gwmTransYCoordViewerToStorage( p.y );
    pts[ i ] = p2;
  }
  _gwmBas.ptArray = pts;
}

//=========================================================
function _gwmTransGlobalPntsViewerToMeasure()
{
  if( _gwmBas.ptArray.length <= 0 ) return;
  
var pts = _gwmBas.ptArray;
var p, p2;
  for( var i = 0; i < pts.length; ++i )
  {
    p = pts[ i ];
    // Create new Point for storage. Must use diff var or ret values get rounded up.
    p2 = new _gwmPoint();
    p2.x = _gwmTransXCoordViewerToMeasure( p.x );
    p2.y = _gwmTransYCoordViewerToMeasure( p.y );
    pts[ i ] = p2;
  }
  _gwmBas.ptArray = pts;
}

//=========================================================
function _gwmTransGlobalPntsViewerToReadout()
{
  if( _gwmBas.ptArray.length <= 0 ) return;
  
var pts = _gwmBas.ptArray;
var p, p2;

  for( var i = 0; i < pts.length; ++i )
  {
    p = pts[ i ];
    // Create new Point for storage. Must use diff var or ret values get rounded up.
    p2 = new _gwmPoint();
    p2.x = _gwmTransXCoordViewerToReadout( p.x );
    p2.y = _gwmTransYCoordViewerToReadout( p.y );    
    pts[ i ] = p2;
  }
  _gwmBas.ptArray = pts;
}

//=========================================================
function _gwmTransGlobalPnts( bApplyUnRotate )
{    
  if( _gwmBas.iCoordUnits == _gwmBas.COORDUNIT_READOUT ) 
    _gwmTransGlobalPntsViewerToReadout();
  else if( _gwmBas.iCoordUnits == _gwmBas.COORDUNIT_STORAGE ) 
    _gwmTransGlobalPntsViewerToStorage();
  else if( _gwmBas.iCoordUnits == _gwmBas.COORDUNIT_MEASURE ) 
    _gwmTransGlobalPntsViewerToMeasure();
    
  if( bApplyUnRotate ) _gwmTransGlobalPntsUnRotate();
}
 
//=========================================================
function _gwmTransXCoordViewerToReadout( fX1 )
{
var fResult = 0.0;  
  fResult = (((fX1/_gwmBas.fDisplayToStorageScale) + 
               _gwmBas.fStorageOffsetX) * 
             _gwmBas.fStorageToReadoutScaleX) + 
             _gwmBas.fReadoutOffsetX;
return fResult;
} 

//=========================================================
function _gwmTransYCoordViewerToReadout( fY1 )
{
var fResult = 0.0;
  fResult = ((_gwmBas.fStorageOffsetY - (fY1/_gwmBas.fDisplayToStorageScale)) * 
               _gwmBas.fStorageToReadoutScaleY) + 
            _gwmBas.fReadoutOffsetY;
return fResult;
} 

//=========================================================
function _gwmTransXCoordViewerToStorage( fX1 )
{
var fResult = 0.0;
  fResult = (fX1/_gwmBas.fDisplayToStorageScale) + 
            _gwmBas.fStorageOffsetX;
return fResult;
} 

//=========================================================
function _gwmTransYCoordViewerToStorage( fY1 )
{
var fResult = 0.0;
  fResult =  _gwmBas.fStorageOffsetY - (fY1/_gwmBas.fDisplayToStorageScale);
return fResult;
}

//=========================================================
function _gwmTransXCoordViewerToMeasure( fX1 )
{
var fResult = 0.0;
  fResult = ((fX1/_gwmBas.fDisplayToStorageScale) + 
              _gwmBas.fStorageOffsetX) * 
            _gwmBas.fStorageToDistanceScale;
return fResult;
}

//=========================================================
function _gwmTransYCoordViewerToMeasure( fY1 )
{
var fResult = 0.0;
  fResult = (_gwmBas.fStorageOffsetY - (fY1/_gwmBas.fDisplayToStorageScale)) * _gwmBas.fStorageToDistanceScale;
return fResult;
} 

//================================================================
function _gwmTransXCoordMeasureToViewer( fX1 )
{
  return (((fX1/_gwmBas.fStorageToDistanceScale) - 
           _gwmBas.fStorageOffsetX) * 
           _gwmBas.fDisplayToStorageScale);
}

//================================================================
function _gwmTransYCoordMeasureToViewer( fY1 )
{
  return (-((fY1/_gwmBas.fStorageToDistanceScale) - _gwmBas.fStorageOffsetY) * 
          _gwmBas.fDisplayToStorageScale);
}

//=====================================================================
function _gwmTransXCoordStorageToViewer( fX1 )
{
  return ((fX1 - _gwmBas.fStorageOffsetX) * _gwmBas.fDisplayToStorageScale);
} 

//=====================================================================
function _gwmTransYCoordStorageToViewer( fY1 )
{
 return (-(fY1 - _gwmBas.fStorageOffsetY) * _gwmBas.fDisplayToStorageScale);
} 

//=========================================================
function _gwmTransXCoordReadoutToViewer( fX1 )
{
var fResult = (((fX1 - _gwmBas.fReadoutOffsetX) / _gwmBas.fStorageToReadoutScaleX) - 
              _gwmBas.fStorageOffsetX) * _gwmBas.fDisplayToStorageScale;
  return fResult;
} 

//=========================================================
function _gwmTransYCoordReadoutToViewer( fY1 )
{
var fResult = (-(((fY1 - _gwmBas.fReadoutOffsetY ) / _gwmBas.fStorageToReadoutScaleY) - 
               _gwmBas.fStorageOffsetY) * _gwmBas.fDisplayToStorageScale);
  return fResult;            
} 

//==========================================================
function _gwmTransXCoordToViewer(x, coordunit)
{
  switch(coordunit)
  {
    case _gwmBas.COORDUNIT_READOUT:
      return _gwmTransXCoordReadoutToViewer(x);
      break;
    case _gwmBas.COORDUNIT_STORAGE:
      return _gwmTransXCoordStorageToViewer(x);
      break;
    case _gwmBas.COORDUNIT_MEASURE:
      return _gwmTransXCoordMeasureToViewer(x);
      break;
  }
  return x; //case where coordunit is Viewer
}

//==========================================================
function _gwmTransYCoordToViewer(y, coordunit)
{
  switch(coordunit)
  {
    case _gwmBas.COORDUNIT_READOUT:
      return _gwmTransYCoordReadoutToViewer(y);
      break;
    case _gwmBas.COORDUNIT_STORAGE:
      return _gwmTransYCoordStorageToViewer(y);
      break;
    case _gwmBas.COORDUNIT_MEASURE:
      return _gwmTransYCoordMeasureToViewer(y);
      break;
  }
  return y; //case where coordunit is Viewer
}

//==========================================================
function _gwmTransXCoordFromViewer(x, coordunit)
{
  switch(coordunit)
  {
    case _gwmBas.COORDUNIT_READOUT:
      return _gwmTransXCoordViewerToReadout(x);
      break;
    case _gwmBas.COORDUNIT_STORAGE:
      return _gwmTransXCoordViewerToStorage(x);
      break;
    case _gwmBas.COORDUNIT_MEASURE:
      return _gwmTransXCoordViewerToMeasure(x);
      break;
  }
  return x;
}

//==========================================================
function _gwmTransYCoordFromViewer(y, coordunit)
{
  switch(coordunit)
  {
    case _gwmBas.COORDUNIT_READOUT:
      return _gwmTransYCoordViewerToReadout(y);
      break;
    case _gwmBas.COORDUNIT_STORAGE:
      return _gwmTransYCoordViewerToStorage(y);
      break;
    case _gwmBas.COORDUNIT_MEASURE:
      return _gwmTransYCoordViewerToMeasure(y);
      break;
  }
  return y;
}
  


/************************************************/
/*** DRAWING SUPPORT FUNCTIONS ******************/
/************************************************/


//=========================================================
function _gwmGetDynLineStyle()
{
  if( !_gwmCore.svgDoc ) return _gwm_E_GETELEMENT;
  var ele = _gwmCore.svgDoc.getElementById(_gwmBas.ELEID_DRAWLINE); 
  if( !ele ) return _gwm_E_GETELEMENT;
  return( ele.getAttribute('style') );
}

//=========================================================
function _gwmGetDynShapeStyle()
{
  if( !_gwmCore.svgDoc ) return _gwm_E_GETELEMENT;
  var ele = _gwmCore.svgDoc.getElementById(_gwmBas.ELEID_DRAWRECT); 
  if( !ele ) return _gwm_E_GETELEMENT;
  return( ele.getAttribute('style') );
}

//=========================================================
function _gwmSetDynLineStyle( sStyle )
{
var doc = _gwmCore.svgDoc;
var ele = null;

  if( null == sStyle || sStyle == "") return _gwm_E_PARAMNULL;
  if( !doc ) return _gwm_E_GETELEMENT;

  ele = doc.getElementById(_gwmBas.ELEID_DRAWLINE); 
  if( ele ) ele.setAttribute('style', sStyle);

  ele = doc.getElementById(_gwmBas.ELEID_DRAWPOLYLINE); 
  if( ele ) ele.setAttribute('style', sStyle);
  
  return _gwm_E_SUCCESS;
}

//=========================================================
function _gwmSetDynShapeStyle( sStyle )
{
var doc = _gwmCore.svgDoc;
var ele = null;

  if( null == sStyle || sStyle == "") return _gwm_E_PARAMNULL;
  if( !doc ) return _gwm_E_GETELEMENT;

  ele = doc.getElementById(_gwmBas.ELEID_DRAWPOLYGON);    
  if( ele ) ele.setAttribute('style', sStyle);

  ele = doc.getElementById(_gwmBas.ELEID_DRAWRECT); 
  if( ele ) ele.setAttribute('style', sStyle);

  ele = doc.getElementById(_gwmBas.ELEID_DRAWCIRCLE); 
  if( ele ) ele.setAttribute('style', sStyle);

  ele = doc.getElementById(_gwmBas.ELEID_DRAWTEXT); 
  if( ele ) ele.setAttribute('style', sStyle);

  return _gwm_E_SUCCESS;
}

//=========================================================
function _gwmAttachActiveCursor( pnt )
{
  if( null == _gwmBas.activeCursorEle ) return;
  var transform = "translate(" + pnt.x + " " + pnt.y + ") " + _gwmBas.activeCursorTrans;
  _gwmBas.activeCursorEle.setAttribute('transform', transform );
}


//=========================================================
function _gwmSetActiveCursor( sId, bOn )
{

  if( bOn && sId != "" )
  {
    _gwmBas.activeCursorEle = _gwmCore.svgDoc.getElementById(sId);
    if( _gwmBas.activeCursorEle )
    {   
      var mtx = _gwmCore.getSVGTransMatrix();
      var pxToCoord = mtx.a; //pixels per user unit

      //specific styles for specific cursors
      if( sId == _gwmBas.ELEID_CURSCROSSHAIR )
      {
        _gwmBas.activeCursorEle.style.setProperty('stroke-width', pxToCoord);
      }
      else if( sId == _gwmBas.ELEID_CURSPANARRW )
      {
        _gwmBas.activeCursorTrans = 'scale(' + pxToCoord/9 + ')';
        _gwmBas.activeCursorEle.setAttribute('transform', _gwmBas.activeCursorTrans );
      }

      _gwmBas.activeCursorEle.style.setProperty('display', 'inline');
    }
  }
  else if( !bOn && null != _gwmBas.activeCursorEle )
  {
    _gwmBas.activeCursorEle.style.setProperty('display', 'none');
    _gwmBas.activeCursorEle = null;
  }
}

//=========================================================
function _gwmSetDrawingState( bOn )
{
var state = _gwmBasME.iListenerState;

  if( bOn )
  {
    _gwmSetActiveCursor( 'gwmcursorcrosshair', true );
  }
  else
  {
    _gwmSetActiveCursor( "", false );

    if( state == _gwmBasME.IDLE ) return;

    if( state == _gwmBasME.LINEMODE ) 
      _gwmClearPath( _gwmBas.sEleDrawLine, null );

    else if( state == _gwmBasME.POLYLINEMODE ) 
      _gwmClearPath( _gwmBas.sEleDrawPolyline, null );

    else if( state == _gwmBasME.POLYGONMODE ) 
      _gwmClearPath( _gwmBas.sEleDrawPolygon, null );

    else if( state == _gwmBasME.RECTANGLEMODE ) 
      _gwmClearRectangle( _gwmBas.sEleDrawRect, null ); 

    else if( state == _gwmBasME.CIRCLEMODE ) 
      _gwmClearCircle( _gwmBas.sEleDrawCircle, null );

    else if( state == _gwmBasME.POINTMODE ) 
      _gwmClearPoint( _gwmBas.sEleDrawPoint, null );
      
    else if( state == _gwmBasME.TEXTMODE ) 
    {
      _gwmClearMultiple( _gwmBas.sEleDrawText, null );
      _gwmBas.sDrawText = "";
    }
    
    /***
    else if( state == _gwmBasME.CALLOUTMODE  ) 
    {
      _gwmClearPath( _gwmBas.sEleDrawCalloutLine, null );
      _gwmClearMultiple( _gwmBas.sEleDrawText, null );      
      _gwmBas.sDrawText = "";
    }
    ***/
    
    else if( state == _gwmBasME.TEXTKEYINMODE || 
             state == _gwmBasME.TEXTKEYINSTART || 
             state == _gwmBasME.CALLOUTMODE ) 
    {
      //_gwmClearMultiple( _gwmBas.sEleDrawTextKeyin, null );
      _gwmBas.sDrawText = "";
      // for Callout Keyins, we're in TEXTKEYINMODE
      _gwmClearPath( _gwmBas.sEleDrawCalloutLine, null );
      _gwmClearMultiple( _gwmBas.sEleDrawText, null );  
    }
  }
    
}

//=========================================================
function _gwmDrawRectangle( sEleID, inEle, anchorPt, currentPt )
{
var x, y, dx, dy;
var ele = null;

  if( null != sEleID && sEleID != ""  ) 
    ele = _gwmCore.svgDoc.getElementById( sEleID );
  else
    ele = inEle;
    
  if( !ele ) return;  

  if( currentPt.x < anchorPt.x )
  {
    x = currentPt.x;
    dx = anchorPt.x - x;
  }
  else
  {
    x = anchorPt.x;
    dx = currentPt.x - x;
  }

  if( currentPt.y < anchorPt.y )
  {
    y = currentPt.y;
    dy = anchorPt.y - y;
  }
  else
  {
    y = anchorPt.y;
    dy = currentPt.y - y;
  }

  ele.setAttribute("x", x);
  ele.setAttribute("width",dx);
  ele.setAttribute("y", y);
  ele.setAttribute("height",dy);
  ele.style.setProperty('display', 'inline');

  return;
}

//=========================================================
function _gwmClearRectangle( sEleID, inEle )
{
var ele = null;

  if( null != sEleID && sEleID != ""  ) 
    ele = _gwmCore.svgDoc.getElementById( sEleID );
  else
    ele = inEle;
    
  if( !ele ) return;
  ele.setAttribute("x", 0);
  ele.setAttribute("width", 0);
  ele.setAttribute("y", 0);
  ele.setAttribute("height", 0);
  ele.style.setProperty('display', 'none');
}

//=========================================================
function _gwmDrawPath( sEleID, inEle, pts, currentPt, bClosed )
{
var sPathData = "";
var ele = null;

  var len = pts.length;
  if( len < 1 ) return;
  
  if( null != sEleID && sEleID != ""  ) 
    ele = _gwmCore.svgDoc.getElementById( sEleID );
  else
    ele = inEle;

  if( !ele ) return;  

  for( var i = 0; i < len; ++i )
  {
    if( i != 0 )
      sPathData = sPathData + " L " + pts[i].x + " " + pts[i].y;
    else
      sPathData = "M " + pts[i].x + " " + pts[i].y;
  } 

  if( currentPt != null )
  {
    //add current point only if not same as last point
    var same = (currentPt.x == pts[len-1].x && currentPt.y == pts[len-1].y);
    if(!same) sPathData = sPathData + " L " + currentPt.x + " " + currentPt.y;
  }
 
  if( bClosed ) sPathData = sPathData + " Z";

  ele.setAttribute("d", sPathData);
  ele.style.setProperty('display', 'inline');

  return;
}

//=========================================================
function _gwmClearPath( sEleID, inEle )
{
var ele = null;

  if( null != sEleID && sEleID != ""  ) 
    ele = _gwmCore.svgDoc.getElementById( sEleID );
  else
    ele = inEle;
    
  if( !ele ) return;
  ele.setAttribute("d", " ");
  ele.style.setProperty('display', 'none');
}

//=========================================================
function _gwmDrawCircle( sEleID, inEle, anchorPt, currentPt )
{
var r;
var ele = null;
  
  if( null != sEleID && sEleID != ""  ) 
    ele = _gwmCore.svgDoc.getElementById( sEleID );
  else
    ele = inEle;
    
  if( !ele ) return;  

  r = _gwmCalcDistance(anchorPt.x,anchorPt.y,currentPt.x,currentPt.y);

  ele.setAttribute("cx", anchorPt.x);
  ele.setAttribute("cy", anchorPt.y);
  ele.setAttribute("r", r);
  ele.style.setProperty('display', 'inline');

  return;
}

//=========================================================
function _gwmClearCircle( sEleID, inEle )
{
var ele = null;

  if( null != sEleID && sEleID != ""  ) 
    ele = _gwmCore.svgDoc.getElementById( sEleID );
  else
    ele = inEle;
    
  if( !ele ) return;
  ele.setAttribute("cx", 0);
  ele.setAttribute("cy", 0);
  ele.setAttribute("r", 0);
  ele.style.setProperty('display', 'none');
}

//=========================================================
function _gwmDrawText( sEleID, inEle, anchorPt, sText )
{
var data = null;
var tspan = null;
var ele = null;
var doc = _gwmCore.svgDoc;

  if( null == sText ) return;

  if( null != sEleID && sEleID != ""  ) 
    ele = doc.getElementById( sEleID );
  else
    ele = inEle;
    
  if( !ele ) return;  

  ele.setAttribute("x", String(anchorPt.x));
  ele.setAttribute("y", String(anchorPt.y));

  re = new RegExp( _gwmBas.NLREPATTERN, "g" ); 

  var txtArray = sText.split(re);
  data = doc.createTextNode(txtArray[0]);
  ele.appendChild(data);
  for( var i = 1; i < txtArray.length; i++ )
  {
    data  = doc.createTextNode( txtArray[i] );
    tspan = doc.createElement( "tspan" );
    tspan.setAttribute("x", ele.getAttribute('x'));
    //tspan.setAttribute("x", String(anchorPt.x));
    
    tspan.setAttribute("dy", "1em");
    tspan.appendChild(data);
    ele.appendChild(tspan);
  }

  ele.style.setProperty('display', 'inline');
    
  return;
}

//=========================================================
function _gwmClearMultiple( sEleID, inEle )
{
var ele = null;
  
  if( null != sEleID && sEleID != ""  ) 
    ele = _gwmCore.svgDoc.getElementById( sEleID );
  else
    ele = inEle;
    
  if( !ele ) return;
  while( ele.hasChildNodes() )
  {
    ele.removeChild(ele.firstChild);
  }
  ele.setAttribute("x", 0);
  ele.setAttribute("y", 0);
  ele.style.setProperty('display', 'none');
}

//=========================================================
function _gwmDrawPoint( sEleID, inEle, anchorPt )
{
  //Assumes a rect SVG element.
  //Element should have its width and height already set.
var x, y
var ele = null;

  if( null != sEleID && sEleID != ""  ) 
    ele = _gwmCore.svgDoc.getElementById( sEleID );
  else
    ele = inEle;
    
  if( !ele ) return;  

  x = anchorPt.x;
  y = anchorPt.y;

  // Adjust for final placement at center of rect.
  var w = ele.getAttribute("width");
  var h = ele.getAttribute("height");
  if( w == "" ) w = 0;
  else w = parseInt(w);
  if( h == "" ) h = 0;
  else h = parseInt(h);
  x = x - (w/2);
  y = y - (h/2);

  ele.setAttribute("x", x);
  ele.setAttribute("y", y);
  ele.style.setProperty('display', 'inline');

  return;
}

//=========================================================
function _gwmClearPoint( sEleID, inEle )
{
var ele = null;

  if( null != sEleID && sEleID != ""  ) 
    ele = _gwmCore.svgDoc.getElementById( sEleID );
  else
    ele = inEle;
    
  if( !ele ) return;
  ele.setAttribute("x", 0);
  ele.setAttribute("y", 0);
  ele.style.setProperty('display', 'none');
}


//========================================================
function _gwmSetCalloutTextPlacement( sTxtEleID, p1, p2 )
{
var ret=0;

  if( !sTxtEleID || sTxtEleID == "" || !p1 || !p2 ) 
    return _gwm_E_UNSPECIFIED;
  
  ret = _gwmSetDynTextProps(sTxtEleID);
  if( ret != _gwm_E_SUCCESS ) return ret;

  ret = _gwmSetCalloutTextPlacementParams(p1,p2);
  if( ret != _gwm_E_SUCCESS ) return ret;
  
  ret = _gwmSetCalloutTextLocation(); 
  if( ret != _gwm_E_SUCCESS ) return ret;

  return _gwm_E_SUCCESS;
}

//=========================================================
function _gwmSetDynTextProps( sTxtEleID )
{

/*********************************************************
SetDynTextProps: given a text element ID will pull from
  the Text element info in order to set internal
  "dynamic text" properties, used by other funcs. 
  SVG NOTE: The Text element must be displayed in order
  to have valid BBox values.
**********************************************************/

var txtEle = null;

  try
  {
    if( !_gwmCore.svgDoc ) return _gwm_E_GETELEMENT;
    txtEle = _gwmCore.svgDoc.getElementById(sTxtEleID);
    if( !txtEle ) return _gwm_E_GETELEMENT;
  }
  catch(e)
  {
    return _gwm_E_UNKNOWN;
  }
  
  // First reset current props.
  _gwmBas.iDynTxtW = 0;
  _gwmBas.iDynTxtH = 0;
  _gwmBas.iDynTxtNumLines = 0;
  _gwmBas.iDynTxtHgtPerLine = 0;
  _gwmBas.iDynTxtStrokeW = 0;
  
  var txtBox = txtEle.getBBox();

  if( txtBox.width  < 0 ) txtBox.width = 0;
  if( txtBox.height < 0 ) txtBox.height = 0;

  _gwmBas.iDynTxtW = txtBox.width;
  _gwmBas.iDynTxtH = txtBox.height;
  
  var nl = _gwmGetTextEleNumLines( txtEle );
  if( nl < 1 ) nl=1;
  _gwmBas.iDynTxtNumLines = nl;
  _gwmBas.iDynTxtHgtPerLine = txtBox.height/nl;

  try
  {
    _gwmBas.iDynTxtStrokeW = 
      parseInt(txtEle.style.getPropertyCSSValue('stroke-width').cssText);
  }
  catch(e)
  {
    _gwmBas.iDynTxtStrokeW = 0;
  }
  
  /***
  sFs = null;
  try
  {
    sFs = txtEle.style.getPropertyCSSValue('font-size').cssText;           
  }
  catch( e )
  {
    _gwmBas.iDynTxtFontSize = 0;
  }
  if( sFs ) _gwmBas.iDynTxtFontSize = parseInt(sFs);
  ***/
  
  return _gwm_E_SUCCESS;
}

//=========================================================
function _gwmSetCalloutTextPlacementParams( p1, p2 )
{
/**********************************************************
Func: _gwmSetCalloutTextPlacementParams
Abstract:
  Given 2 points of a vector, will calculate and set all 
  the global dynamic text parameters 
  that will be used for final text location placement.
  Theta calculation and settings according to the following:
  
                    theta = 0 
                       | 
                    q2 |  q1
                       |
   theta = -(PI/2) ---- ---- theta = PI/2 =~ 1.57
                       |
                    q3 |  q4
                       |
                  theta = PI
  
   quadrant 1:  theta > 0 && theta < PI/2
   quadrant 2:  theta < 0 && theta < -(PI/2)
   quadrant 3:  theta < -(PI/2)
   quadrant 4:  theta > PI/2 
    
   special cases:
   quad   0: theta = 0;          is q1 or q2?
   quad  90: theta = PI/2;       is q1 or q4?
   quad 180: theta = PI;         is q3 or q4?
   quad 270: theta = -(PI/2);    is q2 or q3?
   
**********************************************************/
  if( !p1 || !p2 )  return _gwm_E_UNSPECIFIED;

  var txtAlign = "middle"; // always middle for now.
  var txtAnc = "left";
  var dirY = "";
  var dirX = "";
  
  //SVG Coord plane origin is upper left; switch Y values.
  var theta = Math.atan2( p2.x-p1.x, p1.y-p2.y );

  if( theta > 0 )
  {
    if( theta < .395 ) txtAnc = "bottom"; // .785 
    else if( theta >  2.355 ) txtAnc = "top";  // 1.9265
    else txtAnc = "left";
  }
  else if( theta < 0 )
  {
    if( theta > (-1*.395) ) txtAnc = "bottom";  //  .785
    else if( theta < (-1*2.355) ) txtAnc = "top"; // 1.9265
    else txtAnc = "right";
  }
  else if( theta == 0 ) //up vertical
  {
    txtAnc = "bottom";
  }

  if( theta == 0 || theta == Math.PI )
  {
      ; // dirY should not be considered
  }
  else if( theta < Math.PI/2 && theta > (-1*(Math.PI/2)) )
    dirY = "decrease";
  else dirY = "increase"
    
  if( theta < 0 ) dirX = "decrease";
  else if( theta > 0 && theta < Math.PI ) dirX= "increase";
  
  _gwmBas.sDynTxtAnchor = txtAnc;
  _gwmBas.sDynTxtAlign  = txtAlign;
  _gwmBas.sDynTxtVectorDirX = dirX;
  _gwmBas.sDynTxtVectorDirY = dirY;
  _gwmBas.fDynTxtVectorSlope = ((p1.y-p2.y)/(p2.x-p1.x));
  
  _gwmBas.fDynTxtOffsetRefPntX = p2.x;
  _gwmBas.fDynTxtOffsetRefPntY = p2.y;

  return _gwm_E_SUCCESS;
}


//=========================================================
function _gwmSetCalloutTextLocation()
{
  // REQUIRES: _gwmSetDynTextProps() having been called first.
  
  var sAnc  = _gwmBas.sDynTxtAnchor;
  var dirX  = _gwmBas.sDynTxtVectorDirX;
  var dirY  = _gwmBas.sDynTxtVectorDirY;
  var slope = _gwmBas.fDynTxtVectorSlope;
  var nl    = _gwmBas.iDynTxtNumLines;
  var newY  = 0;
  var newX  = 0;
  var yShift = 0;
  var xShift = 0;
  var iSpacer = _gwmBas.CALLOUTTEXTOFFSET;

//var dbgY=0;

  if( sAnc == "left" || sAnc == "right" ) 
  {
    iSpacer += _gwmBas.iDynTxtStrokeW;
    newX = iSpacer;
    // need to center txtbox
    //ORIG yShift = nl > 1 ? -1 * (_gwmBas.iDynTxtH/nl-1): (_gwmBas.iDynTxtH/4);
    yShift = nl > 1 ? -1 * (((_gwmBas.iDynTxtHgtPerLine*(nl-1))/2)): (_gwmBas.iDynTxtH/4);

//dbgY=yShift;

    if( nl > 1 && Math.abs(slope) > 1 )
      yShift /= (Math.abs(slope));
    
    xShift = _gwmBas.iDynTxtW/2;    
    
    if( dirY != "" )
    {      
      newY = Math.abs(newX * slope);
      if( dirY == "decrease" ) newY *= -1;
    }
      
    newX += xShift;
    newY += yShift;    
    if( sAnc == "right" ) newX *= -1;    
  }
  else if( sAnc == "top" || sAnc == "bottom" ) 
  {
    if( sAnc == "bottom" ) 
    {
      yShift = nl > 1 ? ((_gwmBas.iDynTxtH/nl)* (nl-1)): 0;
      iSpacer += (_gwmBas.iDynTxtStrokeW * nl); //add stroke width(s)
    }
    else
    {
      yShift = _gwmBas.iDynTxtHgtPerLine/2;
      iSpacer += _gwmBas.iDynTxtStrokeW; //only the first line has effect
    }
    
    newY = iSpacer + yShift;
    if( dirX != "" )
    {
      newX = Math.abs(newY / slope);
      if( dirX == "decrease" ) newX *= -1;
    }

    if( sAnc == "bottom" ) newY *= -1;
  }
  
  _gwmBas.fDynTxtLocPntX = _gwmBas.fDynTxtOffsetRefPntX + newX;
  _gwmBas.fDynTxtLocPntY = _gwmBas.fDynTxtOffsetRefPntY + newY;

  _gwmBas.dynTxtAnchorPt = null;
  _gwmBas.dynTxtAnchorPt = new _gwmPoint();
  _gwmBas.dynTxtAnchorPt.x = _gwmBas.fDynTxtLocPntX;
  _gwmBas.dynTxtAnchorPt.y = _gwmBas.fDynTxtLocPntY;

  return _gwm_E_SUCCESS;
}


/************************************************/
/*** MISC SUPPORT FUNCTIONS    ******************/
/************************************************/

//=========================================================
function _gwmGetMetadataImp(name)
{
  var meta = _gwmCore.svgDoc.getElementById("_GWMMetadata");  
  return meta.getAttribute(name);
}

//=========================================================
function _gwmCalcDistance(x1,y1,x2,y2)
{
var dx = 0.0;
var dy = 0.0;
var fResult = 0.0;

  dx = x2 - x1;
  dy = y2 - y1;
  fResult = Math.sqrt( ((dx*dx) + (dy*dy)) );
return fResult;
} 

//=========================================================
function _gwmResetBASProperty( sProp )
{
  if( null != sProp ) sProp = sProp.toLowerCase();
  
  if( null == sProp || "" == sProp ) //reset all
  {
    _gwmBas.sNewlineMarker = _gwmBas.DEF_NEWLINEMARKER;
  }
  else if( sProp == "newlinemarker" )
    _gwmBas.sNewlineMarker = _gwmBas.DEF_NEWLINEMARKER;
    
  return;
}

//==========================================================
function _gwmReplaceNLMarker( sText )
{
  if( null == sText || "" == sText || 
      (sText.indexOf(_gwmBas.sNewlineMarker)== -1) ) return sText;
      
  var sOutText = "";
  var sArray = sText.split( _gwmBas.sNewlineMarker );

  for( var i = 0; i < sArray.length; ++i )
    sOutText = sOutText + sArray[i] + _gwmBas.NL;

  if( sOutText == "" ) sOutText = sText;
  return sOutText;
}

//==========================================================
function _gwmGetPntViewCtrlPixDim()
{
  var pnt = new _gwmPoint();
  pnt.x = _gwmCore.iViewCtrlWidth;
  pnt.y = _gwmCore.iViewCtrlHeight;
  return pnt;
}

//==========================================================
function _gwmGetSVGPntViewCtrlPixDim()
{
  var pnt = _gwmCore.svgRoot.createSVGPoint();
  pnt.x = _gwmCore.iViewCtrlWidth;
  pnt.y = _gwmCore.iViewCtrlHeight;
  return pnt;
}

//=========================================================
function _gwmTrimString(s) 
{
  // replace 1 or more blanks at beginning and end, with nothing ("")
  s = s.replace(/^ +/, "");
  s = s.replace(/$ +/, "");
  return s;
}

//==========================================================
function _gwmIsValidFunction( func )
{
//window.alert("_gwmIsValidFunction: test func=" + func);

  if( null == func || 
      ""   == func || 
      undefined == func ||
      "function" != typeof(func) ) return false;
      
  return true;
}

