/*
*  MuseZoom 0.9.7 beta
*
*  This script enables web browsers to navigate high-resolution images saved as component
*  JPEGs following certain arbitrary conventions for resolution and relative coordinates.
*
*  MuseZoom 0.9.7--with its accompanying files--works properly in NN4 and IE5 for Macintosh and (as
*  far as I know, but with less testing) Windows. Some functions work in NN6 for Mac. This development
*  version has a few unused variables, redundant calls, and brute-force functions yet to clean up.
*
*  Truncated revision history:
*  
*  Version 0.9 worked properly in certain core browser environments, with certain inefficiencies.
*  0.9.1 integrated into the main .js file what was previously a separate .js file to set metadata
*  variables; this was a means of trying to address a timing issue which would cause Netscape 4 for Mac
*  sometimes to call navigation functions with as yet undefined View attributes; for the same reason,
*  that and later versions force makeView() if nowView is undefined in zoomIn(). This looks redundant
*  with zoomIn()'s "preloaded" conditional, but nonetheless it seems to help N4 load the page.
*  0.9.2 fixed a filename concatenation error that had no visible effect but hit server with bad requests.
*  0.9.3 fixed an x-axis offset algorithm error in loadZoomIns(); in more attempts to work around N4
*  loading issues, it called setMeta() in the page's head, adjusted state-testing conditionals, and it
*  added function zoomCatcher() to the html page.
*  0.9.4 added function loadCatcher() to try to avoid a possible initialization timing issue.
*  0.9.5 fixed a newly found bug that broke newCenter() in IE5 if the page had been scrolled.
*  0.9.6 added detection and warning for non-Mac browsers pending two bug fixes for Windows.
*  0.9.7 added state testing to avoid calling undefined variables when preloading images, and
*  for N4/Windows it fixed (I hope!) newCenter() as well as a layout issue (in HTML and CSS).
*  0.9.8, if it comes to pass, will fix newCenter() in N6's DOM without breaking in IE5.
*
*  Script author: Rob Lancefield <rlancefield at wesleyan.edu>
*  Source code copyright (c) 2001 Davison Art Center, Wesleyan University
*  Made using BBEdit for the DAC site at http://www.wesleyan.edu/dac/home.html
*
*/


// INITIALIZATION -- (check through for all vars not used across functions and reduce their scope)

