//\/////
//\  SOMIPan - You may not remove or change this notice.
//\  Do not sell this as your own work or remove this copyright notice.
//\  $Id: SOMIPan.js,v 1.92 2008/06/05 03:22:47 pjamason Exp $
//\
//\  Credit given (and copyright statements maintained) where code
//\  used directly or modified from other sources.
//\  
//\  This software is Copyright © 2006 The Regents of the University of
//\  California. All Rights Reserved.
//\  
//\  Permission to use, copy, modify, and distribute this software and its
//\  documentation for educational, research and non-profit purposes,
//\  without fee, and without a written agreement is hereby granted, provided
//\  that the above copyright notice, this paragraph and the following three
//\  paragraphs appear in all copies.
//\  
//\  Permission to incorporate this software into commercial products may be
//\  obtained by contacting
//\  Technology Transfer Office
//\  9500 Gilman Drive, Mail Code 0910
//\  University of California
//\  La Jolla, CA 92093-0910
//\  (858) 534-5815
//\  invent@ucsd.edu
//\  
//\  This software program and documentation are copyrighted by The Regents of the
//\  University of California. The software program and documentation are
//\  supplied "as is", without any accompanying services from The Regents.
//\  The Regents does not warrant that the operation of the program will be
//\  uninterrupted or error-free. The end-user understands that the program was
//\  developed for research purposes and is advised not to rely exclusively on
//\  the program for any reason.
//\  
//\  IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO
//\  ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
//\  CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
//\  OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
//\  EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF
//\  THE POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF
//\  CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
//\  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
//\  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//\  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND
//\  THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
//\  PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
//\  MODIFICATIONS.
//\  
//\/////


//\/////
//\      Copyright (c) 2005 Michal Migurski <mike-gsv@teczno.com>
//\      
//\      Redistribution and use in source form, with or without modification,
//\      are permitted provided that the following conditions are met:
//\      1. Redistributions of source code must retain the above copyright
//\         notice, this list of conditions and the following disclaimer.
//\      2. The name of the author may not be used to endorse or promote products
//\         derived from this software without specific prior written permission.
//\      
//\      THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
//\      IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
//\      OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//\      IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
//\      INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
//\      NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
//\      DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
//\      THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
//\      (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
//\      THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//\/////



/*

  Description:   Javascript for a draggable viewer for SOPAC's
                 Online Mapping Interface (SOMI).
  Requires:       .css counterpart in order to function.
  Adapted from : GSV 1.0, by Michal Migurski <mike-gsv@teczno.com>
                 $Id: SOMIPan.js,v 1.92 2008/06/05 03:22:47 pjamason Exp $	
  Usage:
    For an HTML construct such as this:

        <div class="imageViewer">
            <div class="well">
              <div class="shadow"></div>
              <div class="overlay"></div>
            </div>
            <div class="surface"> </div>
        </div>

    ...pass the DOM node for the top-level DIV, an <img> element
    corresponding to the map image for the viewer, and an integer
    describing the height of the image to viewerInitialize().

        viewerInitialize(viewer element, <img> , imageSize);
        
    It is expected that the visual behavior of these nodes is determined
    by a set of CSS rules (in .css counterpart).
    
    The "well" node is where the IMG element is stored.  It
    should have the CSS rule "overflow: hidden", to occlude portions of
    the image that have scrolled out of view.
    
    The "overlay" node is where "floating" items (to be turned off|on, etc)
    would be stored.  It should also have the CSS rule "overflow: hidden",
    to occlude items that have scrolled out of view.

    The "shadow" node is where shadows (or highlights) "floating" items
    would be stored (if requested) for each item floating in the
    "overlay" layer.

    The "surface" node is the transparent mouse-responsive layer of the
    image viewer, and should match the well in size.
    
*/   

var SOMIv;  // The stuff.  Global.  The one and only.
var SOMIap; // The stuff.  Global.  The one and only.
var SOMIbm; // The stuff.  Global.  The one and only.
var SOMIml; // The stuff.  Global.  The one and only.
var SOMItb; // The stuff.  Global.  The one and only.
var SOMIsb; // The stuff.  Global.  The one and only.
var SOMIzib; // The stuff.  Global.  The one and only.
var SOMIzob; // The stuff.  Global.  The one and only.
var SOMIslider; // The stuff.  Global.  The one and only.
var SOMIzsRail; // The stuff.  Global.  The one and only.

// Below are the ONLY global variables created or used within this file.
// Please think carefully about whether or not you need to modify or
// add to this list because they could conflict with other globals
// introduced in other source files.
//

var global_progressCycleIntervalID;
var global_refreshCycleIntervalID;
var global_widthOfViewerOverflow;
var global_heightOfViewerOverflow;
var global_jsIncludeList = null;
var global_cssIncludeList = null;
// Cross-browser (Mozilla, Firefox, IE, Opera) double-click
// handling code adapted from:
//
//  http://www.chipchapin.com/WebTools/JavaScript/example3-01.html
// 
var dcTime=250;    // doubleclick time
var dcDelay=100;   // no clicks after doubleclick
var dcAt=0;        // time of doubleclick
var savEvent=null; // save Event for handling doClick().
var savEvtTime=0;  // save time of click event.
var savTO=null;    // handle of click setTimeOut

// Libraries to include on-the-fly
//

viewerInclude("/lib/extern/slider.js","js");
viewerInclude("/lib/extern/overlib.js","js");
viewerInclude("/lib/extern/log4javascript/log4javascript.js","js");
viewerInclude("/lib/SOMIQuery.js","js");
viewerInclude("/lib/SOMIQuickNav.js","js");
viewerInclude("/lib/SOMIVelocity.js","js");
viewerInclude("/lib/SOMIAnime.js","js");
viewerInclude("/lib/SOMI_G.js","js");
viewerInclude("/lib/SOMIBase.js","js");
viewerInclude("/lib/SOMILayers.js","js");

// pj, 11/08/2007: only include GPSE items if in portal url.
// list the websites hosts/urls here that do not require these js and
// search against them
var url = location.href;
if (url.search("sopac")==-1 && url.search("troy")==-1 && url.search("reason.scign.org")==-1 && url.search("csrc")==-1) {
viewerInclude("/lib/GPSE_Utils.js","js");
viewerInclude("/lib/GPSE_WS.js","js");
viewerInclude("/lib/GPSE_SiteGroup.js","js");
viewerInclude("/lib/gpseConfig.js","js");
}


function hadDoubleClick() {
  var d = new Date();
  var now = d.getTime();
  if ((now - dcAt) < dcDelay) {
    return true;
  }
  return false;
}

function doClick(which) {
  // preempt if DC occurred after original click.
  if (savEvtTime - dcAt <= 0) {
    return false;
  }
}


function startProgressCycleAnimation() {
   // Progress cycle loader placement dependent upon
   // map size and viewer geometry.
   //
   var progressCycle = document.getElementById('progressCycleAnimation');
   progressCycle.innerHTML = '<font class="setFontSmall"><b>Loading...</b></font>';
   progressCycle.style.left = 10 + 'px';
   progressCycle.style.top = 3 + 'px';
   //global_progressCycleIntervalID = setInterval(runProgressCycleAnimation, 100);
}

function runProgressCycleAnimation() {
  // Do something here if you like.
}

function stopProgressCycleAnimation() {
   //clearInterval(global_progressCycleIntervalID);
   var progressCycle = document.getElementById('progressCycleAnimation');
   progressCycle.innerHTML = '';
   if (SOMIv.context == 'portal') {
     // Update settings, and location of map such that
     // the user picks up where they left off browsing.
     SOMIv.centerLatitudeFirst = SOMIv.centerLatitudeCurrent;
     SOMIv.centerLongitudeFirst = SOMIv.centerLongitudeCurrent;

     // Calculate, update and distribute EXTENT of map
     //
     SOMIv.maxLatitude = calculateLatitudeFromMapY(0);
     SOMIv.minLatitude = calculateLatitudeFromMapY(SOMIv.dimensions.tileSize.y);
     SOMIv.minLongitude = calculateLongitudeFromMapX(0);
     SOMIv.maxLongitude = calculateLongitudeFromMapX(SOMIv.dimensions.tileSize.x);
     
     if (SOMIv.tools.query.isLoaded) {
       if (SOMIv.tools.query.type == 'extent') {
         if (SOMIv.tools.query.subtype == 'map') {
           viewerQuerySyncFromMapExtent();
         } else {
           viewerQuerySyncFromViewerExtent();
         }
       }
     }

     // Refresh which layers are visible (optional functionality)
     // and which are not...if the layer toolbox is open.
     //     
     if (SOMIv.tools.layers.isLoaded) {
       viewerToolboxPresentLayers();
     }

     SOMIv.dimensions.delta_x = SOMIv.dimensions.center.x;
     SOMIv.dimensions.delta_y = SOMIv.dimensions.center.y;
     SOMIv.start = {x : 0, y : 0};
     positionTileWRTViewer({x : 0,y : 0});

     // Hide the rubber-band box (if present)
     viewerHideRubberBandBox();

     if (document.getElementById('mainScaleBar') != undefined) {
        var sb = document.getElementById('mainScaleBar');
        var newScaleBarImageURL = 'http://geoapp02.ucsd.edu/cgi-bin/mapserv?mode=scalebar&map=%2Fvar%2Fwww%2Fhtml%2Fmaps%2F' + viewerGetActiveBaseMapFile();
        newScaleBarImageURL += '&mapext=shapes&mapsize=' + SOMIv.dimensions.tileSize.x + '+' + SOMIv.dimensions.tileSize.y;
        newScaleBarImageURL += '&mapxy=' + SOMIv.centerLongitudeCurrent + '+' + SOMIv.centerLatitudeCurrent;
        newScaleBarImageURL += '&scale=' + SOMIv.scale;
        sb.innerHTML = '<img border=0 style="position:relative;z-index:4;display:block;" src="'+ newScaleBarImageURL + '">';
        //sb.getElementsByTagName('img')[0].src = newScaleBarImageURL;
     }
     SOMIv.reload = false;
     SOMIv.pressed = false;
     SOMIv.state = 'calm';
     SOMIv.tLState = 'calm';

     //if (SOMIv.tools.query.isLoaded) {
       viewerQFeaturesLoadFromLayer();
     //}
     SOMIv.tLState = 'calm';
     if (SOMIv.onComplete != undefined) {
       SOMIv.onComplete();
       SOMIv.onComplete = undefined;
     }
   }
}


function getViewerEvent(event)
{
    if (event == undefined) {
        return window.event;
    }
    
    return event;
}



