a53b9958fa734f73aeffb9ddfe2fbad1ca65f90c
galt
  Mon Jan 30 16:18:41 2017 -0800
Check-in of CSP2 Content-Security-Policy work. All C-language CGIs should now support CSP2 in browser to stop major forms of XSS javascript injection. Javascript on pages is gathered together, and then emitted in a single script block at the end with a nonce that tells the browser, this is js that we generated instead of being injected by a hacker. Both inline script from script blocks and inline js event handlers had to be pulled out and separated. You will not see js sprinkled through-out the page now. Older browsers that support CSP1 or that do not understand CSP at all will still work, just without protection. External js libraries loaded at runtime need to be added to the CSP policy header in src/lib/htmshell.c.

diff --git src/hg/htdocs/bigImage.html src/hg/htdocs/bigImage.html
index f1cd80c..ea2c195 100755
--- src/hg/htdocs/bigImage.html
+++ src/hg/htdocs/bigImage.html
@@ -1,1106 +1,1106 @@
 <html>
 <head>
 
 <title>visiGene bigImage</title>
 
 <style>
 
 table  { border-collapse: collapse; border-spacing: 0; }
 tr, th, td { padding: 0px; }
 
 </style>
 
 </head>
 
 
 
 <SCRIPT LANGUAGE="javascript" TYPE="text/javascript">
 
 function showBrowser() {
     window.status=navigator.appName+' '+navigator.appVersion;
 }
 
 var isSafari = (navigator.userAgent.toLowerCase().indexOf("safari") >= 0);
 
 // magnification for over-zoom-in.  as if there were a level -1.
 m = 1;
 maxmag = 8;  // max magnification  (doubles each time: 1,2,4,8)
 
 // number of zoom levels, level 0 = no zoom, numLevels-1=max zoom out=6
 numLevels=7;
 
 myClip = document.getElementById("myClip");
 
 myImg = document.getElementById("myImg");
 
 myCover = document.getElementById("myCover");
 
 
 // original width and height of picture
 wOrig = new Array(numLevels);
 hOrig = new Array(numLevels);
 wOrig[0] = 7248;
 hOrig[0] = 3780;
 
 // size of subImages (tiles)
 wSub = 512; //240; //300;
 hSub = 512; //240; //300;
 
 // width of last column of tiles (since we are scaling tiles by setting width)
 wSubEdge = new Array(numLevels);
 wSubEdge[0] = wSub;  
 hSubEdge = new Array(numLevels);  // safari
 hSubEdge[0] = hSub;  
 
 // number of extra cell rows and columns to cover in view window myClip
 wdl = wdr = 2;  
 hdl = hdr = 1;
 
 picName="T12832210aa";
 picExt =".jpg";
 
 // number of rows and columns in table
 wCnt = new Array(numLevels);
 hCnt = new Array(numLevels);
 
 z = 1;  // accumulated zoom factor at level 0
 wSmallDelta = hSmallDelta =  50;
 hSmallDelta = hSmallDelta =  50;
 wBigDelta   = hBigDelta   = 300;
 hBigDelta   = hBigDelta   = 300;
 dr = 5;  // amount to scroll
 
 marginLeft = 5;
 marginTop = 5;
 
 // clip size (changes with window resize)
 wc = 800;
 hc = 600;
 
 // minimum number of tiles required to cover view area wcXhc
 wt = Math.ceil(wc/wSub) + 1;
 ht = Math.ceil(hc/hSub) + 1;
 
 
 
 // position in original picture space, not scaled by zoom
 x = 0;   
 y = 0; 
 
 // target coords (scrolls so that x,y approaches goal x,y
 xx = 0;
 yy = 0;
 
 // mouse position
 mousex = 0;
 mousey = 0;
 mouseDrag = false;
 mouseAction = false;
 mouseDragX = mousex;
 mouseDragY = mousey;
 
 // hash of visible IMGs in use
 vis = new Object;
 
 // stack of invisible IMGs heap available for use
 invistop=-1;
 invis=new Array();
 
 // cells are pointers to the IMG objects (we can walk through entire list)
 cells = new Array();
 
 var qsParm = new Array();
 
 //function to retrieve and parse parms:
 function qs() {
 var query = window.location.search.substring(1);
 var parms = query.split('&');
 for (var i=0; i<parms.length; i++) {
    var pos = parms[i].indexOf('=');
    if (pos > 0) {
       var key = parms[i].substring(0,pos);
       var val = parms[i].substring(pos+1);
       qsParm[key] = val;
       }
    }
 }
 //assign default values and read parms in
 //e.g. qsParm['url'] = "junk.jpg";
 qs();  // init parm array from cmdline
 
 
 function placeTile(cell) {
 
 var wSize = wSub;
 var hSize = hSub;
 if ((cell.r != -1) && (cell.c != -1)) {
     
     if (cell.c+1==wCnt[l])
 	{
 	wSize = wSubEdge[l];
 	}
     if (cell.r+1==hCnt[l])
 	{
 	hSize = hSubEdge[l];
 	}
     // safari seems to need both width and height specified.
     // I wanted to use the style so I could express both properties at once,
     // but it doesn't seem to work when we magnify it (m > 1)
     cell.width  = wSize*m;
     cell.height = hSize*m;  //safari
     //cell.style='width:'+wSize*m+';'+'height:'+hSize*m+';';
     //'width:100%; height: auto;';
     
     cell.style.top  = cell.r*hSub*m;
     cell.style.left = cell.c*wSub*m;
     	    
 //alert('onTileLoad: cell.r,cell.c='+cell.r+','+cell.c+'  m='+m+' wCnt[l]='+wCnt[l]+' cell.width='+cell.width);
 }		
 }
 
 function onTileLoad(theImage) {  
 // does this work in all browsers we need to cover?
 // it seems to get around the FireFox bug where initial screen refreshes
 // were happening in wrong order leaving screen dirt.
 if (!theImage) var theImage = window.event;
 //alert('this='+this);
 //alert('theImage='+theImage);
 //var cell = document.getElementById(e.target);
 var cell = this;
 placeTile(cell);
 }
 
 
 function makeLevel(doHTML) { // make table of tiles large enough to cover view area wc,wh 
 
 var html = '';
 var r=0;
 var c=0;
 var cnt=0;
 
 if (doHTML) {  // since this may be expensive for old machines, skip if just a zoom rather than resize
     html = '';
     for(r=0; r<ht; r++) {
 	for(c=0; c<wt; c++) {
 	    html += '<IMG ID="myImg_'+cnt+'" src="images/dot_clear.gif" STYLE="position:absolute;left:-10000px;top:-10000px;">';
 	    cnt++;
 	}
     }   
     myImg.innerHTML=html;  // big initialization
 }
 
 myImg.style.width  = wOrig[l]*m;
 myImg.style.height = hOrig[l]*m;
 
 // hash of visible IMGs in use
 vis = new Object;
 // stack of invisible IMGs heap available for use
 invistop=-1;
 invis=new Array();
 // cells are pointers to the IMG objects (we can walk through entire list)
 cells = new Array();
 
 cnt=0;
 
 for(r=0; r<ht; r++) {
     for(c=0; c<wt; c++) {
 	var cell = document.getElementById("myImg_"+cnt);
 	var cstr = "images/dot_clear.gif";
 	cell.style.top  = -hc-hSub*m; // move it off screen to hide  -hc
 	cell.style.left = -wc-wSub*m; // -wc
 	cell.r = -1;  // used to identify current position role
 	cell.c = -1;
 	cells[cells.length] = cell;  // save an array as a list of all IMGs
 	invis[invis.length] = cell;  // list of all invisible IMGs avail for reuse - all cells start out available
 	cell.onload = onTileLoad;
 	cell.path=cstr;    // if the property does not exist it will be created
 	cell.src=cstr; 
 	cnt++;
     }
 }
 invistop=invis.length-1;  // point to top actual element
 
 // probably skip this as i would have to do all r,c in hCnt,wCnt to be worth using
 //theStatus[somevar] = null;   // just initialize so that it will at least exist on startup.
 
 }
 
 
 function scrollMe() {
 var m = 0;
 var dist = 0;
 var dx = 0;
 var dy = 0;
 var drz = Math.round(dr/z);  // scale up to larger motions in absolute x,y on smaller image
 
 if (typeof(y) != "number") {
     alert('typeof(y) != "number": type='+typeof(y)+' value='+y);	
 }
 
 if ((x != xx) || (y != yy)) {
     dist = Math.sqrt((yy-y)*(yy-y)+(xx-x)*(xx-x));
     if (dist < drz) {
 	x = xx;
       	y = yy;
     } else {
 	if (xx==x) {
 	    dx = 0;
 	    dy = drz;
     	    if (yy<y) dy *= -1;
 	} else {
 	    m = (yy-y)/(xx-x);
     	    dx = Math.sqrt((drz*drz)/(m*m+1));
     	    if (xx<x) dx *= -1;
 	    dy = m * dx;
 	}
 	x += dx;
 	y += dy;
     }
     updatePos();
 }
 }
 
 
 function updatePos() {
 
 var c = Math.floor(x*z/(hSub*m));
 var r = Math.floor(y*z/(wSub*m));
 var rr = 0;
 var cc = 0;
 var cell = null;
 var cstr = "";
 var i = 0;
 var cntinv = 0;
 var cntvis = 0;
 
 wdl = Math.ceil((wc/2-(x*z)%(wSub*m))/(wSub*m)); if(wdl<0)wdl=0;
 hdl = Math.ceil((hc/2-(y*z)%(hSub*m))/(hSub*m)); if(hdl<0)hdl=0;
 
 wdr = Math.ceil((wc/2-wSub+(x*z)%(wSub*m))/(wSub*m)); if(wdr<0)wdr=0;
 hdr = Math.ceil((hc/2-hSub+(y*z)%(hSub*m))/(hSub*m)); if(hdr<0)hdr=0;
 
 // pre-fetch over-border adjustment:
 //wdl++;hdl++;wdr++;hdr++;
 
 //alert('x,y='+x+','+y+'  c,r='+c+' '+r); //debug
 //alert('wc,hc='+wc+','+hc+'  wdl,wdr,hdl,hdr='+wdl+' '+wdr+','+hdl+' '+hdr); //debug
 
 
 // for each cell add to invisible stack (an array) if not visible (and remove from visible hash?)
 // else add to visible hash (as an object "property") (if not already present).
 
 //debug
 //alert("cells.length="+cells.length+"  invistop="+invistop);
 
 for(i=0;i<cells.length;i++) {
     var cell = cells[i];
     if ((cell.r != -1) && // just ignore -1 it is already invisible
     (
     (cell.r<r-hdl) || 
     (cell.c<c-wdl) ||
     (cell.r>r+hdr) || 
     (cell.c>c+wdr)
     )
     ) {  // it was visible, but now becomes invisible
 	invistop++;              // point to new top element position
 	invis[invistop] = cell;  // add to list of invisible IMGs avail for reuse
 	vis[""+cell.r+"_"+cell.c] = null;     // erase id from hash of visible cell.ids
 	cstr = "images/dot_clear.gif";
 	cell.path=cstr;
 	//cell.src=cstr; // causes IE flashes, don't even need it so skip it
 	cell.r = -1;
 	cell.c = -1;
 	cell.style.top  = -hc-hSub*m; // move it off screen to hide
 	cell.style.left = -wc-wSub*m;
 	cntinv++;  // debug
     }
 }
 
 //alert("new invistop="+invistop);
 
 // Then, loop again, for each position for wCnt*hCnt,
 // if it is in visible hash, do not update.  Else draw
 // from top of invisible stack and assign new position,.src.
 // Anything remaining on the stack is truly not visible,
 // move it to -wSub,-hSub to hide it.
 
 for(rr=r-hdl;rr-r<=hdr;rr++) {
     for(cc=c-wdl;cc-c<=wdr;cc++) {
 	if ((rr>=0) && (rr<hCnt[l]) && (cc>=0) && (cc<wCnt[l])) {
 	    cell = vis[""+rr+"_"+cc];
 	    if (!cell) {  
 		//-- if it is not present, recycle from invisible list, add to visible list, set properties
 	        if (invistop < 0) { alert("invis stack underflow - not enough IMGs!"); return; }
 		cell = invis[invistop];
 		invistop--;
 		cell.r = rr;
 		cell.c = cc;
 		vis[""+cell.r+"_"+cell.c] = cell;  // add to visible hash
 		cstr = String(rr*wCnt[l]+cc);
 		while (cstr.length<3) cstr='0'+cstr;
 		cstr = ''+picName+'_'+l+'_'+cstr+picExt;
 
 		//debug
 		//alert("rr,cc="+rr+","+cc+" path="+cstr+"  l="+l+" wCnt[l]="+wCnt[l]);
 		
 		cell.path=cstr;
 		cell.src=cstr;
 
 		if (isSafari)
     		    placeTile(cell);  // safari hack
 		
 		cntvis++;
 		
 	    }
 	}
     }
 }
 
 //alert("new invistop="+invistop);
 
 //-- moving the entire div should be the last thing we do
 myImg.style.left=wc/2 - x*z;
 myImg.style.top =hc/2 - y*z;
 
 //alert("myClip.style.left="+myClip.style.left+",  myClip.style.top="+myClip.style.top);
 //alert("myImg.style.left="+myImg.style.left+",  myImg.style.top="+myImg.style.top);
 
 if ((cntinv > 0) || (cntvis > 0))
     window.status='cntinv='+cntinv+'  and cntvis='+cntvis;
 
 thumbFollow();
 
-if ((x != xx) || (y != yy)) setTimeout('scrollMe()',100);
+if ((x != xx) || (y != yy)) setTimeout(scrollMe, 100);
 
 }
 
 
 function getInnerSize(dim) {  
 // note: screen.availWidth works for both IE and Mozilla (but not in a frame?)
 var myWidth = 0, myHeight = 0;
 if( typeof( window.innerWidth ) == 'number' ) {
   myWidth = window.innerWidth; 
   myHeight = window.innerHeight;
 } else if( document.documentElement && 
            (document.documentElement.clientWidth ||document.documentElement.clientHeight ) ) {
   myWidth = document.documentElement.clientWidth; 
   myHeight = document.documentElement.clientHeight;
 } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
   myWidth = document.body.clientWidth; 
   myHeight = document.body.clientHeight;
 }  
 
 //debug safari
 //if (myWidth==0) myWidth=320;
 //if (myHeight==0) myHeight=200;
 
 if (dim=="w") return myWidth
 else        return myHeight;
 }
 
 
 function windowResize() {
 
 //window.onresize=null;  // this really screws up Safari, don't do it!
 
 wc = getInnerSize("w") - 2*marginLeft;
 hc = getInnerSize("h") - 2*marginTop;
 
 myClip.style.width  = wc;
 myClip.style.height = hc;
 
 myCover.style.width  = wc;
 myCover.style.height = hc;
 
 wSmallDelta = Math.round(wc/16);
 hSmallDelta = Math.round(hc/16);
 wBigDelta   = Math.round(wc/4);
 hBigDelta   = Math.round(hc/4);
 
 // minimum number of tiles required to cover view area wc X hc
 wt = Math.ceil(wc/(wSub*m)) + 1;
 ht = Math.ceil(hc/(hSub*m)) + 1;
 
 // increase number of tiles so that we get a sort of pre-fetch buffer going
 //wt+=2;
 //ht+=2;
  
 makeLevel(true);
 updatePos();
 
 //window.onresize=windowResize;   // this really screws up Safari, don't do it!
 }
 
 function getThumb() {
 var result = null;
 if (typeof(parent.parent) != 'undefined')
  if (typeof(parent.parent.list) != 'undefined')
   if (typeof(parent.parent.list.document) != 'undefined') {
     var imgs = parent.parent.list.document.getElementsByTagName('img');
     var target = picName.replace("full","200");
     target = target.substring(0,target.lastIndexOf("/"));
     target = target+picExt;
     target = target.substring(target.indexOf("/visiGene"));
     //alert('imgs.length='+imgs.length+' target='+target);
     for (var i = 0; i < imgs.length; ++i) {
 	var src = imgs[i].src;
 	src = src.substring(src.indexOf("/visiGene"));
 	if (src == target) {
 	    result = imgs[i];
 	    break;
 	}
     }
     
   }
 return result;
 }
 
 
 function init() {
 
 if (!myClip)
     myClip = document.getElementById("myClip");
 
 if (!myCover)
     myCover = document.getElementById("myCover");
 
 picName  = qsParm['url'];
 wOrig[0] = parseInt(qsParm['w']);
 hOrig[0] = parseInt(qsParm['h']);
 
 //debug
 //alert('w,h='+wOrig[0]+','+hOrig[0]+' picName='+picName);
 
 //debug
 //alert('isSafari='+isSafari);
 
 for (l=1;l<numLevels;l++) 
     {
     wOrig[l] = wOrig[l-1]/2;
     hOrig[l] = hOrig[l-1]/2;
     }
 
 for (l=0;l<numLevels;l++) 
     {
     wCnt[l] = Math.ceil(wOrig[l]/wSub);
     hCnt[l] = Math.ceil(hOrig[l]/hSub);
     }    
 
 for (l=0;l<numLevels;l++) 
     {
     wSubEdge[l] = wOrig[l] % wSub;
     if (wSubEdge[l] == 0)
 	wSubEdge[l] = wSub;  
     hSubEdge[l] = hOrig[l] % hSub;
     if (hSubEdge[l] == 0)
 	hSubEdge[l] = hSub;  
     }
 
 var html = '';
 
 myClip.style.left = marginLeft;
 myClip.style.top  = marginTop;
 
 myCover.style.left = marginLeft;
 myCover.style.top  = marginTop;
 
 // one DIV for all levels
 html += '<DIV ID="myImg'+'" STYLE="position:absolute;left:1000;top:0;z-index:1;" ></DIV>';
 myClip.innerHTML=html;  
 
 if (!myImg)
     myImg = document.getElementById("myImg");
 
 l=0;
 z=1;
 wc = getInnerSize("w") - 2*marginLeft;
 hc = getInnerSize("h") - 2*marginTop;
 
 //debug
 //alert('wc='+wc+' marginLeft='+marginLeft+'location.href='+location.href);
 
 //debug
 //alert(document.parent.bigImg.src);
 //alert('test:'+typeof(parent.bigImg.width));
 
 if (wc <= 0) /* safari bug work-around, just make it reload again */
     {
     location.href=location.href;
     return;
     }
 
 while ((wOrig[l] > wc || hOrig[l] > hc) && (l+1) < numLevels)
     {
     l++;
     z = z * 0.5;
     }
 
 x = wOrig[0]/2;
 y = hOrig[0]/2;
 xx = x;
 yy = y;
 
 windowResize();
 
 if (window.focus) window.focus();
 
 }
 
 
 function thumbFollow() {
 
 //debug
 var thumb = getThumb();
 var tox = 0;
 var toy = 0;
 
 if (typeof(parent) == 'undefined') return;
 if (typeof(parent.parent) == 'undefined') return;
 if (typeof(parent.parent.list) == 'undefined') return;
 if (typeof(parent.parent.list.document) == 'undefined') return;
 
 var pclip = parent.parent.list.document.getElementById('perspClip');  // use to clip
 var persp = parent.parent.list.document.getElementById('perspective');  // use to position
 var pbox = parent.parent.list.document.getElementById('perspBox');  // make box using 1x1 transparent gif with border
 
 if (pclip == null) return;
 if (persp == null) return;
 if (pbox  == null) return;
 
 if (thumb == null) { // no thumb to follow with, so hide box and exit
     pclip.style.left = -1000 + 'px';
     pclip.style.top = -1000 + 'px';
     return;
 }    
 
 var wt = thumb.offsetWidth;
 var ht = thumb.offsetHeight;
 //alert('wt='+wt+' ht='+ht);
 
 //alert('img'
 //   +'\nthumb.width='+thumb.width
 //   +'\nthumb.left='+thumb.left
 //   +'\nthumb.style.left='+thumb.style.left
 //   +'\nthumb.offsetWidth='+thumb.offsetWidth
 //   +'\nthumb.offsetLeft='+thumb.offsetLeft
 //   +'\nthumb.offsetTop='+thumb.offsetTop
 //   +'\nthumb.offsetParent='+thumb.offsetParent
 //   +'\nthumb.src='+thumb.src);
 
 tox += thumb.offsetLeft;
 toy += thumb.offsetTop;
 
 //tox += thumb.offsetLeft+thumb.style.borderWidth;
 //toy += thumb.offsetTop+thumb.style.borderWidth;
 
 thumb = thumb.offsetParent;
 
 //alert('cell'
 //   +'\nthumb.width='+thumb.width
 //   +'\nthumb.left='+thumb.left
 //   +'\nthumb.style.left='+thumb.style.left
 //   +'\nthumb.offsetWidth='+thumb.offsetWidth
 //   +'\nthumb.offsetLeft='+thumb.offsetLeft
 //   +'\nthumb.offsetTop='+thumb.offsetTop
 //   +'\nthumb.offsetParent='+thumb.offsetParent
 //   +'\nthumb.src='+thumb.src);
 
 tox += thumb.offsetLeft;
 toy += thumb.offsetTop;
 
 thumb = thumb.offsetParent;
 
 //alert('table'
 //   +'\nthumb.width='+thumb.width
 //   +'\nthumb.left='+thumb.left
 //   +'\nthumb.style.left='+thumb.style.left
 //   +'\nthumb.offsetWidth='+thumb.offsetWidth
 //   +'\nthumb.offsetLeft='+thumb.offsetLeft
 //   +'\nthumb.offsetTop='+thumb.offsetTop
 //   +'\nthumb.offsetParent='+thumb.offsetParent
 //   +'\nthumb.src='+thumb.src);
 
 tox += thumb.offsetLeft;
 toy += thumb.offsetTop;
    
 //alert('tox='+tox+' toy='+toy);
 
 var wp = wc/z * wt/wOrig[0];
 var hp = hc/z * ht/hOrig[0];
 //alert('wp='+wp+' hp='+hp);
 
 //alert('pbox.width='+pbox.width+' pbox.height='+pbox.height);
 //alert('pbox.style.width='+pbox.style.width+' pbox.style.height='+pbox.style.height);
 // set the width and height box, i.e. the 1x1 transparent gif with border
 //pbox.width  = wp+'px';
 //pbox.height = hp+'px';
 pbox.style.width  = wp+'px';
 pbox.style.height = hp+'px';
 
 //alert('x='+x+' y='+y+' wOrig[0]='+wOrig[0]+' hOrig[0]='+hOrig[0]);
 //alert('persp.style.left='+persp.style.left+' persp.style.top='+persp.style.top);
 // use persp div to position the "box"
 persp.style.left = (0 - wp/2 + x*wt/wOrig[0])+'px';
 persp.style.top  = (0 - hp/2 + y*ht/hOrig[0])+'px';
 
 //alert( 'pclip.style.left='+pclip.style.left+' pclip.style.top='+pclip.style.top + ' pclip.style.width='+pclip.style.width+' pclip.style.height='+pclip.style.height);
 // position clipping div over the thumbnail
 pclip.style.left = tox+'px';
 pclip.style.top  = toy+'px';
 pclip.style.width = wt+'px';
 pclip.style.height  = ht+'px';
 
 }
 
 function moveRight(delta) {
 xx = xx + delta/z;
 if (xx > wOrig[0]) xx = wOrig[0];
 updatePos();
 }
 
 function moveLeft(delta) {
 xx = xx - delta/z;
 if (xx < 0) xx = 0;
 updatePos();
 }
 
 function moveUp(delta) {
 yy = yy - delta/z;
 if (yy < 0) yy = 0;
 updatePos();
 }
 
 function moveDown(delta) {
 yy = yy + delta/z;
 if (yy > hOrig[0]) yy = hOrig[0];
 updatePos();
 }
 
 function zoomIn(zoomout) {
 if (zoomout) {
     if ((l<=0) && (m>=maxmag)) return;  // do not zoom too far out
     if (l<=0)
 	m*=2;
     else
     	l--;
     z*=2;
 } else {
     if (l>=numLevels-1) return;  // do not zoom too far in
     if (m>1)
 	m/=2;
     else
     	l++;
     z/=2;
 }
 makeLevel(false);
 updatePos();
 }
 
 function cancelKey(e) {
     if (e.preventDefault) {
 	e.preventDefault();
 	return false;
     }
     else {
 	e.keyCode = 0;
 	e.returnValue = false;
     }
 }
 
 function keyPress(e)
 {  // trapping keyPress seems to be necessary only for old Mozilla browsers
    // because normally suppressing the keyDown event should prevent keyPress from happening at all,
    // but old Mozilla browsers ignore this canceling.
 var code;
 var result = false;
 if (!e) var e = window.event;
 if (e.keyCode) code = e.keyCode;
 else if (e.which) code = e.which;
 var character = String.fromCharCode(code);
 
 //alert('code='+code+' Character was ' + character);
 
 //debug
 //window.status='keypress: code='+code+' Character was ' + character;
 //return;
 
 if (character == '+') zoomIn(true);   // +   // bugfix for FF Mac
 if (character == '-') zoomIn(false);  // -   // bugfix for FF Mac
 
 if ((code == 32) // suppress for mozilla  ; else 
  || (code == 39) // moveRight(wSmallDelta); else 
  || (code == 37) // moveLeft (wSmallDelta); else
  || (code == 38) // moveUp   (hSmallDelta); else
  || (code == 40) // moveDown (hSmallDelta); else
  || (code == 35) // moveRight(wBigDelta  ); else // end 
  || (code == 36) // moveLeft (wBigDelta  ); else // home
  || (code == 33) // moveUp   (hBigDelta  ); else // pgup
  || (code == 34) // moveDown (hBigDelta  ); else // pgdn
  || (code == 43) || ((code == 107) || (code == 187) || ((code == 61))) //  zoomIn(true); else // + 
  || (code == 45) || ((code == 109) || (code == 189)) //  zoomIn(false); else // - 
 ) result = false;
 else result = true;
 if (!result) {
     cancelKey(e);
     if (e.preventBubble)
 	e.preventBubble();
     return false;
 }
 
 //debug
 //if (result) {
 //    alert('code='+code);
 //    alert('Character was ' + character);
 //}
 
 }
 
 function keyDown(e)
 {
 var code;
 var result = false;
 if (!e) var e = window.event;
 if (e.keyCode) code = e.keyCode;
 else if (e.which) code = e.which;
 //var character = String.fromCharCode(code);
 //alert('Character was ' + character);
 //window.status='Character was ' + character;
 window.status='Character was ' + code;
 
 //debug
 //alert('Code was '+code + ' e.charCode='+e.charCode + ' e.ctrlKey='+e.ctrlKey + ' e.shiftKey='+e.shiftKey + ' e.type='+e.type);
 //return;
 
 //debug
 //alert('code='+code);
 //cancelKey(e);
 //if (e.preventBubble)
 //    e.preventBubble();
 //return false;
 
 if (code == 32) { /* do nothing - suppress for mozilla */ } else 
 if (code == 39) moveRight(wSmallDelta); else 
 if (code == 37) moveLeft (wSmallDelta); else
 if (code == 38) moveUp   (hSmallDelta); else
 if (code == 40) moveDown (hSmallDelta); else
 if (code == 35) moveRight(wBigDelta  ); else // end 
 if (code == 36) moveLeft (wBigDelta  ); else // home
 if (code == 33) moveUp   (hBigDelta  ); else // pgup
 if (code == 34) moveDown (hBigDelta  ); else // pgdn
 //disable for FF Mac bugfix, "-" key now trapped in keyPress
 //if ((code == 43) || (code == 107) || (code == 187) || ((code == 61)))  zoomIn(true); else // + 
 //if ((code == 45) || (code == 109) || (code == 189))  zoomIn(false); else // -   
 // else
 result = true;
 window.status=window.status+' result='+result;  // debug
 if (!result) {
     window.status=window.status+' canceling event'; //debug
     cancelKey(e);
     return false;
 }
 return result;
 }
 
 function checkMouseRange() {
 if (mousex < 0) mousex = -1;
 if (mousey < 0) mousey = -1;
 if (mousex >= wOrig[0]) mousex = -1;
 if (mousey >= hOrig[0]) mousey = -1;
 if ((mousex == -1) || (mousey == -1)) {
     mousex = -1;
     mousey = -1;
 }
 }    
 
 function falseFunc(e) { return false; } 
 
 function mouseMove(e) {
 //document.onmousemove = falseFunc;  // disable move event
 var posx = 0;
 var posy = 0;
 if (!e) var e = window.event;
 if (mouseAction) {
     mouseAction = false; // move cancels the click action
     document.body.style.cursor="move";
 }    
 
 if (e.pageX || e.pageY) {
     posx = e.pageX;
     posy = e.pageY;
 }
 else if (e.clientX || e.clientY) {
     // do not use scroll info because of our special handling
     posx = e.clientX; // + document.body.scrollLeft; 
     posy = e.clientY; // + document.body.scrollTop;
 }
 posx = posx - marginLeft;
 posy = posy - marginTop;
 
 //if (posx < 0) posx = -1;
 //if (posy < 0) posy = -1;
 //if (posx >= wc) posx = -1;
 //if (posy >= hc) posy = -1;
 
 window.status='posx,posy='+posx+','+posy;
 
 //mousex = -1;
 //mousey = -1;
 //if ((posx >= 0) && (posy >= 0)) {
     mousex = x+(posx - wc/2)/z;
     mousey = y+(posy - hc/2)/z;
 //    checkMouseRange();
 //}    
 
 
 window.status='mousex,mousey='+mousex+','+mousey+'  mouseDrag='+mouseDrag;
 
 if (mouseDrag) {
     //if ((mousex > 0) && (mousey > 0)) {
         var tempx = x + mouseDragX-mousex;
         var tempy = y + mouseDragY-mousey;
 	if (tempx < 0) tempx = 0;
 	if (tempy < 0) tempy = 0;
 	if (tempx >= wOrig[0]) tempx = wOrig[0]-1;
 	if (tempy >= hOrig[0]) tempy = hOrig[0]-1;
 	x = tempx;
 	y = tempy;
 	xx = x;
 	yy = y;
 	updatePos();
     //} else {
 	//mouseUp(e);
     //}
 }
 
 
 //document.onmousemove = mouseMove;  // re-enable
 
 //e.preventDefault();  // this works to prevent things like dragging in mozilla and safari
 return false;
 }
 
 function dblClick(e) {
 //debug testing singleclick action
 // dont need double click
 if (!e) var e = window.event;
 //alert('dblClick() event called!');
 //clickAction();
 if (e.preventDefault) 
     e.preventDefault();
 return false;
 }
 
 function clickAction(reverse) {
 if ((mousex > 0) && (mousey > 0)) {
     var dx = mousex - x;
     var dy = mousey - y;
     if (!reverse) {
     	x = mousex;
     	y = mousey;
 	xx = x;
     	yy = y;
 	updatePos();
     }
     // now must set new mouse position since no move event yet
     // i.e. if user dblClicks in exactly the same spot, mousex and mousey
     // are still pointing to the old coordinates even though the pictures has shifted.
     mousex = mousex + dx;
     mousey = mousey + dy;
     checkMouseRange();
 
     if (reverse) 
 	zoomIn(false);
     else 
 	zoomIn(true);
 }
 
 }
 
 function mouseDown(e) {
 if (!e) var e = window.event;
 checkMouseRange();
 if ((mousex > 0) && (mousey > 0)) {
     mouseDrag = true;
     mouseAction = true;
     mouseDragX = mousex;
     mouseDragY = mousey;
     window.status='mouseDrag='+mouseDrag;
 }
 //e.preventDefault();  // this works to prevent things like dragging in mozilla and safari but messes up window focus, drats!
 if (window.focus) window.focus();   // in case the windows has lost focus, so we will receive keypresses, if browser supports
 return false;
 }
 
 
 function mouseUp(e) {
 var clickType=1;
 if (!e) var e = window.event;
 if (mouseAction) {
     if (e.which)
 	clickType = e.which;
     if (e.button)
 	clickType = e.button;
 
 	//debug
 	//alert('clickType='+clickType);
 	
     clickAction(clickType>1);
 }    
 mouseAction = false;
 mouseDrag = false;
 document.body.style.cursor="default";
 window.status='mouseDrag='+mouseDrag;
 //e.preventDefault();  // this works to prevent things like dragging in mozilla and safari
 return false;
 }
 
 
 function zoomer(cmd) {
 if (cmd=="in") {
 	zoomIn(true);
 }
 if (cmd=="out") {
 	zoomIn(false);
 }
 if (cmd=="fit") {
     //alert('l='+l+' m='+m+' z='+z+' wc='+wc+' hc='+hc);
     x = wOrig[0]/2;
     y = hOrig[0]/2;
     xx = x;
     yy = y;
     updatePos();
     while ((wOrig[l]*m <= wc || hOrig[l]*m <= hc) && m < 8) {
 	zoomIn(true);
     }
     while ((wOrig[l]*m > wc || hOrig[l]*m > hc) && (l+1) < numLevels) {
 	zoomIn(false);
     }
 }	
 if (cmd=="full") {
     //alert('l='+l+' m='+m+' z='+z);
     while (l > 0)
 	zoomIn(true);
     while (m > 1)
 	zoomIn(false);
 }
 
 return false;
 }
 
 
 function mouseOut(e) {
 if (!e) var e = window.event;
 var tg = (window.event) ? e.srcElement : e.target;
 //window.status='tg.nodeName='+tg.nodeName;
 var reltg = (e.relatedTarget) ? e.relatedTarget : e.toElement;
 if (!reltg) { // apparently FF and IE return null when goes outside window!
     mouseAction=false;
     mouseUp(e);
     return;	
 }    
 //alert('tg.nodeName='+tg.nodeName+'  reltg.nodeName='+reltg.nodeName);
 //window.status='  reltg.nodeName='+reltg.nodeName;
 //while (reltg != tg && reltg.nodeName != 'BODY')
 //	reltg= reltg.parentNode
 //if (reltg== tg) return;
 // Mouseout took place when mouse actually left layer
 // Handle event
 //mouseAction=false;
 //mouseUp(e);
 }
 
 function dragStart(e) {
 if (!e) var e = window.event;
 // suppress!
 if (e.preventDefault) 
     e.preventDefault();
 if (e.preventBubble)
     e.preventBubble();
 e.cancelBubble = true;
 e.returnValue = false;
 return false;
 }
 
 function contextMenu(e) {
 dragStart(e);
 return false;
 }
 
 
 // Additional code for NS
 if (navigator.appName=="Netscape") {
     // need for old Mozilla browsers
     //alert('Netscape event handling!');
     document.addEventListener("keydown", keyDown, true);
     document.addEventListener("keypress", keyPress, true);
     document.addEventListener("dblclick", dblClick, true); 
     document.addEventListener("mouseup", mouseUp, true); 
     document.addEventListener("mousedown", mouseDown, true);
    
     // safari
     document.addEventListener("dragstart", dragStart, true);
     document.addEventListener("drag", dragStart, true);
     
 } else {
     document.onkeydown = keyDown;
     document.onkeypress = keyPress;
     document.ondragstart = dragStart;
     document.ondblclick  = dblClick; 
     document.onmouseup   = mouseUp;
     document.onmousedown = mouseDown;
 }
 
 document.onmousemove = mouseMove;
 document.onmouseout  = mouseOut;
 document.oncontextmenu = contextMenu;
 
 //document.body.ondrag = function () { return false; };
 //document.body.onselectstart = function () { return false; };
 //document.ondrag = function () { return false; };
 //document.onselectstart = function () { return false; };
 
 
 window.onresize=windowResize;
 
 //window.captureEvent(Event.RESIZE)  // old NN 4 style
 
 </SCRIPT>
 
 <body
 onLoad="showBrowser();init();return true;" STYLE="overflow: hidden ! important" 
 >
 
 <!--
 
 myCover empty div with higher z-index appears to defeat the icky auto-drag in safari and mozilla.
 (Hope it works in ff and ie too.)
 (Any text or an image placed in myCover div will hover over our image.)
 
 
 why does safari hang on resize?
 
 STYLE="border:thin;position:absolute;left:60;top:60;width:750;height:600;overflow:hidden!important;z-index:1;filter:alpha(opacity=50%);" 
 <IMG SRC="http://hgwdev-galt.cse.ucsc.edu/images/thelastphotoievertook.jpg">
 
 This is good for making the IE hover-icons, at least reduces it to only one in upper-left corner.
 However it messes up Safari.  However the div in Safari at least still helps reduce dragging problems!
 <IMG SRC="/images/dot_clear.gif" width="100%" height="100%">
 
 visibility:hidden;
 
 -->
 
 <DIV
 ID="myClip"
 STYLE="position:absolute;left:1000;top:0;z-index:1;"
 >
 </DIV>
 
 <DIV
 ID="myCover"
 STYLE="position:absolute;left:1000;top:0;z-index:2;"
 >
 </DIV>
 
 </body>
 </html>