if (document.images) {

  var preloaded = false;  // this remains false until set to true at end of makeView();
  var metaloaded = false; // this remains false until set to true at end of setMeta();
  var close_p = "<\/p>";

  // set browser-detection variables for branching functions and for incompatibility warning

  var net4 = false;
  var net6 = false;
  var mie5 = false;
  var naVersion = navigator.appVersion;
  var v4up = (naVersion.indexOf("4.") != -1);
  var v5up = (naVersion.indexOf("5.") != -1);

  if (navigator.appName == "Netscape") { 
    net4 = ((v4up) ? true : false);
    net6 = ((v5up) ? true : false);
  }

  if ((navigator.appName == "Microsoft Internet Explorer") && v5up) {
    mie5  = true;
    var e = "";   // this prevents IE5 errors when calling newCenter() in its N6-compatible form as of v0.9.6 
  }
  
  var mac = false;
  if (naVersion.indexOf("Mac") >= 0) {
    mac = true;
  }

  var warn = (mac == false) || ((net4 == false) && (net6 == false) && (mie5 == false));

  // set variables for relative paths to directories housing image component files, navigation graphics, and metadata

  var fileBase = "../../imag/";  // top directory of image directories based on accession numbers
  var naviBase = "../navi/";     // directory housing all GIF images for navigation graphics
  var metaBase = "../meta/";     // not yet used; directory to house XML-tagged metadata files

  // preload navigation graphics

  document.navRightDead = new Image();     document.navRightDead.src     = naviBase + "dead_vertical.gif";
  document.navRightLive = new Image();     document.navRightLive.src     = naviBase + "right_live.gif";
  document.navRightPres = new Image();     document.navRightPres.src     = naviBase + "right_pressed.gif";
  document.navDownRightDead = new Image(); document.navDownRightDead.src = naviBase + "dead_corner.gif";
  document.navDownRightLive = new Image(); document.navDownRightLive.src = naviBase + "downright_live.gif";
  document.navDownRightPres = new Image(); document.navDownRightPres.src = naviBase + "downright_pressed.gif";
  document.navDownDead = new Image();      document.navDownDead.src      = naviBase + "dead_horizontal.gif";
  document.navDownLive = new Image();      document.navDownLive.src      = naviBase + "down_live.gif";
  document.navDownPres = new Image();      document.navDownPres.src      = naviBase + "down_pressed.gif";
  document.navDownLeftDead = new Image();  document.navDownLeftDead.src  = naviBase + "dead_corner.gif";
  document.navDownLeftLive = new Image();  document.navDownLeftLive.src  = naviBase + "downleft_live.gif";
  document.navDownLeftPres = new Image();  document.navDownLeftPres.src  = naviBase + "downleft_pressed.gif";
  document.navLeftDead = new Image();      document.navLeftDead.src      = naviBase + "dead_vertical.gif";
  document.navLeftLive = new Image();      document.navLeftLive.src      = naviBase + "left_live.gif";
  document.navLeftPres = new Image();      document.navLeftPres.src      = naviBase + "left_pressed.gif";
  document.navUpLeftDead = new Image();    document.navUpLeftDead.src    = naviBase + "dead_corner.gif";
  document.navUpLeftLive = new Image();    document.navUpLeftLive.src    = naviBase + "upleft_live.gif";
  document.navUpLeftPres = new Image();    document.navUpLeftPres.src    = naviBase + "upleft_pressed.gif";
  document.navUpDead = new Image();        document.navUpDead.src        = naviBase + "dead_horizontal.gif";
  document.navUpLive = new Image();        document.navUpLive.src        = naviBase + "up_live.gif";
  document.navUpPres = new Image();        document.navUpPres.src        = naviBase + "up_pressed.gif";
  document.navUpRightDead = new Image();   document.navUpRightDead.src   = naviBase + "dead_corner.gif";
  document.navUpRightLive = new Image();   document.navUpRightLive.src   = naviBase + "upright_live.gif";
  document.navUpRightPres = new Image();   document.navUpRightPres.src   = naviBase + "upright_pressed.gif";

  document.navPreviousLive = new Image();  document.navPreviousLive.src  = naviBase + "previous_live.gif";
  document.navPreviousPres = new Image();  document.navPreviousPres.src  = naviBase + "previous_pressed.gif";
  document.navPreviousGrey = new Image();  document.navPreviousGrey.src  = naviBase + "previous_greyed.gif";
  document.navZoomOutLive  = new Image();  document.navZoomOutLive.src   = naviBase + "zoom_out_live.gif";
  document.navZoomOutPres  = new Image();  document.navZoomOutPres.src   = naviBase + "zoom_out_pressed.gif";
  document.navZoomOutGrey  = new Image();  document.navZoomOutGrey.src   = naviBase + "zoom_out_greyed.gif";
  document.navFullViewLive = new Image();  document.navFullViewLive.src  = naviBase + "full_view_live.gif";
  document.navFullViewPres = new Image();  document.navFullViewPres.src  = naviBase + "full_view_pressed.gif";
  document.navFullViewGrey  = new Image(); document.navFullViewGrey.src  = naviBase + "full_view_greyed.gif";

  // create navigation image variables

  document.hotUpRight = new Image();
  document.hotRight = new Image();
  document.hotDownRight = new Image();
  document.hotDown = new Image();
  document.hotDownLeft = new Image();
  document.hotLeft = new Image();
  document.hotUpLeft = new Image();
  document.hotUp = new Image();

  document.hotPrevious = new Image();
  document.hotZoomOut = new Image();
  document.hotFullView = new Image();
  document.hotPrevious.src = document.hotZoomOut.src = document.hotFullView.src = naviBase + "blank_18x128.gif";

  imageUpRight1 = new Image();
  imageRight1 = new Image();
  imageDownRight1 = new Image();
  imageDown1 = new Image();
  imageDownLeft1 = new Image();
  imageLeft1 = new Image();
  imageUpLeft1 = new Image();
  imageUp1 = new Image();

  imageInUpRight1 = new Image();
  imageInDownRight1 = new Image();
  imageInDownLeft1 = new Image();
  imageInUpLeft1 = new Image();

  imageZoomOut1 = new Image();

  // initialize arrays for image-specific x and y jumps; these will hold numbers of overlapping x and y
  // segments at levels 3 through 5, derived from four square quadrants cut from each corner of level 2
  // for simplified (and thus more easily automatible) image production and consistent navigation code.

  var xJumps = new Array(true,0,0,0,0,1);
  var yJumps = new Array(true,0,0,0,0,1);

  // initialize array and array variables to hold view history

  var pastViews = new Array();
  pastViews[0] = new View();
  var undefined; // holds undefined value to undefine pastViews values for push/pop emulation

  var accessionNumber = "";
  var sourceWidth = "";
  var sourceHeight = "";
  var xMax = 5;
  var yMax = 5;
  
  View();

  var nowView = new View("",1,1,1,5,1,1,xJumps,yJumps);
  var goView  = new View("",1,1,1,5,1,1,xJumps,yJumps);

  // initialize other global variables

  var metaText = "hey!"; // in process

  var rightAble = false;
  var downRightAble = false;
  var downAble = false;
  var downLeftAble = false;
  var leftAble = false;
  var upLeftAble = false;
  var upAble = false;
  var upRightAble = false;

  var inLevel = "";
  var inRight = "";
  var inDown = "";
  var inLeft = "";
  var inUp = "";

  var nextRight = "";
  var nextDown = "";
  var nextLeft = "";
  var nextUp = "";

  var lOutNext = "";
  var xOutNext = "";
  var yOutNext = "";

  var base = "";
  var aspectRatio = "";
  var xClick = "";
  var yClick = "";
  var upEvent = "";

  var loadingZoomIns   = false;
  var loadingZoomOut   = false;
  var loadingAdjacents = false;

  // var buttonClock = "";

  var statusUpLeft = "";
  var statusUpRight = "";
  var statusDownLeft = "";
  var statusDownRight = "";

  var net6SmallMap = "";
  var net6MapClick = "";

  // set up Netscape 4 event handling for newCenter() calls from small image map

  if (net4) {
    document.captureEvents(Event.MOUSEUP);
    document.onmouseup = newCenter;
  }
}