function viewerCreate(idOfViewer,idOfBaseMaps,idOfMapLayers,idOfToolbox,idOfAnimationPlayer,
                      width,height,lat,lon,scale,minScale,maxScale,printable,zoomable,pannable,context) {

  // If user's screen resolution is 1024 pixels wide or less,
  // resize map to prevent toolbox overflow in Gridsphere.
  //if (screen.width <= 1024) {
  //  width = 250;
  //  height = 250;
  //}

  // SOMIv is a DOM element, likely empty, to be used as the container
  // for our embedded SOMI map.
  //
  SOMIv = document.getElementById(idOfViewer);
  if (idOfBaseMaps != undefined) {
    SOMIbm = document.getElementById(idOfBaseMaps);
  }
  if (idOfMapLayers != undefined) {
    SOMIml = document.getElementById(idOfMapLayers);
  }
  if (idOfToolbox != undefined) {
    SOMItb = document.getElementById(idOfToolbox);
  }
  if (idOfAnimationPlayer != undefined) {
    SOMIap = document.getElementById(idOfAnimationPlayer);
  }

  // 1. Add embedded components
  //
  var eComponents = new Array ("viewerZoomInButton",
                               "viewerZoomButton","viewerZoomSliderButton","viewerZoomOutButton",
                               "mainScaleBar","progressCycleAnimation","viewerToolbar","positionText");
  for (var i = 0; i < eComponents.length; i+=1) {
    var o = document.createElement("div");
    o.id = eComponents[i];
    o.className = "embedded";
    SOMIv.appendChild(o);
  }
  var viewerZoomSliderRailDiv = document.createElement("div");
  viewerZoomSliderRailDiv.id = "viewerZoomSliderRail";
  viewerZoomSliderRailDiv.className = "scaleSliderRail";
  SOMIv.appendChild(viewerZoomSliderRailDiv);

  var viewerZoomSliderRailDiv = document.createElement("div");
  viewerZoomSliderRailDiv.id = "viewerZoomSliderRail";
  viewerZoomSliderRailDiv.className = "scaleSliderRail";
  SOMIv.appendChild(viewerZoomSliderRailDiv);

  var oWell = document.createElement("div");
  oWell.id = "well";
  oWell.className = "well";
  var oShadow = document.createElement("div");
  oShadow.id = "shadow";
  oShadow.className = "shadow";
  oWell.appendChild(oShadow);
  var oOverlay = document.createElement("div");
  oOverlay.id = "overlay";
  oOverlay.className = "overlay";
  oWell.appendChild(oOverlay);
  SOMIv.appendChild(oWell);
  var oSurface = document.createElement("div");
  oSurface.id = "surface";
  oSurface.className = "surface";
  SOMIv.appendChild(oSurface);

  // 2. Cross-reference surface and well
  //    with one another.  We have 1 tile and
  //    and 1 view frame only.
  //
  for (var child = SOMIv.firstChild; child; child = child.nextSibling) {
      if (child.className == 'surface') {
          SOMIv.activeSurface = child;
          child.imageViewer = SOMIv;
      } else if (child.className == 'well') {
          SOMIv.tileWell = child;
          child.imageViewer = SOMIv;
      }
  }

  SOMIv.tileWell.overlay = document.getElementById('overlay');
  SOMIv.tileWell.shadow = document.getElementById('shadow');
  if (width != undefined) {
    SOMIv.style.width = width + 'px';
  } else {
    SOMIv.style.width = '200px';
  }
  if (height != undefined) {
    SOMIv.style.height = height + 'px';
  } else {
    SOMIv.style.height = '200px';
  }
  if (lat != undefined) {
    SOMIv.centerLatitudeFirst = lat; 
    SOMIv.centerLatitudeCurrent = lat;
  } else {SOMIv.centerLatitudeFirst = "0.0"; 
    SOMIv.centerLatitudeCurrent = "0.0";
  }
  if (lon != undefined) {
    SOMIv.centerLongitudeFirst = lon; 
    SOMIv.centerLongitudeCurrent = lon;
  } else {
    SOMIv.centerLongitudeFirst = "0.0"; 
    SOMIv.centerLongitudeCurrent = "0.0";
  }
  if (scale != undefined) {
    SOMIv.scale = scale;
  } else {
    SOMIv.scale = 10000000;
  }
  if (printable != undefined) {
    SOMIv.printable = printable;
  } else {
    SOMIv.printable = false;
  }
  if (zoomable != undefined) {
    SOMIv.zoomable = zoomable;
  } else {
    SOMIv.zoomable = false;
  }
  if (pannable != undefined) {
    SOMIv.pannable = pannable;
  } else {
    SOMIv.pannable = false;
  }
  if (minScale != undefined) {
    SOMIv.minScale = minScale;
  } else {
    SOMIv.minScale = "100000";
  }
  if (maxScale != undefined) {
    SOMIv.maxScale = maxScale;
  } else {
    SOMIv.maxScale = "190000000";
  }
  SOMIv.state = 'loading';

  // Set the default/initial cursor style.
  SOMIv.tileWell.style.cursor = SOMIv.activeSurface.style.cursor = 'move';
  SOMIv.interactionMode = 'pan';  // Other choices set via viewerInteractionMode ['zoom','selectbox']
  
  SOMIv.basketChange = 0;
  SOMIv.hasLiveContent = false;
  SOMIv.geodesyMapFile = undefined;
  SOMIv.mapLayers = new Array();
  SOMIv.selectedMapLayer = undefined;
  SOMIv.onComplete = undefined;  // User-defined callback function to be linked to stopProgressCycleAnimation
  SOMIv.mapFeaturesByID = new Object();
  SOMIv.selectedMapFeatures = new Array();
  SOMIv.zoomSlider = new Slider("zsButton"); // create zoom slider object
  viewerTieSlider();  
  SOMIv.toolbar = {state: 'closed'};
  SOMIv.rbbbox = {ulxWRTMapImage : undefined, ulyWRTMapImage : undefined};
  var qm = new Array({ver: 'add', active_icon : {src : '/lib/images/add_hi.png'},
                                                     inactive_icon : {src : '/lib/images/add.png'}},

                                        {ver : 'info', active_icon : {src : '/lib/images/info_hi.png'},
                                                    inactive_icon : {src : '/lib/images/info.png'}},

                                        {ver : 'remove', active_icon : {src : '/lib/images/remove_hi.png'},
                                                   inactive_icon : {src : '/lib/images/remove.png'}}
                                       );

  SOMIv.tools = {animation : {isLoaded : false, isRunning : false},
                       basemaps : {isLoaded : false},
                       layers : {isLoaded : false},
                       quicknav : {isLoaded : false},
                       query : {isLoaded : false, type : 'extent', subtype : 'viewer', selectMode : 'info', selectModes : qm},
                       velocities : {isLoaded : false}};

  SOMIv.basemaps = new Array ({name : 'basic', mapFileName : 'GlobalVector.map',
                                       active_icon : {src : '/lib/images/somi-icon-50x20-on-Basic.png', 
                                                        width : 50, height : 20},
                                       inactive_icon : {src : '/lib/images/somi-icon-50x20-off-Basic.png',
                                                          width : 50, height : 20},
                                       isactive : false,
                                       isenabled : true,
                                       isdefault : false,
                                       legendText : 'Suitable for all scales'
                                      },
                            {name : 'relief', mapFileName : 'GlobalTopoPP.map',
                                      active_icon : {src : '/lib/images/somi-icon-50x20-on-Relief.png',
                                                       width : 50, height : 20},
                                      inactive_icon : {src : '/lib/images/somi-icon-50x20-off-Relief.png',
                                                       width : 50, height : 20},
                                      isactive : false,
                                      isenabled : true,
                                      isdefault : false,
                                      legendText : 'Down to 2500000'
                                      },
                            {name : 'imagery', mapFileName : 'GlobalLandsat.map',
                                      active_icon : {src : '/lib/images/somi-icon-50x20-on-Imagery.png',
                                                       width : 50, height : 20},
                                      inactive_icon : {src : '/lib/images/somi-icon-50x20-off-Imagery.png',
                                                         width : 50, height : 20},
                                      isactive : false,
                                      isenabled : true,
                                      isdefault : false,
                                      legendText : '15000000 : 1500000'
                                      },
                             {name : 'drg', mapFileName : 'CRTNDRG.map',
  			              active_icon : {src : '/lib/images/somi-icon-50x20-on-DRG.png',
                                                       width : 50, height : 20},
                                      inactive_icon : {src : '/lib/images/somi-icon-50x20-off-DRG.png',
                                                         width : 50, height : 20},
                                      isactive : false,
                                      isenabled : true,
                                      isdefault : false,
                                      legendText : 'US only 300000 : 24000'
                                     });
  SOMIv.projection = "cyleqdist";
  SOMIv.context = "portal"; // Other choice is "page"    
  if ((context != undefined) && (context == 'page')) {
    SOMIv.context = "page";
  }
  return true;
}