// FUNCTIONS

// define View as a class of objects to be used for nowView, goView, and pastViews array

function View(base,level,x,y,lMax,xMax,yMax,xJumps,yJumps,sourceWidth,sourceHeight) {
  this.b = base;          // base as filepath from "../../" through accession number
  this.l = level;         // zoom level as integer
  this.x = x;             // x location as integer
  this.y = y;             // y location as integer
  this.lm = lMax;         // maximum level depth integer
  this.xm = xMax;         // maximum x at current level
  this.ym = yMax;         // maximum y at current level
  this.xj = xJumps;       // xJumps array
  this.yj = yJumps;       // yJumps array
  this.xs = sourceWidth;  // pixel width of original source image
  this.ys = sourceHeight; // pixel height of original source image
}

// eventually this could read and transform xml-tagged metadata; for now, it's hardcoded
// with item-specific string and integer content in this file. In v0.9b, this function was
// in a separate file; moved here to try to fix intermittent timing issues when N4/Mac loads.

function setMeta() {
  if (document.images) {

    /*  tagged data as currently in xml file (not yet actually pulled into this script):
      <object_metadata>
        <object_accession_number>1949.D3.1</object_accession_number>
        <object_agent type="creator">Albrecht D&uuml;rer (German, 1471-1528)</object_agent>
        <object_title>Knight, Death and Devil</object_title>
        <object_year>1513</object_year>
        <object_medium>Engraving</object_medium>
        <object_support>Laid paper</object_support>
        <object_plate_height unit="mm">243</object_plate_height>
        <object_plate_width unit="mm">186</object_plate_width>
        <object_inscription>Signed in plate: monogram in tablet, lower left</object_inscription>
        <object_references>Bartsch 98; Dodgson 70; Meder 74b; Panofsky 205; Strauss 71</object_references>
        <object_credit_line>Gift of George W. Davison (B.A. Wesleyan 1892), 1949</object_credit_line>
      </object_metadata>
      <image_metadata>
        <source_height_px>4580</source_height_px>
        <source_width_px>3575</source_width_px>
      </image_metadata>
    */
    
    // later build below into View objects as nowView.meta.creator etc. or concatenate as nowView.metaText?

    sourceHeight = 4580; sourceWidth = 3575;  // pixel dimensions of master source image;
    accessionNumber = '1949.D3.1';            // these first 3 vars are global, declared above

    var close_para = "<\/p>"; // local close_p works around timing issue of global var close_p sometimes still undefined
    var close_span = "<\/span>";
    
    var creator = 'Albrecht D&uuml;rer (German, 1471-1528)';  // pre-processing note: HTML entity versus database high-ASCII
    var title = 'Knight, Death and Devil'; 
    var medium = 'Engraving'; 
    var support = 'laid paper';                               // pre-processing note: char 0 dropped from database uppercase
    var watermark = '';
    var year = '1513';
    var plateHeight = '243'; 
    var plateWidth = '186'; 
    var inscription = 'Signed in plate with monogram, lower left'; 
    var references = 'Bartsch 98; Meder 74b; Strauss 71'; 
    var creditLine = 'Gift of George W. Davison (B.A. Wesleyan 1892), 1949';

    metaText = '<p class="mainmeta">' + '<span class="creator">' + creator + ": " + close_span + '<span class="title">' + title + close_span + close_para;
    
    metaText += '<p class="submeta">' + medium;
    if (support) {
        metaText += ' on ' + support;
    }
    metaText += ", " + year + ". ";
    if (watermark) {
      metaText += 'Watermark: ' + watermark + ". ";
    }    
    if (plateHeight && plateWidth) {
      metaText += 'Plate ' + plateHeight + " x "+ plateWidth + " mm. " ;
    }    
    if (inscription) {
      metaText += inscription + ". ";
    } 
    if (references) {
      metaText += 'References: ' + references + ". ";
    }
    if (creditLine) {
      metaText += creditLine + ". ";
    }
    metaText += 'DAC ' + accessionNumber + "." + close_para;
    
    metaloaded = true;
    return metaText;
  }
}

// initialize current View with image-specific values in accessionNumber, sourceWidth, sourceHeight

function makeView() {
  if (document.images && metaloaded) {
    nowView.xs = sourceWidth;
    nowView.ys = sourceHeight;
    aspectRatio = nowView.ys/nowView.xs;

    var mainPath = fileBase;
    var components = accessionNumber.split(".");

    for (var c = 0; c < components.length; c++) {
      mainPath += pad4(components[c]) + "/";
    }

    mainPath += accessionNumber.replace(/\./g,"-") + "_l1x01y01.jpg";
    nowView.b = mainPath.substr(0,(mainPath.lastIndexOf("l")));
    pastViews[0].l = nowView.l = Math.abs(mainPath.substr((mainPath.lastIndexOf("l")+1),1));
    pastViews[0].x = nowView.x = Math.abs(mainPath.substr((mainPath.lastIndexOf("x")+1),2));
    pastViews[0].y = nowView.y = Math.abs(mainPath.substr((mainPath.lastIndexOf("y")+1),2));

    if ((aspectRatio > 1.1) && (aspectRatio < 1.4)) {  // aspect ratio of KDD test image is 1.28; the
      for (var l = 3; l <= nowView.lm; l++) {          // arbitrary range here of 1.1-1.4 is entirely a
        nowView.xj[l] = Math.floor(Math.pow(2,(l-3))); // guess that sets KDD's xj3=1, xj4=2, xj5=4, but
        nowView.yj[l] = Math.floor(Math.pow(2,(l-5))); // it will need tweaking to handle general cases
      }
    }

    setStatus(nowView);
    preloaded = true;
  }
}

// this N6 stuff doesn't work yet called as a function here or read in on load above  -- fix to handle DOM and events

function setN6() {
  if (net6) {
    net6SmallMap = document.getElementById("smallMap1");
    net6MapClick = net6SmallMap.addEventListener("click", newCenter, true);
  }
}

// pad and trim accession number component values to zero-padded 4-digit strings for directory names in image file path

function pad4(component) {
  var padLong = "000" + component;
  return padLong.substr(padLong.length-4,4);
}

// pad and trim x and y values (stored as integers in View properties) to zero-padded 2-digit strings for image file names

function pad2(coordinate) {
  var padLong = "0" + coordinate;
  return padLong.substr(padLong.length-2,2);
}

// set global variables for limits of movement from a location, assuming square grids from corners from level 2 down

function goAbles() {
  nowView.xm = nowView.ym = Math.pow(2,(nowView.l-1));

  rightAble = (nowView.x < nowView.xm);
  downAble  = (nowView.y < nowView.ym);
  leftAble  = (nowView.x > 1);
  upAble    = (nowView.y > 1);
  
  upRightAble   = (upAble   && rightAble);
  downRightAble = (downAble && rightAble);
  upLeftAble    = (upAble   && leftAble);
  downLeftAble  = (downAble && leftAble);
}

// set status bar text based on mouse position over main view to explain effect of clicking at that location

function setStatus() {
  if (nowView.l < nowView.lm) {
    statusUpLeft    = "Zoom in to upper left quadrant";
    statusUpRight   = "Zoom in to upper right quadrant";
    statusDownLeft  = "Zoom in to lower left quadrant";
    statusDownRight = "Zoom in to lower right quadrant";
  }
  else if (nowView.l == nowView.lm) {
    statusUpLeft = statusUpRight = statusDownLeft = statusDownRight = "This view is at the highest resolution; it is impossible to zoom in further.";
  }
}

// if navigation buttons and small map are useable, display them; otherwise hide or grey out as interface cues