function viewerInitialize (mapImage, tileSizeX, tileSizeY, mapImageURL, onComplete) {

  var width = SOMIv.offsetWidth;
  var height = SOMIv.offsetHeight;
  var top = 0;
  var left = 0;
  for (var node = SOMIv; node; node = node.offsetParent) {
    top += node.offsetTop;
    left += node.offsetLeft;
  }
  if (onComplete != undefined) {
    SOMIv.onComplete = onComplete;
  }
  SOMIv.dimensions = {

    // width and height of the viewer in pixels
    width: width, height: height,

    // position of the viewer in the document, from the upper-left corner
    top: top, left: left,

    // The single tile for this viewer (vastly simplified)
    tile: {x : 0, y : 0, url : mapImageURL, img : mapImage, imageViewer : SOMIv},

    // Size of tile
    tileSize: {x : tileSizeX, y : tileSizeY},

    // Top-left pixel of viewer, if it were to be centered in the view window
    center : {x: ((tileSizeX - width) / -2), y: ((tileSizeY - height) / -2)},

    // The x,y position of the upper-left corner of the tile (a single image)
    // with respect to the upper-left corner of the view frame.  Usually
    // negative, constantly updated.
    //  ex. (-100,-100) means the upper left
    //      corner of the map image is hidden, up and to the
    //      left of the upper left corner of the map viewer.
    //
    delta_x : ((tileSizeX - width) / -2), delta_y: ((tileSizeY - height) / -2)
  };

  var SOMIzob = document.getElementById('viewerZoomOutButton');
  var SOMIzsRail = document.getElementById('viewerZoomSliderRail');
  var SOMIzib = document.getElementById('viewerZoomInButton');
  var SOMItbar = document.getElementById('viewerToolbar');
  var SOMIsb = document.getElementById('mainScaleBar');
  if (SOMIv.dimensions.width < 400) { //Check map size before placing controls
    document.getElementById('smallMapControlsDiv').innerHTML = '<table><tr><td></td></tr></table><table><tr><td></td></tr></table><table><tr><td></td></tr></table>';
    document.getElementById('smallMapScalebarDiv').innerHTML = '<table><tr><td></td></tr></table><table><tr><td></td></tr></table><table><tr><td></td></tr></table>';
    if (SOMIv.context == 'portal') {
      // We have a small map within a portal
      SOMIzob.style.left = (SOMIv.dimensions.width/20) + 'px';
      SOMIzob.style.top = - 23 + 'px';
      SOMIzsRail.style.left = (SOMIv.dimensions.width/20) + 25 + 'px';
      SOMIzsRail.style.top = - 19 + 'px';
      SOMIzib.style.left = (SOMIv.dimensions.width/20) + 255 + 'px';
      SOMIzib.style.top = - 23 + 'px';
      SOMItbar.style.left = (SOMIv.dimensions.width/20) + 285 + 'px';
      SOMItbar.style.top = - 26 + 'px';
      SOMIsb.style.left = (SOMIv.dimensions.width/20) + 'px';
      SOMIsb.style.top = SOMIv.dimensions.height + 5 + 'px';
    } else {
      // We have a small map within a standard HTML page
      SOMIzob.style.left = SOMIv.dimensions.center.x + 145 + 'px';
      SOMIzob.style.top = - 23 + 'px';
      SOMIzsRail.style.left = SOMIv.dimensions.center.x + 170 + 'px';
      SOMIzsRail.style.top = - 19 + 'px';
      SOMIzib.style.left = SOMIv.dimensions.center.x + 402 + 'px';
      SOMIzib.style.top = - 23 + 'px';
      SOMItbar.style.left = SOMIv.dimensions.center.x + 429 + 'px';
      SOMItbar.style.top = - 24 + 'px';
      SOMIsb.style.left = (SOMIv.dimensions.width/20) + 'px';
      SOMIsb.style.top = SOMIv.dimensions.height + 5 + 'px';
    }
  } else {
    // Map is larger than 400 px wide
    SOMIzob.style.left = SOMIv.dimensions.width - 310 + 'px';
    SOMIzob.style.top = 5 + 'px';
    SOMIzsRail.style.left = SOMIv.dimensions.width - 286 + 'px';
    SOMIzsRail.style.top = 9 + 'px';
    SOMIzib.style.left = SOMIv.dimensions.width - 57 + 'px';
    SOMIzib.style.top = 5 + 'px';
    SOMItbar.style.left = SOMIv.dimensions.width - 32 + 'px';
    SOMItbar.style.top = 4 + 'px';
    SOMIsb.style.left = 15 + 'px';
    SOMIsb.style.top = SOMIv.dimensions.height -25 + 'px';
  }
    
  global_widthOfViewerOverflow = parseInt((tileSizeX - width) / 2);
  global_heightOfViewerOverflow = parseInt((tileSizeY - height) / 2);
  SOMIv.start = {x: 0, y: 0}; // this is reset each time that the mouse is pressed anew
  SOMIv.pressed = false;
  SOMIv.state = 'init';

  if (SOMIv.pannable) {
    document.body.onmouseup = releaseViewer;
  }

  // Calculate extent of map in geographic coordinates
  //
  if (SOMIv.maxLatitude == undefined) {
    SOMIv.maxLatitude = calculateLatitudeFromMapY(0);
  }
  if (SOMIv.minLatitude == undefined) {
    SOMIv.minLatitude = calculateLatitudeFromMapY(height);
  }
  if (SOMIv.minLongitude == undefined) {
    SOMIv.minLongitude = calculateLongitudeFromMapX(0);
  }
  if (SOMIv.maxLongitude == undefined) {
    SOMIv.maxLongitude = calculateLongitudeFromMapX(width);
  }


  // Place transparent placeholder image to keep width of area to right of map
  // a constant size.
  SOMItb.innerHTML = '<img src="/lib/images/pixel.png" height=1 width=200 border=none>';
  viewerLoadBasemapToolbox();


  // Place zoom icons (if necessary)
  if ((SOMIv.context == 'portal') && SOMIv.zoomable && (document.getElementById('viewerZoomInButton') != undefined)) {
    var zib = SOMIzib;
    // Zoom in button placement dependent upon
    // map size and viewer geometry.
    //
    zib.innerHTML += '<a href="javascript:void(0)" onClick="event.returnValue=false;viewerZoomIn();"><img src="/lib/images/zib.png" border=none alt="Zoom in icon"></a>';
  }
 
  if ((SOMIv.context == 'portal') && SOMIv.zoomable && (document.getElementById('viewerZoomSliderButton') != undefined)) {
    var zsRail = SOMIzsRail;
    // Zoom slider rail placement dependent upon
    // map size, viewer geometry AND map scale.
    //
    //zsRail.innerHTML += '<img src="/lib/images/zsRail.png" ID="zsRail" border=none alt="Base image for scale slider" onclick="viewerSliderClicked(event)">';
    zsRail.innerHTML += '<img src="/lib/images/zsRail.png" ID="zsRail" border=none alt="Base image for scale slider">';
    SOMIv.zoomSlider.buttonImg = "/lib/images/zsb.png";
    SOMIv.zoomSlider.buttonHoverImg = "/lib/images/zsb.png";
    SOMIv.zoomSlider.buttonHiliteImg = null;
    SOMIv.zoomSlider.buttonWidth = 12;
    SOMIv.zoomSlider.buttonHeight = 12;
    if (SOMIv.dimensions.width < 400) {
    // We need to adjust the vertical offset of the slider
    // button for Gridsphere where it is placed a little
    // differently (odds are small map size = Gridsphere)
      SOMIv.zoomSlider.offsetY = -1;
    } else {
      SOMIv.zoomSlider.offsetY = -0.6;
    }
    SOMIv.zoomSlider.leftValue = 0;
    SOMIv.zoomSlider.rightValue = 15;
    SOMIv.zoomSlider.defaultValue = 6;
    SOMIv.zoomSlider.maxSlide = 213;
    SOMIv.zoomSlider.onmouseup = "this.setValue(this.getValue())";
    SOMIv.zoomSlider.writeSlider();
    SOMIv.zoomSlider.placeSlider('zsRail');
    // NOTE : 2006-06-26 : Michael - 2nd arg here indicates "ignore"
    SOMIv.zoomSlider.setValue(SOMIv.scale,true);
  }

  if ((SOMIv.context == 'portal') && SOMIv.zoomable && (document.getElementById('viewerZoomOutButton') != undefined)) {
     var zob = SOMIzob;
     // Zoom out button placement dependent upon
     // map size and viewer geometry.
     //
     zob.innerHTML += '<a href="javascript:void(0)" onClick="event.returnValue=false;viewerZoomOut();"><img src="/lib/images/zob.png" border=none alt="Zoom out icon"></a>';
  }

 
  // Place embedded toolbar (initial state is closed)
  //
  if ((SOMIv.context == 'portal') && (document.getElementById('viewerToolbar') != undefined)) {
     var tbar = document.getElementById('viewerToolbar');
     // Toolbar placement dependent upon
     // map size and viewer geometry.
     //
     var txt ='<div id="viewerToolbarBackground" style="position:relative; border: 1px solid black; background-color:white; width:29px;">';
     txt+='<div style="position:relative; background-color:white; z-index:3;">';
     txt+= '<table cellpadding=0 cellspacing=0><tr><td align="center"><a href="javascript:void(0)" onmouseover="return overlib(\'Click to open SOMI tools.\', OFFSETX, 13, WIDTH, 150, FGCOLOR, \'#FFFF33\');" onmouseout="return nd();" onClick="event.returnValue=false;viewerToolbarOpenShut()"><img src="/lib/images/toolbox.gif" border=none alt="Toolbar"></a></td></tr>';
     txt+='</div>';
     txt+='</div>';
     tbar.innerHTML+=txt;
     SOMIv.toolbar.state = 'closed';
  }

  // Position scalebar
  if ((SOMIv.context == 'portal') && (document.getElementById('mainScaleBar') != undefined)) {
     var sb = SOMIsb;
     // Scalebar placement dependent upon
     // map size and viewer geometry.
     //
  }
		
  startProgressCycleAnimation();
  prepareTileForViewer();		
}