function setNavigation() {
  if (pastViews.length > 1) {
    document.images['prevView'].src = document.navPreviousLive.src;
  }
  else {
    document.images['prevView'].src = document.navPreviousGrey.src;
  }
  if (nowView.l > 1) {
    document.images['zoomOut'].src   = document.navZoomOutLive.src;
    document.images['fullView'].src  = document.navFullViewLive.src;
    document.images['smallMapImageOne'].src = nowView.b + "smallmap.jpg";
    document.images['mapNote'].src   = naviBase + "map_note_live.gif";
  }
  else {
    document.images['zoomOut'].src   = document.navZoomOutGrey.src;
    document.images['fullView'].src  = document.navFullViewGrey.src;
    document.images['smallMapImageOne'].src = naviBase + "zoom_note.gif";
    document.images['mapNote'].src   = naviBase + "blank_36x128.gif";
  }
}

// set current framing navigation bars based on possible directions of movement; a more
// concise refactoring with loop and arrays might crash into N4 cache bug, so keep as is.

function setBars(nowView) {
  if (preloaded) {
    if (rightAble) {
      document.images['navRight'].src = document.navRightLive.src;
      document.hotRight.src = document.navRightPres.src;
    }
    else {
      document.images['navRight'].src = document.navRightDead.src;
      document.hotRight.src = document.navRightDead.src;
    }
    if (downRightAble) {
      document.images['navDownRight'].src = document.navDownRightLive.src;
      document.hotDownRight.src = document.navDownRightPres.src;
    }
    else {
      document.images['navDownRight'].src = document.navDownRightDead.src;
      document.hotDownRight.src = document.navDownRightDead.src;
    }
    if (downAble) {
      document.images['navDown'].src = document.navDownLive.src;
      document.hotDown.src = document.navDownPres.src;
    }
    else {
      document.images['navDown'].src = document.navDownDead.src;
      document.hotDown.src = document.navDownDead.src;
    }
    if (downLeftAble) {
      document.images['navDownLeft'].src = document.navDownLeftLive.src;
      document.hotDownLeft.src = document.navDownLeftPres.src;
    }
    else {
      document.images['navDownLeft'].src = document.navDownLeftDead.src;
      document.hotDownLeft.src = document.navDownLeftDead.src;
    }
    if (leftAble) {
      document.images['navLeft'].src = document.navLeftLive.src;
      document.hotLeft.src = document.navLeftPres.src;
    }
    else {
      document.images['navLeft'].src = document.navLeftDead.src;
      document.hotLeft.src = document.navLeftDead.src;
    }
    if (upLeftAble) {
      document.images['navUpLeft'].src = document.navUpLeftLive.src;
      document.hotUpLeft.src = document.navUpLeftPres.src;
    }
    else {
      document.images['navUpLeft'].src = document.navUpLeftDead.src;
      document.hotUpLeft.src = document.navUpLeftDead.src;
    }
    if (upAble) {
      document.images['navUp'].src = document.navUpLive.src;
      document.hotUp.src = document.navUpPres.src;
    }
    else {
      document.images['navUp'].src = document.navUpDead.src;
      document.hotUp.src = document.navUpDead.src;
    }
    if (upRightAble) {
      document.images['navUpRight'].src = document.navUpRightLive.src;
      document.hotUpRight.src = document.navUpRightPres.src;
    }
    else {
      document.images['navUpRight'].src = document.navUpRightDead.src;
      document.hotUpRight.src = document.navUpRightDead.src;
    }
  }
}

// swap in depressed navigation bar image on mouseDown for user feedback while new view is loading

function hotPress(action) {
  switch (action) {
    case "previous":
      document.navPrevious.src = document.hotPrevious.src;
      break;
    case "zoomout":
      document.navZoomOut.src = document.hotZoomOut.src;
      break;
    case "fullview":
      document.navFullView.src = document.hotFullView.src;
      break;
    case "upright":
      document.navUpRight.src = document.hotUpRight.src;
      break;
    case "right":
      document.navRight.src = document.hotRight.src;
      break;
    case "downright":
      document.navDownRight.src = document.hotDownRight.src;
      break;
    case "down":
      document.navDown.src = document.hotDown.src;
      break;
    case "downleft":
      document.navDownLeft.src = document.hotDownLeft.src;
      break;
    case "left":
      document.navLeft.src = document.hotLeft.src;
      break;
    case "upleft":
      document.navUpLeft.src = document.hotUpLeft.src;
      break;
    case "up":
      document.navUp.src = document.hotUp.src;
      break;
  }
}

// master function to preload images for three most likely types of possible movement from new location

function loadNexts(caller) {  // later use 'caller' for predictive algorithm determining sequence of calls to subfunctions?
  if (document.images) {

    if (nowView.l < nowView.lm) {
      loadingZoomIns   = true;
      loadZoomIns();
    }

    if (nowView.l > 1) {
      loadingZoomOut   = true;
      loadingAdjacents = true;
      loadZoomOut();
      loadAdjacents();
    }
  }
}

// determine, set, and preload images for zoomIn

function loadZoomIns() {
  var inLevel = nowView.l+1;
  
  inRight = nowView.x*2;
  inLeft  = inRight-1;
  inDown  = nowView.y*2;
  inUp    = inDown-1;
  
  xMax = yMax = Math.pow(2,nowView.l);

  if ((nowView.xj[inLevel] == 1) && (inLeft == (xMax/2 + 1))) {  // This redirects KDD zoomIns from l2x02y01-04 via upLeft and downLeft
    inLeft -= nowView.xj[inLevel];                               // to l3x02y01-04 rather than default l3x03y01-04 and from l3x02y01-04 via
  }                                                              // upRight and downRight to l4x06y01-04 rather than default l4x04y01-04,
  if ((nowView.xj[inLevel] == 2) && (inRight == (xMax/2))) {     // but won't necessarily handle general cases; tweak when have more images.
    inRight += nowView.xj[inLevel];
  }

/*
  if ((nowView.yj[inLevel] == 1) && (inUp == (yMax/2 + 1))) {    // this y-axis version was blindly transformed by analogy from the x-axis
    inUp -= nowView.yj[inLevel];                                 // version (see comment above) that works for KDD; check with a horizontal
  }                                                              // image to see if it works, and wrap both in conditionals based on aspectRatio
  if ((nowView.yj[inLevel] == 2) && (inDown == (yMax/2))) {      // to skip when not needed. Possibly abstract shared jump-handling parts of
    inDown += nowView.yj[inLevel];                               // goAdjacent, zoomIn, and zoomOut when general cases for all settle down.
  }
*/

  var inBase = nowView.b + "l" + inLevel;

  var padRight = "x" + pad2(inRight);
  var padLeft  = "x" + pad2(inLeft);
  var padDown  = "y" + pad2(inDown);
  var padUp    = "y" + pad2(inUp);

  imageInUpRight1.src   = inBase + padRight + padUp   + ".jpg";
  imageInDownRight1.src = inBase + padRight + padDown + ".jpg";
  imageInDownLeft1.src  = inBase + padLeft  + padDown + ".jpg";
  imageInUpLeft1.src    = inBase + padLeft  + padUp   + ".jpg";

  loadingZoomIns = false;
}

// determine, set, and preload image for zoomOut

function loadZoomOut() {
  lOutNext = nowView.l - 1;
  xOutNext = Math.ceil(nowView.x/2);
  yOutNext = Math.ceil(nowView.y/2);

  // this redirects KDD zoomOuts from l4x06y01-08 to l3x02y01-04 (from default l3x03y01-04),
  // but won't work as is for all cases--tweak and add y version when have more sample images;
  // abstract shared jump-handling parts of goAdjacent, zoomIn, and zoomOut when settle down.

  xMax = yMax = Math.pow(2,(lOutNext-1));
  if ((nowView.xj[lOutNext] == 1) && (xOutNext == (xMax/2 + 1))) {
    xOutNext -= nowView.xj[lOutNext];
  }

  imageZoomOut1.src = nowView.b + "l" + lOutNext + "x" + pad2(xOutNext) + "y" + pad2(yOutNext) + ".jpg";

  loadingZoomOut = false;
}

// determine possible destinations and preload images for goAdjacent