function viewerToolbarOpenShut () {
  if (document.getElementById('viewerToolbar') != undefined) {
    var tb = document.getElementById('viewerToolbar');
    if (SOMIv.toolbar.state == 'open') {
      var txt ='<div id="viewerToolbarBackground" style="position:relative; border: 1px solid black; background-color:white; width:29px;">';
      txt+='<div style="position:relative; background-color:white; z-index:3;">';
      txt+= '<table cellpadding=0 cellspacing=0><tr><td align="center"><a href="javascript:void(0)" onmouseover="return overlib(\'Click to open SOMI tools.\', OFFSETX, 13, WIDTH, 150, FGCOLOR, \'#FFFF33\');" onmouseout="return nd();" onClick="event.returnValue=false;viewerToolbarOpenShut()"><img src="/lib/images/toolbox.gif" border=none alt="Toolbar"></a></td></tr>';
      txt+='</div>';
      txt+='</div>';
      tb.innerHTML=txt;
      SOMIv.toolbar.state = 'closed';
    } else {
      var txt ='<div id="viewerToolbarBackground" style="position:relative; border: 1px solid black; background-color:white; width:29px;">';
      txt+='<div style="position:relative; background-color:white; z-index:3;">';
      txt+= '<table cellpadding=0 cellspacing=2><tr><td align="center"><a href="javascript:void(0)" onmouseover="return overlib(\'Click to close SOMI tools.\', OFFSETX, 13, WIDTH, 150, FGCOLOR, \'#FFFF33\');" onmouseout="return nd();" onClick="event.returnValue=false;viewerToolbarOpenShut()"><img src="/lib/images/toolbox.gif" border=none alt="Toolbar"></a></td></tr>';

      // Add Basemap Toolbox icon/button
      //
      txt+= '<tr></tr><tr></tr><tr><td align="center"><a href="javascript:void(0)" onmouseover="return overlib(\'Basemap tools.\', OFFSETX, 13, WIDTH, 100, FGCOLOR, \'#FFFF33\');" onmouseout="return nd();" onClick="event.returnValue=false;viewerLoadBasemapToolbox()"><img src="/lib/images/tb_basemap.png" border=none alt="Basemap Icon"></a></td></tr>';

      // Add Layer Toolbox icon/button
      //
      txt+= '<tr><td align="center"><a href="javascript:void(0)" onmouseover="return overlib(\'Layer tools.\', OFFSETX, 13, WIDTH, 90, FGCOLOR, \'#FFFF33\');" onmouseout="return nd();" onClick="event.returnValue=false;viewerLoadLayerToolbox()"><img src="/lib/images/tb_layer.png" border=none alt="Layer Icon"></a></td></tr>';

      // Add Quick Navigation Toolbox icon/button
      //
      txt+= '<tr><td align="center"><a href="javascript:void(0)" onmouseover="return overlib(\'Quick Navigation tools.\', OFFSETX, 13, WIDTH, 130, FGCOLOR, \'#FFFF33\');" onmouseout="return nd();" onClick="event.returnValue=false;viewerLoadQuickNavToolbox()"><img src="/lib/images/tb_quickNav.png" border=none alt="QuickNav Icon"></a></td></tr>';

      // Add Query Toolbox icon/button
      //
      txt+= '<tr><td align="center"><a href="javascript:void(0)" onmouseover="return overlib(\'Query tools.\', OFFSETX, 13, WIDTH, 130, FGCOLOR, \'#FFFF33\');" onmouseout="return nd();" onClick="event.returnValue=false;viewerLoadQueryToolbox()"><img src="/lib/images/tb_query.png" border=none alt="Query Tools Icon"></a></td></tr>';

      // Add Velocity Toolbox icon/button - IF one or more "geodesy"
      // layers are present.
      //
      var found = false;
      for (var i = 0, j=0; i < SOMIv.mapLayers.length; i+=1) {
         if ((SOMIv.mapLayers[i].stage == 'geodesy') && ((SOMIv.mapLayers[i].toggleable == undefined) || (SOMIv.mapLayers[i].toggleable))) {
           found = true;
         }
      }
      if (found) {
        txt+= '<tr><td align="center"><a href="javascript:void(0)" onmouseover="return overlib(\'Velocity tools.\', OFFSETX, 13, WIDTH, 85, FGCOLOR, \'#FFFF33\');" onmouseout="return nd();" onClick="event.returnValue=false;viewerLoadVelocityToolbox()"><img src="/lib/images/tb_vec.png" border=none alt="SOMI Velocity Tools"></a></td></tr>';
      }

      // Add Animation Toolbox icon/button
      //
      txt+= '<tr><td align="center"><a href="javascript:void(0)" onmouseover="return overlib(\'Animation tools.\', OFFSETX, 13, WIDTH, 100, FGCOLOR, \'#FFFF33\');" onmouseout="return nd();" onClick="event.returnValue=false;viewerLoadAnimationToolbox()"><img src="/lib/images/tb_movie.png" border=none alt="Animation Icon"></a></td></tr>';

      // Add Print icon/button
      //
      txt+= '<tr><td align="center"><a href="javascript:void(0)" onmouseover="return overlib(\'Print image.\', OFFSETX, 13, WIDTH, 75, FGCOLOR, \'#FFFF33\');" onmouseout="return nd();" onClick="event.returnValue=false;viewerPrintMap()"><img src="/lib/images/tb_print.png" border=none alt="Print image"></a></td></tr>';

      // Add Help icon/button
      //
      txt+= '<tr><td align="center"><a target="_new" href="http://troy.ucsd.edu/ubbcgi/ultimatebb.cgi?category=7" onmouseover="return overlib(\'Help.\', OFFSETX, 13, WIDTH, 35, FGCOLOR, \'#FFFF33\');" onmouseout="return nd();"><img src="/lib/images/tb_help.png" border=none alt="Question Icon"></a></td></tr>';
      txt+='</table>';
      txt+='</div>';
      txt+='</div>';
      tb.innerHTML=txt;
      SOMIv.toolbar.state = 'open';
    }
  }
}

function viewerLoadBasemapToolbox () {
  if (SOMIv.tools.basemaps.isLoaded) {
    viewerToolboxHideBasemaps();
    // Place transparent placeholder image to keep width of area to right of map
    // a constant size.
    SOMItb.innerHTML = '<img src="/lib/images/pixel.png" height=1 width=200 border=none>';
    SOMIv.tools.basemaps.isLoaded = false;
  } else {
    if (SOMIv.tools.layers.isLoaded) {
      SOMIv.tools.layers.isLoaded = false;
      viewerToolboxHideLayers();
    }
    if (SOMIv.tools.animation.isLoaded) {
      SOMIv.tools.animation.isLoaded = false;
      viewerToolboxHideAnimation();
    }
    if (SOMIv.tools.velocities.isLoaded) {
      SOMIv.tools.velocities.isLoaded = false;
      viewerToolboxHideVelocity();
    }
    if (SOMIv.tools.quicknav.isLoaded) {
      SOMIv.tools.quicknav.isLoaded = false;
      viewerToolboxHideQuickNav();
    }
    if (SOMIv.tools.query.isLoaded) {
      SOMIv.tools.query.isLoaded = false;
      viewerToolboxHideQuery();
    }
    viewerToolboxPresentBasemaps();
    SOMIv.tools.basemaps.isLoaded = true;
  }
}

function viewerLoadLayerToolbox () {
  if (SOMIv.tools.layers.isLoaded) {
    viewerToolboxHideLayers();
    // Place transparent placeholder image to keep width of area to right of map
    // a constant size.
    SOMItb.innerHTML = '<img src="/lib/images/pixel.png" height=1 width=200 border=none>';
    SOMIv.tools.layers.isLoaded = false;
  } else {
    if (SOMIv.tools.basemaps.isLoaded) {
      viewerToolboxHideBasemaps();
      SOMIv.tools.basemaps.isLoaded = false;
    }
    if (SOMIv.tools.animation.isLoaded) {
      SOMIv.tools.animation.isLoaded = false;
      viewerToolboxHideAnimation();
    }
    if (SOMIv.tools.velocities.isLoaded) {
      SOMIv.tools.velocities.isLoaded = false;
      viewerToolboxHideVelocity();
    }
    if (SOMIv.tools.quicknav.isLoaded) {
      SOMIv.tools.quicknav.isLoaded = false;
      viewerToolboxHideQuickNav();
    }
    if (SOMIv.tools.query.isLoaded) {
      SOMIv.tools.query.isLoaded = false;
      viewerToolboxHideQuery();
    }
    viewerToolboxPresentLayers();
    SOMIv.tools.layers.isLoaded = true;
  }
}

function viewerLoadVelocityToolbox () {
  if (SOMIv.tools.velocities.isLoaded) {
    viewerToolboxHideVelocity();
    // Place transparent placeholder image to keep width of area to right of map
    // a constant size.
    SOMItb.innerHTML = '<img src="/lib/images/pixel.png" height=1 width=200 border=none>';
    SOMIv.tools.velocities.isLoaded = false;
  } else {
    if (SOMIv.tools.basemaps.isLoaded) {
      viewerToolboxHideBasemaps();
      SOMIv.tools.basemaps.isLoaded = false;
    }
    if (SOMIv.tools.layers.isLoaded) {
      SOMIv.tools.layers.isLoaded = false;
      viewerToolboxHideLayers();
    }
    if (SOMIv.tools.animation.isLoaded) {
      SOMIv.tools.animation.isLoaded = false;
      viewerToolboxHideAnimation();
    }
    if (SOMIv.tools.quicknav.isLoaded) {
      SOMIv.tools.quicknav.isLoaded = false;
      viewerToolboxHideQuickNav();
    }
    if (SOMIv.tools.query.isLoaded) {
      SOMIv.tools.query.isLoaded = false;
      viewerToolboxHideQuery();
    }
    viewerToolboxPresentVelocity();
    SOMIv.tools.velocities.isLoaded = true;
  }
}

function viewerLoadAnimationToolbox () {
  if (SOMIv.tools.animation.isLoaded) {
    viewerToolboxHideAnimation();
    // Place transparent placeholder image to keep width of area to right of map
    // a constant size.
    SOMItb.innerHTML = '<img src="/lib/images/pixel.png" height=1 width=200 border=none>';
    SOMIv.tools.animation.isLoaded = false;
  } else {
    if (SOMIv.tools.basemaps.isLoaded) {
      viewerToolboxHideBasemaps();
      SOMIv.tools.basemaps.isLoaded = false;
    }
    if (SOMIv.tools.layers.isLoaded) {
      SOMIv.tools.layers.isLoaded = false;
      viewerToolboxHideLayers();
    }
    if (SOMIv.tools.velocities.isLoaded) {
      SOMIv.tools.velocities.isLoaded = false;
      viewerToolboxHideVelocity();
    }
    if (SOMIv.tools.quicknav.isLoaded) {
      SOMIv.tools.quicknav.isLoaded = false;
      viewerToolboxHideQuickNav();
    }
    if (SOMIv.tools.query.isLoaded) {
      SOMIv.tools.query.isLoaded = false;
      viewerToolboxHideQuery();
    }
    loadTopics('/xml/anime/animationTopicList.xml');
    SOMIv.tools.animation.isLoaded = true;
  }
}


function viewerLoadQuickNavToolbox () {
  if (SOMIv.tools.quicknav.isLoaded) {
    viewerToolboxHideQuickNav();
    // Place transparent placeholder image to keep width of area to right of map
    // a constant size.
    SOMItb.innerHTML = '<img src="/lib/images/pixel.png" height=1 width=200 border=none>';
    SOMIv.tools.quicknav.isLoaded = false;
  } else {
    if (SOMIv.tools.basemaps.isLoaded) {
      viewerToolboxHideBasemaps();
      SOMIv.tools.basemaps.isLoaded = false;
    }
    if (SOMIv.tools.layers.isLoaded) {
      SOMIv.tools.layers.isLoaded = false;
      viewerToolboxHideLayers();
    }
    if (SOMIv.tools.animation.isLoaded) {
      SOMIv.tools.animation.isLoaded = false;
      viewerToolboxHideAnimation();
    }
    if (SOMIv.tools.velocities.isLoaded) {
      SOMIv.tools.velocities.isLoaded = false;
      viewerToolboxHideVelocity();
    }
    if (SOMIv.tools.query.isLoaded) {
      SOMIv.tools.query.isLoaded = false;
      viewerToolboxHideQuery();
    }
    viewerToolboxPresentQuickNav();
    SOMIv.tools.quicknav.isLoaded = true;
  }
}