function loadAdjacents() {

  // set variables for jumps from current location to allow square quadrants from corners in image production
  var xOffsetRight = 0;
  var yOffsetDown = 0;
  var xOffsetLeft = 0;
  var yOffsetUp = 0;

  if ((nowView.xj[0] == true) && (nowView.x == (Math.pow(2,(nowView.l-1))/2 - Math.floor(nowView.xj[nowView.l]/2)))) {
    xOffsetRight = nowView.xj[nowView.l];
  }
  if ((nowView.yj[0] == true) && (nowView.y == (Math.pow(2,(nowView.l-1))/2 - Math.floor(nowView.yj[nowView.l]/2)))) {
    yOffsetDown = nowView.yj[nowView.l];
  }
  if ((nowView.xj[0] == true) && (nowView.x == (Math.pow(2,(nowView.l-1))/2 + Math.floor(nowView.xj[nowView.l]/2)) + 1)) {
    xOffsetLeft = nowView.xj[nowView.l];
  }
  if ((nowView.yj[0] == true) && (nowView.y == (Math.pow(2,(nowView.l-1))/2 + Math.floor(nowView.yj[nowView.l]/2)) + 1)) {
    yOffsetUp = nowView.yj[nowView.l];
  }

  // set possible destination components including any offsets; may exceed image limits, but movement is constrained later
  nextRight = nowView.x + xOffsetRight + 1;
  nextDown  = nowView.y + yOffsetDown + 1;
  nextLeft  = nowView.x - xOffsetLeft - 1;
  nextUp    = nowView.y - yOffsetUp - 1;

  var padRight = "x" + pad2(nextRight);
  var padDown  = "y" + pad2(padDown);
  var padLeft  = "x" + pad2(padLeft);
  var padUp    = "y" + pad2(padUp);

  var thisBase = nowView.b + "l" + nowView.l;
  var thisX    = "x" + pad2(nowView.x);
  var thisY    = "y" + pad2(nowView.y);

  // preload images for goAdjacent; although conditional logic is the same as for framing navigation bar loading
  // above, keep separate so navigation gifs all load without waiting for far heavier (off-screen) JPEG preloads
  
  if (rightAble) {
    imageRight1.src     = thisBase + padRight + thisY   + ".jpg";
  } 
  if (downRightAble) {
    imageDownRight1.src = thisBase + padRight + padDown + ".jpg";
  }
  if (downAble) {
    imageDown1.src      = thisBase + thisX    + padDown + ".jpg";
  }
  if (downLeftAble) {
    imageDownLeft1.src  = thisBase + padLeft  + padDown + ".jpg";
  }
  if (leftAble) {
    imageLeft1.src      = thisBase + padLeft  + thisY   + ".jpg";
  } 
  if (upLeftAble) {
    imageUpLeft1.src    = thisBase + padLeft  + padUp   + ".jpg";
  }
  if (upAble) {
    imageUp1.src        = thisBase + thisX    + padUp   + ".jpg";
  }
  if (upRightAble) {
    imageUpRight1.src   = thisBase + padRight + padUp   + ".jpg";
  }
  loadingAdjacents = false;
}

// execute the move to a new view

function goNext(caller) {
  if (mie5 && caller == "zoomin") {
    document.viewImage1.blur();
  }
  if (net6 && caller == "zoomin") {
    document.anchors["smallMap1"].blur();  // document.anchors["viewImage1"].blur(); seems more sensible, but N6 returns "no properties" error
  }
  document.images["viewImage1"].src = goView.b + "l" + goView.l + "x" + pad2(goView.x) + "y" + pad2(goView.y) + ".jpg";
  nowView = goView;
  setStatus();
  goAbles(nowView);
  setBars(nowView);
  setNavigation();
  if (caller != "lastview") {
    pastViews[pastViews.length] = new View;
    pastViews[pastViews.length-1].l = nowView.l;
    pastViews[pastViews.length-1].x = nowView.x;
    pastViews[pastViews.length-1].y = nowView.y;
    document.images['prevView'].src = document.navPreviousLive.src;
  }
  loadNexts(caller);
}

// go back to the previously displayed view

function lastView() {
  if (preloaded && pastViews.length > 1) {
    goView = nowView;
    goView.l = pastViews[pastViews.length-2].l;
    goView.x = pastViews[pastViews.length-2].x;
    goView.y = pastViews[pastViews.length-2].y;
    // goView = pastViews[pastViews.length-2];  would be more compact, but doesn't work -- how to pass entire object from array?
    pastViews.length = pastViews.length-1;
    goNext("lastview");
  }
}

// zoom in to deeper-level view representing one quadrant of current view

function zoomIn(zoomQuadrant) {
  if (preloaded && nowView.l < nowView.lm) {  // && loadingZoomIns == false
    if (nowView.b == null || nowView.l == null || nowView.x == null || nowView.y == null) {
      makeView();
    }
    loadNexts(nowView);  // N6 needs nowView passed to loadNexts() -- why?
    goView = nowView;
    goView.l += 1;
    switch (zoomQuadrant) {
      case "upright":
        goView.x = inRight;
        goView.y = inUp;
        break;
      case "downright":
        goView.x = inRight;
        goView.y = inDown;
        break;
      case "downleft":
        goView.x = inLeft;
        goView.y = inDown;
        break;
      case "upleft":
        goView.x = inLeft;
        goView.y = inUp;
        break;
    }
    goNext("zoomin");

  }
}

// go to shallower zoom-level view encompassing current view -- currently disregards jumps, so tweak to accomodate them.

function zoomOut() {
  if (preloaded && nowView.l > 1 && loadingZoomOut == false) {
    goView = nowView;
    goView.l -= 1;
    goView.x = xOutNext;
    goView.y = yOutNext;
    goNext("zoomout");
  }
}

// go to full view of object

function fullView() {
  if (preloaded && nowView.l > 1) {
    goView = nowView;
    goView.l = 1;
    goView.x = 1;
    goView.y = 1;
    goNext("fullview");
  }
}

// go to adjacent detail view at current level -- called from framing buttons for adjacent zones if exist