function viewerLoadQueryToolbox () {
  if (SOMIv.tools.query.isLoaded) {
    viewerToolboxHideQuery();
    // Place transparent placeholder image to keep width of area to right of map
    // a constant size.
    SOMItb.innerHTML = '<img src="/lib/images/pixel.png" height=1 width=200 border=none>';
    SOMIv.tools.query.isLoaded = false;
  } else {
    if (SOMIv.tools.basemaps.isLoaded) {
      viewerToolboxHideBasemaps();
      SOMIv.tools.basemaps.isLoaded = false;
    }
    if (SOMIv.tools.layers.isLoaded) {
      SOMIv.tools.layers.isLoaded = false;
      viewerToolboxHideLayers();
    }
    if (SOMIv.tools.animation.isLoaded) {
      SOMIv.tools.animation.isLoaded = false;
      viewerToolboxHideAnimation();
    }
    if (SOMIv.tools.velocities.isLoaded) {
      SOMIv.tools.velocities.isLoaded = false;
      viewerToolboxHideVelocity();
    }
    if (SOMIv.tools.quicknav.isLoaded) {
      SOMIv.tools.quicknav.isLoaded = false;
      viewerToolboxHideQuickNav();
    }
    viewerToolboxPresentQuery();
    SOMIv.tools.query.isLoaded = true;
  }
}


function viewerInteractionMode (mode) {
  if (mode == undefined) {
    return SOMIv.interactionMode;
  }
  // Since we are sticking to 3 query modes - add, info, remove - we need to do a little
  // extra checking as to whether the user has clicked Box Add/Remove and set icons accordingly.
  switch (mode) {
     case "pan":
       // Change icon to indicate panning mode.
       SOMIv.tileWell.style.cursor = SOMIv.activeSurface.style.cursor = 'move';
       if (document.getElementById('boxAdd') != undefined) {
       document.getElementById('boxAdd').src = '/lib/images/boxAdd.png'; 
       document.getElementById('boxRemove').src = '/lib/images/boxRemove.png'; 
       }
       if (document.getElementById('pan') != undefined) {
       document.getElementById('pan').src = '/lib/images/pan_hi.png'; 
       document.getElementById('zoom').src = '/lib/images/zoom.png'; 
       }

       break;
     case "zoom":
       // Change icon to indicate zoom mode.
       SOMIv.tileWell.style.cursor = SOMIv.activeSurface.style.cursor = 'crosshair';
       if (document.getElementById('boxAdd') != undefined) {
       document.getElementById('boxAdd').src = '/lib/images/boxAdd.png'; 
       document.getElementById('boxRemove').src = '/lib/images/boxRemove.png'; 
       }
       if (document.getElementById('zoom') != undefined) {
       document.getElementById('pan').src = '/lib/images/pan.png'; 
       document.getElementById('zoom').src = '/lib/images/zoom_hi.png'; 
       }
 
       break;
     case "selectbox":
       // Change icon to indicate select mode.
       SOMIv.tileWell.style.cursor = SOMIv.activeSurface.style.cursor = 'help';
        if (SOMIv.tools.query.selectMode == 'add') {
         if (document.getElementById('boxAdd') != undefined) {
          document.getElementById('boxAdd').src = '/lib/images/boxAdd_hi.png'; 
          document.getElementById('add').src = '/lib/images/add.png'; 
          document.getElementById('boxRemove').src = '/lib/images/boxRemove.png'; 
         }
        } else {
         if (document.getElementById('boxAdd') != undefined) {
          document.getElementById('boxAdd').src = '/lib/images/boxAdd.png'; 
          document.getElementById('boxRemove').src = '/lib/images/boxRemove_hi.png'; 
          document.getElementById('remove').src = '/lib/images/remove.png'; 
         }
        }

       break;
     default:
       // Unrecognized mode.
       alert("SOMI - Interaction Mode (" + mode + ") is not recognized.");
       return false;
       break;
   }
   SOMIv.interactionMode = mode;
   return true;
}





function viewerEnableRefresh (seconds) {
  var s = 300;
  if (seconds == undefined) {
    seconds = s;
  }
  SOMIv.hasLiveContent = true;
  viewerDisableRefresh();
  global_refreshCycleIntervalID = setInterval(viewerRefreshInPortal,seconds * 1000);
  return true;
}


function viewerDisableRefresh () {
  if (global_refreshCycleIntervalID != undefined) {
    clearInterval(global_refreshCycleIntervalID);
  }
  SOMIv.hasLiveContent = false;
  return true;
}

function viewerEnableGeodesyMap (nameOfFile) {
  if (nameOfFile != undefined) {
    SOMIv.geodesyMapFile = nameOfFile;
  }
}


function prepareTileForViewer () {
    var activeSurface = SOMIv.activeSurface;
    var tileWell = SOMIv.tileWell;
    var overlay = SOMIv.tileWell.overlay;
    var shadow = SOMIv.tileWell.shadow;
    var dim = SOMIv.dimensions;
    var tile = dim.tile;
		
    tile.img.className = 'tile';
    var agt=navigator.userAgent.toLowerCase();
    if (agt.indexOf("msie") != -1) {
      // Special just for Internet Explorer
      tile.img.style.zIndex = 25;
      shadow.style.zIndex = 49;
      overlay.style.zIndex = 50;
    }
    tile.img.style.width = dim.tileSize.x+'px';
    overlay.style.width = dim.tileSize.x+'px';
    shadow.style.width = dim.tileSize.x+'px';
    tile.img.style.height = dim.tileSize.y+'px';
    overlay.style.height = dim.tileSize.y+'px';
    shadow.style.height = dim.tileSize.y+'px';

    tileWell.appendChild(tile.img);

    if (SOMIv.pannable)
       activeSurface.onmousedown = pressViewer;
    activeSurface.onclick = clickViewer;
    activeSurface.ondblclick = clickViewer;
    positionTileWRTViewer({x: 0, y: 0});
}

function positionTileWRTViewer(moveDelta)
{
    //
    // This function performs one simple action - move the
    // top-left corner of the tile n pixels in the horizontal
    // and m pixels in the vertical.  All we need is a delta
    // vector and the viewer object.
    //
    var dim = SOMIv.dimensions;
    var tile = dim.tile;
    var overlay = SOMIv.tileWell.overlay;
    var shadow = SOMIv.tileWell.shadow;
		
    tile.x = dim.delta_x + moveDelta.x;
    tile.y = dim.delta_y + moveDelta.y;

    tile.img.style.left = tile.x+'px';
    overlay.style.left = tile.x+'px';
    shadow.style.left = tile.x+'px';
    tile.img.style.top = tile.y+'px';
    overlay.style.top = tile.y+'px';
    shadow.style.top = tile.y+'px';
}


function localizeViewerCoordinates (client) {
    var local = {x: client.x, y: client.y};

    for(var node = SOMIv; node; node = node.offsetParent) {
        local.x -= node.offsetLeft;
        local.y -= node.offsetTop;
    }
    
    return local;
}



function refreshViewerAsPage(x,y) {
  //
  // Simulates a click on an imagemap,
  // and, hence, a submission of the form|page by creating
  // image.x and image.y hidden form elements, assigning the
  // "new center x and y" values to those elements, then
  // firing the CLICK action of the form's submit button.
  //
  // This function assumes viewer.context == 'page'
  //
  var image_xElement = document.createElement('input');
  image_xElement.style.display = 'none';
  image_xElement.name = 'image.x';
  image_xElement.value = x;
  var image_yElement = document.createElement('input');
  image_yElement.style.display = 'none';
  image_yElement.name = 'image.y';
  image_yElement.value = y;
  document.main_form.appendChild(image_xElement);
  document.main_form.appendChild(image_yElement);
  var obj = document.getElementById('refreshButton');
  if (obj != undefined) {
    obj.click();
  } else {
    document.main_form.submit();
  }
}





function viewerRefreshInPortal () {
  //
  // Simulates a click on an imagemap,
  // and, hence, a submission of the form|page by creating
  // image.x and image.y hidden form elements, assigning the
  // "new center x and y" values to those elements, then
  // firing the CLICK action of the form's submit button.
  //
  // This function assumes viewer.context == 'portal'
  //
  if ((SOMIv.context == 'portal') && (SOMIv.state == 'loading')) {
    return;
  }
  if ((SOMIv.context == 'portal') && (SOMIv.tools.animation.isLoaded) && (SOMIv.tools.animation.isRunning)) {
    return;
  }
  if ((SOMIv.context == 'portal') && (SOMIv.tools.animation.isLoaded)) {
    // Cancel any currently running activities, remove
    // any movie features from the map.
    //
    stopSOMIReload();
    stopSOMIAnimation();
  }
  var newMapImageURL = viewerGetImageURL();
  newMapImageURL += '&ec=0';
  if (SOMIv.hasLiveContent) {
    newMapImageURL  += '&R=' + Math.random();
  }
  startProgressCycleAnimation();
  if (SOMIv.tools.quicknav.isLoaded) {
    viewerQuickNavSyncFromViewer();
  }
  SOMIv.state = 'loading';
  SOMIv.dimensions.tile.img.src = newMapImageURL;
  if (SOMIv.tools.basemaps.isLoaded) {
    document.getElementById('currentScale').innerHTML = parseInt(SOMIv.scale);
  }
  // Check if portal user is defined and update db
  checkPortalUser();
}



function moveViewer (event) {
    if ((SOMIv.context == 'portal') && (SOMIv.state == 'loading')) {
      return;
    }
    var ev = getViewerEvent(event);
    var mouse = localizeViewerCoordinates({x: ev.clientX, y: ev.clientY});

    positionTileWRTViewer({x: (mouse.x - SOMIv.start.x), y: (mouse.y - SOMIv.start.y)});
}



function viewerHideRubberBandBox () {
  if (document.getElementById('RBBBOX') != undefined) {
    rbbbox = document.getElementById('RBBBOX');
    rbbbox.style.visibility='hidden';
    rbbbox.style.top='0px';
    rbbbox.style.left='0px';
    rbbbox.style.width='0px';
    rbbbox.style.height='0px';
  }
}



function boxOnViewer (event) {
    if ((SOMIv.context == 'portal') && (SOMIv.state == 'loading')) {
      return;
    }
    var ev = getViewerEvent(event);
    var rbbbox = document.getElementById('RBBBOX');
    rbbbox.style.width = ev.clientX - SOMIv.rbbbox.start.x + 'px';
    rbbbox.style.height = ev.clientY - SOMIv.rbbbox.start.y + 'px';
    return;
}

    