function goAdjacent(goDirection) {
  if (preloaded && nowView.l > 1) {   // third condition of (loadingAdjacents == false) made any adjacent move wait for up to 8 images to load

    hotPress(goDirection);
    loadNexts(nowView);

    // buttonClock = window.setTimeout("setBars(nowView)",6000);

    goAbles(nowView);
    goView.l = nowView.l;

    if ((goDirection == "right") && rightAble) {
      goView.x = nextRight;
      goView.y = nowView.y;
    }
    if ((goDirection == "downright") && downRightAble) {
      goView.x = nextRight;
      goView.y = nextDown;
    }
    if ((goDirection == "down") && downAble) {
      goView.x = nowView.x;
      goView.y = nextDown;
    }
    if ((goDirection == "downleft") && downLeftAble) {
      goView.x = nextLeft;
      goView.y = nextDown;
    }
    if ((goDirection == "left") && leftAble) {
      goView.x = nextLeft;
      goView.y = nowView.y;
    }
    if ((goDirection == "upleft") && upLeftAble) {
      goView.x = nextLeft;
      goView.y = nextUp;
    }
    if ((goDirection == "up") && upAble) {
      goView.x = nowView.x;
      goView.y = nextUp;
    }
    if ((goDirection == "upright") && upRightAble) {
      goView.x = nextRight;
      goView.y = nextUp;
    }
    goNext("goadjacent");
  }
}

// set x and y click coordinate variables for Explorer 5 for newCenter()
function setClick() {
  if (mie5) {
    xClick = window.event.offsetX + document.body.scrollLeft;
    yClick = window.event.offsetY + document.body.scrollTop;
  }
}

// go to another detail at current zoom level; eventually overlay red square on map for current location?

function newCenter(evnt) {

  var goodX = false;
  var goodY = false;

  if (net4) {
    xClick = evnt.pageX - document.smallMapImageOne.x;
    yClick = evnt.pageY - document.smallMapImageOne.y;
    goodX = ((xClick >= 1) && (xClick <= 128));
    goodY = ((yClick >= 1) && (yClick <= 128));
  }

  if (net6) { // newCenter() doesn't work yet in N6; for now, warn and and set vars so it won't try

    alert("This map doesn't work yet with Netscape 6;\nall other navigation operates as it should.");

/*
    goodX = goodY = false;

    xClick = e.clientX - parseInt(net6SmallMap.style.left);
    yClick = e.clientY - parseInt(net6SmallMap.style.top);

    goodX = ((xClick >= 1) && (xClick <= 128));
    goodY = ((yClick >= 1) && (yClick <= 128));

    // parseInt(document.getElementById('smallMapImage1').style.left approach didn't seem to work either
*/
  }

  if (mie5) {
    goodX = goodY = true;
   }

  if (goodX && goodY && nowView.l > 1) {

    goAbles(nowView);

    // ignore clicks in white padding areas, if any, that make thumbnail image square
    if (nowView.xs != nowView.ys) {
      var blank = Math.floor(64*(nowView.ys - nowView.xs)/nowView.ys);
      var xBlank = ((blank > 0) ? blank : 0 );
      var yBlank = ((blank < 0) ? blank : 0 );
    }

    if (((xClick > xBlank) && (xClick + xBlank) <= 128) && ((yClick > yBlank) && (yClick + yBlank) <= 128)) {

      // having ignored clicks in thumbnail margins, scale click coordinates as if live area of image = 128 x 128 pixels
      if (xBlank != 0) {
        xClick = Math.floor((xClick - xBlank) * (128/(128-(xBlank*2))));
      }
      if (yBlank != 0) {
        yClick = Math.floor((yClick - yBlank) * (128/(128-(yBlank*2))));
      }

      // account for nonexistent locations at current level
      var xGrid = nowView.xm - nowView.xj[nowView.l];
      var yGrid = nowView.ym - nowView.yj[nowView.l];

      // set horizontal and vertical positions in current level's grid
      var xNew = Math.ceil(xClick*xGrid/128);
      var yNew = Math.ceil(yClick*yGrid/128);

      if ((nowView.xj[nowView.l] > 0) && (xNew > xGrid/2)) {
        xNew += nowView.xj[nowView.l];
      }
      if ((nowView.yj[nowView.l] > 0) && (yNew > yGrid/2)) {
        yNew += nowView.yj[nowView.l];
      }

      goView.b = nowView.b;
      goView.l = nowView.l;
      goView.x = xNew;
      goView.y = yNew;

      goNext("newcenter");

      if (net4) {
        return false;  // this writes the text "false" to IE5.50.4522.1800 (and probably other IE5.X?) for Windows if executed
      }
    }
  }
}