function viewerGetImageCoordinatesFromEvent(event) {
   //-----------------------------------------------------
   // Determine the coordinates (in pixels) of the click
   // WITH RESPECT TO THE UPPER LEFT CORNER OF THE VIEWER
   //            (VxOfClick,VyOfClick)
   //
   // Then simply add the difference between the upper left
   // corner of the tile and the upper left corner of the
   // viewer (a relative measurement) to the Vx,Vy position
   // to arrive at a pair of coordinates (in pixels) of the
   // click WITH RESPECT TO THE UPPER LEFT CORNER OF THE IMAGE
   //            (IxOfClick,IyOfClick)
   //-----------------------------------------------------
   var dim = SOMIv.dimensions;
   var theTop = viewerGetScrolledOffScreenOffset();
   var VxOfClick = event.x - SOMIv.offsetLeft;
   var VyOfClick = (event.y + theTop) - SOMIv.offsetTop;
   var IxOfClick = Math.abs(dim.delta_x) + VxOfClick;
   var IyOfClick = Math.abs(dim.delta_y) + VyOfClick;
   return({x : IxOfClick, y : IyOfClick});
}





function clickViewer(event) {

   var dim = SOMIv.dimensions;
   var ev = getViewerEvent(event);
 
   var click = viewerGetImageCoordinatesFromEvent({x: ev.clientX, y: ev.clientY});

   switch (ev.type) {
     case "click":
       viewerQFeatureQuery(click.x,click.y);
 
       // If we've just had a doubleclick then ignore it
       if (hadDoubleClick()) {
         return false;
       }

       // Otherwise set timer to act.  It may be preempted by a doubleclick.
       savEvent = ev.type;
       d = new Date();
       savEvtTime = d.getTime();
       savTO = setTimeout("doClick(savEvent)", dcTime);
       break;
     case "dblclick":
       var d = new Date();
       dcAt = d.getTime();
       if (savTO != null) {
         clearTimeout( savTO );          // Clear pending Click
         savTO = null;
       }
       // Submit and recenter map on double-clicked position
       //
       if (SOMIv.context == 'page') {
         // Replace form element values for map center with original
         // map center coordinates....since this is a precise double-click
         // to the location chosen by the user....disregard any previous
         // panning/exploration.
         document.main_form.cx.value = SOMIv.centerLongitudeFirst;
         document.main_form.cy.value = SOMIv.centerLatitudeFirst;
         refreshViewerAsPage(click.x,click.y);
       } else {
         // Initiate calculation of "->to" coordinates...which will eventually
         // fire a fetch for a new image and reposition that image in the viewer.
         // Indicate a reload is requested (once new map center
         // coordinates are fetched).
         //
         SOMIv.reload = true;
         calculateMapCenterCoordinates(click.x,click.y);
       }
       break;
     default:
   }
}

function viewerGetScrolledOffScreenOffset () {
  
   // pj: 07/24/2007 see below
   // var t;
   var t = 0;

  if (document.documentElement && document.documentElement.scrollTop) {
    t = document.documentElement.scrollTop;
  }



  // pj, 07/16/2007: document.body is returning incorrect value of 0
  // in portal when vertical scroll is in use.  this results in 
  // problems highlighting sites, using bbox in surface layer when screen is
  // scrolled.  avoid this code if in portal

  // pj, 07/24/2007: however, when screen is not scrolled, we need a t value
  // of 0.  set above.
  
//  if (!(SOMIv.context == 'portal')) {

//alert ('cPU: ' + checkPortalUser());
  if (checkPortalUser() == undefined) {
     if (document.body) {
        t = document.body.scrollTop;     
     }
  }


  return t;
}


// Various functions for checking SOMI properties
// to be used in portal development, etc.
function getLon () {
  return SOMIv.centerLongitudeCurrent;
}

function getLat () {
  return SOMIv.centerLatitudeCurrent;
}

function getScale () {
  return SOMIv.scale;
}

function gettLState () {
  return SOMIv.tLState;
}

// Obfuscation didn't like returning SOMIv.state directly...
// Don't ask me why. Ian Dec. 11, 2006
function getState () {
  var currentStatus = SOMIv.state;
  return currentStatus;
}

function getBasemap () {
  for (var i = 0; i < SOMIv.basemaps.length; i+=1) { 
    if (SOMIv.basemaps[i].isactive) {
      return SOMIv.basemaps[i].name;
    }
  }
}

function getActiveLayers () {
  var actLayers='';
  for (var i = 0; i < SOMIv.mapLayers.length; i += 1) {
    if ((SOMIv.mapLayers[i].toggleable == undefined) || (SOMIv.mapLayers[i].toggleable)) {
      if (SOMIv.mapLayers[i].state == 'on') {
        // Very messy encoding follows for compliance in AJAX XML 
        // and URL results.
        if (SOMIv.mapLayers[i].stage == 'base') {
          actLayers += '%26amp%3Bblayer%3D' + SOMIv.mapLayers[i].name;
        } else {
          actLayers += '%26amp%3Bglayer%3D' + SOMIv.mapLayers[i].name;
        }
      }
    }
  }
  return actLayers;
}

function checkPortalUser () {
  try {
    if (portalUsername != undefined) {
      saveSOMI_State();
    } else{
  // We're outside of the portal. Do nothing.
	return;
  }
    
  } catch (err) {
  // We're outside of the portal. Do nothing.
     return;
  }
  return true;
}


function checkActiveVels () {
  // Make sure the user has an active velocity layer
  // when they change values in the Velocities toolbox.
  var c = 0;
  for (var i = 0; i < SOMIv.mapLayers.length; i += 1) {
    if (SOMIv.mapLayers[i].stage == "geodesy") {
      if (SOMIv.mapLayers[i].state == "on") {
        c += 1;
      }
    }
  }
  if (c == 0) {
    alert("Please activate a velocity layer in the Layers toolbox");
  } else {
    viewerRefreshInPortal();
  }
}


function pressViewer(event) {
    if ((SOMIv.context == 'portal') && (SOMIv.state == 'loading')) {
      return;
    }
    var dim = SOMIv.dimensions;

    // (x,y) - event - Position [in pixels] with respect to top of PAGE (possibly scrolled off screen)
    // (x,y) - mouse - Position [in pixels] with respect to "parent" DOM element.
    // (x,y) - click - Position [in pixels] with respect to SOMI map image (aka the tile).  Do not confuse
    //                 this with SOMI viewer....the viewable portion of a map. 

    var ev = getViewerEvent(event);
    var mouse = localizeViewerCoordinates({x: ev.clientX, y: ev.clientY});
    var click = viewerGetImageCoordinatesFromEvent({x: ev.clientX, y: ev.clientY});

    var theTop = viewerGetScrolledOffScreenOffset();

    if ((viewerInteractionMode() == 'zoom') || (viewerInteractionMode() == 'selectbox')) {
      // Note : The only graphical difference between the zoom box and the select box
      //        is the color of the box frame.
      var rbbbox;
      if (document.getElementById('RBBBOX') != undefined) {
        rbbbox = document.getElementById('RBBBOX');
        if (viewerInteractionMode() == 'selectbox') { 
          rbbbox.style.borderColor = '#FF5500';
        }
        if (viewerInteractionMode() == 'zoom') {
          rbbbox.style.borderColor = '#00FFAA';
        }
        // Reposition (new upper left corner) the selectbox
        rbbbox.style.visibility = 'visible';
        rbbbox.style.left = ev.clientX + 'px';
        rbbbox.style.top = ev.clientY + theTop + 'px';
      } else {
        // First time use of zoom or selectbox.  Create the DOM
        // element and assign appropriate style.
        rbbbox = document.createElement("div");
        rbbbox.id = 'RBBBOX';
        rbbbox.style.position = 'absolute';
        rbbbox.style.zIndex = 500;
        rbbbox.style.visibility = 'visible';
        rbbbox.style.backgroundColor = 'transparent';
        rbbbox.style.width = '0px';
        rbbbox.style.height = '0px';
        rbbbox.style.borderStyle = 'solid';
        rbbbox.style.borderWidth = '1px';
        if (viewerInteractionMode() == 'zoom') {
          rbbbox.style.borderColor = '#00FFAA';
        }
        if (viewerInteractionMode() == 'selectbox') {
          rbbbox.style.borderColor = '#FF5500';
        }
        rbbbox.style.left = ev.clientX + 'px';
        rbbbox.style.top = ev.clientY + theTop + 'px';
        document.body.appendChild(rbbbox);
      }
      SOMIv.rbbbox.ulxWRTMapImage = click.x;
      SOMIv.rbbbox.ulyWRTMapImage = click.y
      SOMIv.rbbbox.start = {x: ev.clientX, y: ev.clientY};
      // Attach listener to expand the selection box as the user
      // moves the mouse down and to the right.  This listener will
      // not move the map.
      this.onmousemove = boxOnViewer;
    } else {
      // Panning mode...the user grabbed the map.  Part of a drag event.
      //			
      SOMIv.pressed = true;
      SOMIv.tileWell.style.cursor = SOMIv.activeSurface.style.cursor = 'move';
      SOMIv.start = {x: mouse.x, y: mouse.y};
      this.onmousemove = moveViewer;
    }
}


function getTileXOfViewCenter () {
   if (SOMIv.dimensions.delta_x > 0) {
      // Meaning the left edge of our image can be seen.
      return(parseInt(SOMIv.dimensions.width / 2) - SOMIv.dimensions.delta_x);
   } else {
      // Meaning the left edge of our image is still hidden.
      return(parseInt(SOMIv.dimensions.width / 2) + (-1 * SOMIv.dimensions.delta_x));
   }
}

function getTileYOfViewCenter () {
   if (SOMIv.dimensions.delta_y > 0) {
      // Meaning the top edge of our image can be seen.
      return(parseInt(SOMIv.dimensions.height / 2) - SOMIv.dimensions.delta_y);
   } else {
      // Meaning the top edge of our image is still hidden.
      return(parseInt(SOMIv.dimensions.height / 2) + (-1 * SOMIv.dimensions.delta_y));
   }
}



function releaseViewer (event) {
  var ev = getViewerEvent(event);
  var mouse = localizeViewerCoordinates({x: ev.clientX, y: ev.clientY});
  var dim = SOMIv.dimensions;


  if ((viewerInteractionMode() == 'zoom') || (viewerInteractionMode() == 'selectbox')) {
    if (document.getElementById('RBBBOX') != undefined) {
      // Deactivate the "rubber band box" effect.
      SOMIv.activeSurface.onmousemove = null;
      SOMIv.pressed = false;

      // a. Find out what the map image coordinates (pixels) are for the upper
      //    left corner of the selection/zoom box.
      //
      var rbbbox = document.getElementById('RBBBOX');
      var parts = rbbbox.style.width.split("p");
      var rbW = parts[0];
      parts = rbbbox.style.height.split("p");
      var rbH = parts[0];
      var boxWidth = parseInt(rbW);
      var boxHeight = parseInt(rbH);

      if (viewerInteractionMode() == 'selectbox') {
        // b. Issue the bounding box query (NOT GOING TO THE SERVER)
        //    and get on with it.
        var ulx = SOMIv.rbbbox.ulxWRTMapImage;
        var uly = SOMIv.rbbbox.ulyWRTMapImage;
        var lrx = ulx + boxWidth;
        var lry = uly + boxHeight;
        viewerQFeatureQuery(ulx,uly,lrx,lry);
      } else {
        // b. Combine the upper left corner position with the width and height
        //    of the selection/zoom box to determine the CENTER of the box.
        //
        
        var lon = calculateLongitudeFromMapX(SOMIv.rbbbox.ulxWRTMapImage + parseInt(boxWidth / 2));
        var lat = calculateLatitudeFromMapY(SOMIv.rbbbox.ulyWRTMapImage + parseInt(boxHeight / 2));
  
        // c. Compare the width of the box to the width of the map
        //    to determine a new scale to go to. 
        var currentMapImageWidth = SOMIv.dimensions.tileSize.x;
        var currentMapScale = SOMIv.scale;
        var newScale = parseInt(currentMapScale / parseInt(currentMapImageWidth/boxWidth)); 
        
        // d. Reposition, and zoom in, the map.
        //
        if ((newScale < SOMIv.scale) && (newScale > SOMIv.minScale)) {
          SOMIv.zoomSlider.setValue(newScale);
          viewerGoToPositionAndScale(lon,lat,newScale);
        }
      }
    }
    // Hide the rubber-band box (if present)
    viewerHideRubberBandBox();
  }


  if (SOMIv.pressed) {
    // Deactivate the "drag" effect.
    SOMIv.activeSurface.onmousemove = null;
    SOMIv.pressed = false;

    // Calculate difference between initial click and final release
    // (in viewport coordinates).  Use this delta to update our
    // dim.delta_x and dim.delta_y.
    //
    // This is where the position of our single tile is
    // saved for future reference.
    // Do not change this.  (See notes above in viewer creation).
    var dx = (mouse.x - SOMIv.start.x);
    var dy = (mouse.y - SOMIv.start.y);

    dim.delta_x += dx;
    dim.delta_y += dy;

    var currentTileXFromCenterView = getTileXOfViewCenter();
    var currentTileYFromCenterView = getTileYOfViewCenter();
    // If new position of the tile with respect to the viewport is such that 30% (or less) of the buffer
    // originally around the view port remains...then resubmit map to new center location.
    //
    if (
         (Math.abs(parseInt(dim.tileSize.x/2) - currentTileXFromCenterView) > parseInt(9*global_widthOfViewerOverflow/10))
       ||
         (Math.abs(parseInt(dim.tileSize.y/2) - currentTileYFromCenterView) > parseInt(9*global_heightOfViewerOverflow/10))
       ) {
      if (SOMIv.context == 'page') {
        refreshViewerAsPage(currentTileXFromCenterView,currentTileYFromCenterView);
      } else {
        // Indicate a reload is requested (once new map center
        // coordinates are fetched).
        //
        SOMIv.reload = true; 
        calculateMapCenterCoordinates(currentTileXFromCenterView,currentTileYFromCenterView);
      }
    } else {
      // Just recalculate map center.
      calculateMapCenterCoordinates(currentTileXFromCenterView,currentTileYFromCenterView);
      if (SOMIv.tools.query.isLoaded && (SOMIv.tools.query.type == 'extent') && (SOMIv.tools.query.subtype == 'viewer'))
        viewerQuerySyncFromViewerExtent();
     }
  }
}



function calculateLongitudeFromMapX (px) {

  // px is a PIXEL (integer) with respect to the upper left
  // corner of the map image.
  var currentMapScale = SOMIv.scale;
  var imageWidth = SOMIv.dimensions.tileSize.x;
  var DPI = 72.0;
  var mapWidthInInches = parseFloat(imageWidth/DPI);
  var inchesInAMeter = 39.37;
  var metersInADegree = (2.0 * Math.PI * 6378137.0) / 360.0;
  var mapWidthInDegrees = (currentMapScale * mapWidthInInches) / (inchesInAMeter * metersInADegree);
  var degreesPerPixel = parseFloat(mapWidthInDegrees/imageWidth);
  var centerPX = parseInt(imageWidth/2.0);
  var offsetPX = Math.abs(parseInt(px) - centerPX);
  var longitude;
  if (px > centerPX) {
    longitude = parseFloat(SOMIv.centerLongitudeFirst) + (parseFloat(offsetPX) * degreesPerPixel);
  } else {
    longitude = parseFloat(SOMIv.centerLongitudeFirst) - (parseFloat(offsetPX) * degreesPerPixel);
  }
//  alert ("SOMIv.scale: " + SOMIv.scale + " SOMIv.centerLonFirst: " + SOMIv.centerLongitudeFirst + " lon: " + longitude + " mapWidthInDeg: " + mapWidthInDegrees + " offsetPX: " + offsetPX);
  return longitude;
}



function calculateLatitudeFromMapY (py) {
  // py is a PIXEL (integer) with respect to the upper left
  // corner of the map image.
  var currentMapScale = SOMIv.scale;
  var imageHeight = SOMIv.dimensions.tileSize.y;
  var imageWidth = SOMIv.dimensions.tileSize.x;
  var DPI = 72.0;
  var mathPI = 3.14159265358979323846;
  var mapWidthInInches = parseFloat(imageWidth/DPI);
  var mapWidthInDegrees = ((parseFloat(180.0) * mapWidthInInches * currentMapScale) / (mathPI * 6378137.0 * 39.37));
  var degreesPerPixel = parseFloat(mapWidthInDegrees/imageWidth);
  var centerPY = parseInt(imageHeight/2.0);
  var offsetPY = Math.abs(parseInt(py) - centerPY);
  var latitude;
  if (py < centerPY) {
    latitude = parseFloat(SOMIv.centerLatitudeFirst) + (offsetPY * degreesPerPixel);
  } else {
    latitude = parseFloat(SOMIv.centerLatitudeFirst) - (offsetPY * degreesPerPixel);
  }
  return latitude;
}







function calculateMapCenterCoordinates(tileXFromCenterView,tileYFromCenterView) {
    // Convert viewport center (after user's drag event)
    // to pixel coordinates wrt the tile.  These coordinates
    // can then be used to resubmit a form or change other
    // map parameters.
    //
    var smxParams = 'cx=' + SOMIv.centerLongitudeFirst;
    smxParams += '&cy=' + SOMIv.centerLatitudeFirst;
    smxParams += '&proj=' + SOMIv.projection;
    smxParams += '&scale=' + SOMIv.scale;
    smxParams += '&width=' + SOMIv.dimensions.tileSize.x;
    smxParams += '&height=' + SOMIv.dimensions.tileSize.y;
    smxParams += '&pixel=f0' + '|' + tileXFromCenterView + '|' + tileYFromCenterView + '|';

    // TEST - Michael 2006-06-23
    //
    // Try doing mathematics for calculations right here....instead of beginning Ajax
    // procedures.
    //
    //    Convert combination of map scale & map width to a
    //    range of degrees (span) we can use to establish
    //    a west/south/east/north bounding map extent for GMT.
    //
    //    (calculations made/estimated based on dialog in GMT
    //    listserver topic = "Simple Scale Question"
    //    GMT-HELP:11639  2003-10-21.
    //
    //    39.37 : Inches in a meter
    //    6378137.0 : Radius of the Earth (meters) semi-major axis of WGS84 ellipsoid
    if (SOMIv.context == 'portal') {
      var newMapCenterLongitude = calculateLongitudeFromMapX(tileXFromCenterView);
      var newMapCenterLatitude = calculateLatitudeFromMapY(tileYFromCenterView);
      
      // If we are crossing the date line adjust the longitude accordingly
      // Ian - 2008-06-02
      if (newMapCenterLongitude > 180) newMapCenterLongitude = newMapCenterLongitude - 360;
      if (newMapCenterLongitude < -180) newMapCenterLongitude = newMapCenterLongitude + 360;
 
      var nLon = Math.round(newMapCenterLongitude*1000)/1000
      var nLat = Math.round(newMapCenterLatitude*1000)/1000

      var iDiv = document.getElementById('positionText');
      iDiv.innerHTML = '<font class="setFontSmall">' + nLat + '&nbsp;,&nbsp;' + nLon + '</font>';
      iDiv.style.left = SOMIv.dimensions.width  - 125 + 'px';
      iDiv.style.top = SOMIv.dimensions.height - 15 + 'px';
      if (document.getElementById('form-cx')) {
        document.getElementById('form-cx').value = newMapCenterLongitude;
      }
      if (document.getElementById('form-cy')) {
        document.getElementById('form-cy').value = newMapCenterLatitude;
      }
      SOMIv.centerLongitudeCurrent = newMapCenterLongitude;
      SOMIv.centerLatitudeCurrent = newMapCenterLatitude;
      if ((SOMIv.reload == true) && (SOMIv.state != 'loading')) {
        viewerRefreshInPortal();
      }
      if (SOMIv.tools.quicknav.isLoaded) viewerQuickNavSyncFromViewer();
      global_statusDIV.value = '';
    } else {
      getFeaturePositions(smxParams,processMapCenterCoordinates);
    }
}







function processMapCenterCoordinates(req,fetchedDate) {
  //
  // Take XMLHttpRequest (that has "returned") and process specific to
  // the type of information we are expecting.
  // Document handling process starts here.
  //
  //

  // Note date of fetch and perform any notifications
  // to the user here.
  //
  //   Parse returned content from text into xml DOM
  //
  var dom = G_parseXMLTextIntoDOM(req.responseText);

  //   Parse DOM and update state of SOMI
  //
  var status = updateMapCenterCoordinates(dom);
  if (status) {
     return true;
  } else {
     alert("updateMapCenterCoordinates : Error : AJAX failed  Sorry.  Please turn off RSS in SOMI.");
     return false;
  }
}


function updateMapCenterCoordinates(dom) {
  // We're expecting an XML DOM object so it
  // better be one.
  if (typeof dom == "undefined") {
    alert("updateMapCenterCoordinates : Error : Type of returned document is not defined....failure!");
    return false;
  }
  var items = dom;
  var points = items.getElementsByTagName('point');
  for (var i = 0, len = points.length; i < len; i+=1) {
    var point = points[i];

    // a. Collect information returned for this point.
    //
    var lat = point.getElementsByTagName('lat')[0].firstChild.nodeValue;
    var lon = point.getElementsByTagName('lon')[0].firstChild.nodeValue;
    var latRounded = Math.round(lat*1000)/1000
    var lonRounded = Math.round(lon*1000)/1000

    // b. Change values of form elements governing
    //    "current" map center....in other words the coordinates
    //    of the center of the visible map.
    //
    if (SOMIv.context == 'page') {
      document.main_form.cx.value = lon;
      document.main_form.cy.value = lat;
    } else {
      var iDiv = document.getElementById('positionText');
      iDiv.innerHTML = '<font class="setFontSmall">' + latRounded + '&nbsp;,&nbsp;' + lonRounded + '</font>';
      iDiv.style.left = SOMIv.dimensions.width  - 125 + 'px';
      iDiv.style.top = SOMIv.dimensions.height - 15 + 'px';
      if (document.getElementById('form-cx')) {
        document.getElementById('form-cx').value = lon;
      }
      if (document.getElementById('form-cy')) {
        document.getElementById('form-cy').value = lat;
      }
      SOMIv.centerLongitudeCurrent = lon;
      SOMIv.centerLatitudeCurrent = lat;
      if ((SOMIv.reload == true) && (SOMIv.state != 'loading')) {
        viewerRefreshInPortal();
      }
    }
    global_statusDIV.value = '';
 }
 return true;
}



function viewerPrintMap () {
  var WindowArgs;
  WindowArgs = 'width=700,height=700,status=yes,menubar=yes,resizable=yes,scrollbars=yes,screenX=0,screenY=0,top=0,left=0';
  // Make target map image WITH embedded scalebar AND copyright.
  var u = viewerGetImageURL() + '&es=1' + '&ec=1';
  window.open(u,'_blank',WindowArgs);
}


function viewerGetImageURL () {
  //
  // Build URL for map tile based on current configuration
  // and return to caller.
  //
  var host = window.location.host.split(":");
  var u = 'http://' + host[0] + '/cgi-bin/mssomi4g?imagesize=' + SOMIv.dimensions.tileSize.x + ',' + SOMIv.dimensions.tileSize.y + '&scale=' + SOMIv.scale;
  u += '&cx=' + SOMIv.centerLongitudeCurrent + '&cy=' + SOMIv.centerLatitudeCurrent;
  u += '&basemf=' + viewerGetActiveBaseMapFile() + '&geodesymf=' + SOMIv.geodesyMapFile + '&nocross=1';
  if (SOMIv.tools.velocities.isLoaded) {
    u += viewerToolboxURLParamsVelocity();
  }
  for (var i = 0; i < SOMIv.mapLayers.length; i += 1) {
    if ((SOMIv.mapLayers[i].toggleable == undefined) || (SOMIv.mapLayers[i].toggleable)) {
      if (SOMIv.mapLayers[i].state == 'on') {
        if (SOMIv.mapLayers[i].stage == 'base') {
          u += '&blayer=' + SOMIv.mapLayers[i].name;
        } else {
          // pj, 05/24/2007
          SOMIv.showVelMags = true;

          u += '&glayer=' + SOMIv.mapLayers[i].name;
        }
      }
    }
  }
  // pj, 05/24/2007: show velocity magnitudes if vel layer active
  if (SOMIv.showVelMags == true) u += '&vhsf=1&vshc=1&vhv=1';
  return u;
} 


function viewerGoToPositionAndScale (longitude, latitude, scale) {
  if ((longitude != undefined) && (latitude != undefined)) {
    if ((SOMIv.context == 'portal') && (SOMIv.state == 'loading')) {
      return;
    }
    SOMIv.centerLongitudeCurrent = longitude;
    SOMIv.centerLatitudeCurrent = latitude;
    if (scale != undefined) {
      SOMIv.scale = scale;
    }
    viewerRefreshInPortal();
  }
}


function viewerGetExtentMaxLongitude () {
  return SOMIv.maxLongitude;
}
function viewerGetExtentMinLongitude () {
  return SOMIv.minLongitude;
}
function viewerGetExtentMinLatitude () {
  return SOMIv.minLatitude;
}
function viewerGetExtentMaxLatitude () {
  return SOMIv.maxLatitude;
}



function viewerZoomIn () {
  if ((SOMIv.context == 'portal') && (SOMIv.state == 'loading')) {
    return;
  }
  var newScale = parseInt(SOMIv.scale) - parseInt((SOMIv.scale - SOMIv.minScale) / 2);
  if ((newScale < SOMIv.scale) && (newScale > SOMIv.minScale)) {
    viewerZoomTo(newScale);
  }
}

function viewerZoomTo (newScale) {
  if ((SOMIv.context == 'portal') && (SOMIv.state == 'loading')) {
    return;
  }
  if (newScale == undefined) {
    return;
  }
  SOMIv.scale = newScale;
  SOMIv.zoomSlider.setValue(newScale);
  viewerGoToPositionAndScale(SOMIv.centerLongitudeCurrent,SOMIv.centerLatitudeCurrent,newScale);
}

function viewerZoomOut () {
  if ((SOMIv.context == 'portal') && (SOMIv.state == 'loading')) {
    return;
  }
  var newScale = parseInt(SOMIv.scale) + parseInt((SOMIv.scale - SOMIv.minScale) / 2);
  if ((newScale > SOMIv.scale) && (newScale < SOMIv.maxScale)) {
    viewerZoomTo(newScale);
  }
}

function viewerSliderNewScale(sliderValuePct) {
  if (sliderValuePct == undefined) {
    return;
  }
  if ((SOMIv.context == 'portal') && (SOMIv.state == 'loading')) {
    return;
  }
  
  var a = Math.log(SOMIv.maxScale);
  var b = Math.log(SOMIv.minScale);
  var c = 1.0 - sliderValuePct;
  var x = (b + (a-b)*c);
  var newScale = Math.exp(x);

  if (newScale == 0) {
  newScale = SOMIv.maxScale;
  } else if (c == 0) {
  newScale = SOMIv.minScale;
  } else if (sliderValuePct == 0) {
  newScale = SOMIv.maxScale;
  } else if (newScale > SOMIv.maxScale) {
  newScale = SOMIv.maxScale;
  } else if (newScale < SOMIv.minScale) {
  newScale = SOMIv.minScale;
  } 
  viewerZoomTo(newScale);
  return newScale;
}


function viewerInclude(sURI,t)
{
  if (document.getElementsByTagName)
  {

    var base = document.getElementsByTagName("base")[0];
    var baseI = base ? base.href : "";

    if (t == 'js') {
      if (!global_jsIncludeList) {
        global_jsIncludeList = {};
        var s = document.getElementsByTagName("script");
        for (var i=0,len=s.length; i < len; i+=1) {
          if (s[i].src) {
            global_jsIncludeList[s[i].src] = true;
          }
        }
      }
      if (!global_jsIncludeList[sURI]) {
        var o = document.createElement("script");
        o.type = "text/javascript";
        o.language = "javascript";
        o.src = sURI.match(/^\//) ? sURI : baseI + sURI;
        global_jsIncludeList[sURI]=true;
        document.getElementsByTagName("head")[0].appendChild(o);
      }
      return false;
    }
    if (t == 'css') {
      if (!global_cssIncludeList) {
        global_cssIncludeList = {};
        var s = document.getElementsByTagName("link");
        for (var i=0,len=s.length; i < len; i+=1) {
          if (s[i].src) {
            global_cssIncludeList[s[i].src] = true;
          }
        }
      }
      if (!global_cssIncludeList[sURI]) {
        var o = document.createElement("link");
        o.rel = "stylesheet";
        o.type = "text/css";
        o.href = sURI.match(/^\//) ? sURI : baseI + sURI;
        global_cssIncludeList[sURI]=true;
        document.getElementsByTagName("head")[0].appendChild(o);
      }
      return false;
    }
  }
}



function viewerSliderClicked (event) {
  var localX = event.clientX;
  for(var node = document.getElementById('zsRail'); node; node = node.offsetParent) {
    localX -= node.offsetLeft;
  }
  var railLeft = SOMIv.dimensions.width - 286;
  var railRight = railLeft + document.getElementById('zsRail').width;
  var clickPct = (localX-railLeft)/(railRight-railLeft);
  var newScale = viewerSliderNewScale(clickPct);
  SOMIv.zoomSlider.setValue(newScale);
}



function viewerTieSlider () {
  SOMIv.zoomSlider.setValue = function (value,ignore) {
    if (typeof(value) == 'string')  {
      value = parseFloat(value);
    }
    if (isNaN(value)) {
      value = this.defaultValue;
    }
    // set within min/max bounds
    var rangeValue = (this.rightValue >= this.leftValue)?
        Math.min(Math.max(value,this.leftValue),this.rightValue) - this.leftValue :
        Math.max(Math.min(value,this.leftValue),this.rightValue) - this.leftValue;
    var sliderValuePct = (value/this.rightValue);
    if (value < 1000) {
      if ((ignore == undefined) || (! ignore)) {
        // VALUE IN SLOT RANGE - COMING FROM MOUSE UP (DRAG)
        // Need to calcuate map scale
        viewerSliderNewScale(sliderValuePct);
      } else {
        //alert('Slider.setValue - FYI - Received value (' + value + ') but also told to ignore');
      }
    } else {
      // MAP SCALE - COMING FROM ZOOM IN OR ZOOM OUT (CLICK)
      // Need to calculate POSITION of SLIDER
      var a = Math.log(SOMIv.maxScale);
      var b = Math.log(SOMIv.minScale);
      var x = Math.log(value);
      var newZoneVal = (this.rightValue - this.leftValue)*((x-b)/(a-b));
      var pos;
      var off = SOMIv.dimensions.left + SOMIv.dimensions.width - 50; // Offset (to the left) from the right-edge of the image viewer
      var numIntervals = 15;
      var intervalSpacing = 14; // As in pixels
      if (newZoneVal <= 1) {
        pos = this.offset + numIntervals*intervalSpacing + 'px';
      } else {
        pos = this.offset + (parseInt(numIntervals - newZoneVal)*intervalSpacing) + 'px';
      }
      //alert('New position for scale (' + value + ') is (' + pos + ') (' + this.offset + ')');
      // move button to calculated position
      if (this.orientation == "h")  this.button.left = pos;
        else  this.button.top = pos;
      // call slider event handler, unless ignore is true
      if (this.onchange && (!ignore))  this.onchange(null);
    }
  }
}

function viewerAddLoadEvent(func) {
  // From Simon Willison : http://csrc.ucsd.edu/projects/pgm/louisiana-map.html
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      if (oldonload) {
        oldonload();
      }
      func();
    }
  }
}

