// The Great Alyria Locator, copyright Tejon, 2006 // Version 1.3, 10 October 2006. // Please read the terms-of-use statement at // http://www.clanannwn.net/legal.php /// I hate globals, but this *is* Javascript. // Anyway, here are the configureable variables. // PHP might even do some of this configuration dynamically // A debugging flag. Set to FALSE for normal operations. var DEBUG = false; var URL = "http://www.clanannwn.net/tools/great-alyrian-locator"; // The current maximum zoom level. var defaultZoom = 2; // index of the map in the mapPics array you want to be the default for // each plane var defaultmapID = new Array(15,16,17); var mapPics = new Array( "/img/gal/rune.jpg", "/img/gal/newkolvir.png", "/img/gal/desertthorn.png", "/img/gal/loWANGen.jpg", "/img/gal/NR.png", "/img/gal/sigil.jpg", "/img/gal/vospire.png", "/img/gal/OV.jpg", "/img/gal/maldra.png", "/img/gal/auryn-true.png", "/img/gal/beltane-true.jpg", "/img/gal/avros-true.png", "/img/gal/arcane-true.png", "/img/gal/sepharia-zoom.jpg", "/img/gal/sepharia-true.jpg", "/img/gal/alyria-true.jpg", "/img/gal/faerieplane.gif", "/img/gal/underground.jpg", "/img/gal/world-sepia.jpg" ); // Map Titles var mapTitle = new Array( "Rune", "New Kolvir", "The Desert of Thorn", "LoWANGen", "The Towne of New Rigel", "Sigil", "Vospire", "The Ogre Village", "Maldra's Keep", "Auryn", "Beltane", "Avros and surrounding islands", "The Arcane Archipeligo", "The Greater Rune-New Rigel-Xaventry Metro Area", "Sepharia and Surrounding Islands", "The Alyrian Plane", "The Faerie Plane", "The Alyrian Underground", "A Crappy World Map" ); // Sizes of the maps in pixels var mapSize = new Array( 1,1, 1,1, 1,1, 1,1, 1,1, 1,1, 1,1, 1,1, 1,1, 746,456, 640,380, 612,922, 644,600, 660,694, 640,668, 640,417, 660,550, 610,400, 660,446 ); // Sextant coordinates of the NW and SE corners of the maps var bbox = new Array( 695, 762, 695, 762, 571, 984, 571, 984, 1031,316, 1031, 316, 1159,421, 1159, 421, 800, 555, 800, 555, 435,1230, 435,1230, 312,1177, 312,1177, 1885,459, 1885, 459, 1304,1092,1304,1092, 134,1038, 879,1493, 790,909, 1706,1453, 1372,110, 1983,1031, 786, 638, 1107, 937, 552,434, 950, 853, 505,87, 1390,1010, 1,1, 2300,1500, -4,0, 481,401, -8,43, 2283,1449, 17,2, 2301,1551 ); // The plane each map belongs to: 0 = Alyria, 1 = Underground, 0 = Faerie; var mapPlane = new Array( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0 ); // The zoom level of each map. 1 = a world map, 2 = a continent, // 3 = region, 4 = inside an area/town/city var mapZoom = new Array( 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 3, 3, 2, 1, 1, 1, 1 ); // Configureable pointer details // Pointer image file name var pointersrc = "/img/gal/pointer-circle.gif"; // Position of pointer tip in the image file var pointerTipX = 11; // Position of pointer tip in the image file var pointerTipY = 11; // Lists of stock coordinates. The format is as follows // "name", "coordinates", plane // Townes var slist1 = new Array( "Abandoned Lighthouse", "717, 848", 0, "Arcane Archipelago", "926, 816", 0, "Atlantis", "1988, 1305", 0, "Avarice", "671, 572", 0, "Bazaar", "818, 386", 0, "Capture the Flag","993, 893", 0, "Colyon", "1383, 1029", 0, "Dungeon Deceit", "1254, 1071", 0, "Desert Thorne", "1031, 316", 0, "Diocletian", "553, 1360", 0, "Frost Giant's Keep", "1689, 389", 0, "Hall of Heros", "697, 741", 0, "Hellbent", "1342, 365", 0, "Jalur", "1814, 255", 0, "Lasler", "698, 695", 0, "Lonely Dragon Inn", "600, 700", 0, "Lowangen", "1159, 421", 0, "Magencia", "865, 834", 0, "Maldra's Keep", "1304, 1092", 0, "Mandrake Woods", "654, 365", 0, "Mulakanathos", "1549, 1233", 0, "New Kolvir", "571, 984", 0, "New Rigel", "800, 555", 0, "Ogre Village", "1885, 459", 0, "Pirate's Cove", "1191, 968", 0, "Rune", "695, 762", 0, "Rune Forest", "701, 680", 0, "Sigil", "435, 1230", 0, "Tavern of the Boars", "797, 445", 0, "Tellerium", "1720, 690", 0, "Templeton", "1619, 540", 0, "Tower of Aroxa", "1759, 630", 0, "Tower of Art", "1271, 211", 0, "Traveller's Retreat", "776, 651", 0, "Twin Moons Inn", "1007, 306", 0, "Vesuvius", "295, 1160", 0, "Vospire", "312, 1177", 0, "Winton", "1581, 396", 0, "Wreck of the Feisty Wench", "1889, 957", 0, "Wroth", "736, 391", 0, "Xaventry", "607, 445", 0, "Cavhfail", "355,284 FP", 1, "Eanoss Azelmar", "130, 65 FP", 1, "Hellbent in the FP", "255, 163 FP", 1, "Rahdiyr", "102, 69 FP", 1, "Seelie Castle", "43, 55 FP", 1, "Unseelie Castle", "438,299 FP",1, "Decara", "1579,486 UG", 2, "Hotel Hello", "1228, 768 UG", 2, "Hellbent UG", "1341, 364 UG", 2, "Sea Hag Isle", "1113, 873 UG", 2, "Vir", "1439, 1052 UG", 2 ); // World gates var slist2 = new Array( "Arcane WG", "967,699", 0, "Lowangen WG", "1177, 384", 0, "Maldra's Keep WG", "1279, 1053", 0, "Rune WG", "662, 746", 0, "Sigil WG", "502, 1281", 0, "Templeton WG", "1674, 541", 0, "West Beltane WG", "1015, 1116", 0, "Xaventry WG", "625, 461", 0, "Seelie WG", "76, 301 FP", 1, "Unseelie WG", "389, 81 FP", 1, "Decara WG", "1538,430 UG", 2, "Hotel Hello WG", "1234, 865 UG", 2, "Vir WG", "1752,984 UG", 2 ); // var slist3 = new Array( "Tuath Fiana","1716,611",0, "Enigma","1257,1123",0, "Phoenix","972,1038",0, "Cabal","294,1127",0, "Prophecy","1180,233",0, "Circle of Power","877,611",0, "Excession","1632,486",0, "The Lin-Kuei","1262,1005",0, "Knighthood","1370,1235",0, "Vigil","606,727",0, "Spiritwalk","643,481",0, "Midwinter Eqinox","1727,555",0, "Caitiff","762,607",0, "Sanguine","370,1215",0, "Pollacca","566,423",0, "Dragon","1601,1262",0, "Eclipse","630,935",0, "Valhalla","649,646",0, "Apathy","769,481",0, "Whisper","585,894",0, "Black Tower","768,1406",0, "New Alyrian Order","536,1295",0, "Armageddon","515,1411",0, "Innuedo","248,1196",0, "Stargazer","1707,438",0, "Avalanche","776,1213",0, "Power of Entropy","919,296",0, "Euphoria","1060,709",0, "Oracle","617,1335",0, "Tweaked","839,335",0, "Blasphemy","1071,976",0, "Unforgiven","731,1403",0, "Northstar","732,1257",0, "Ascendancy","558,1390",0, "Aceldama","1668,411",0, "Diversity","423,1092",0, "Revelations clan hall","1209,356",0, "Eyes of the Dragon","1076,342",0, "The Hostile Crew","1551,995",0, "Mirage","926,377",0, "Empire","1385,1196",0, "Transcend","629,1379",0, "Section Five","364,1142",0, "Supremacy","690,483",0, "Far Dareis Mai","618,1252",0, "Knights of Chaos","439,1329",0, "Vampire\'s Mask","200,1176",0, "The Fallen","1311,961",0, "Ahriman\'s Army ","1249,1292",0, "The Cwn Annwn","714,575",0, "Amaunet","824,1234",0, "The Higher Power","1608,421",0, "Kindred","994,1167",0, "The Order of Thanatos","782,1401",0, "Odyssey","809,1298",0, "Angels of Vengeance","529,1379",0, "The Chosen","264,1224",0, "Aurora","716,543",0, "Guardians","526,1334",0, "Legacy","739,499",0, "Timeworn","245,1134",0, "Crypt","788,387",0, "The Dark Order","1767,372",0, "The Betrayed","1578,305",0, "The Returners","718,1421",0, "Saresyn\'s Revenge","749,457",0, "Paradigm","1130,1005",0, "Mercenaries of War","788,347",0, "The Lions Den","1161,1227",0, "Allegiance","1799,638",0, "The Ministry","746,1415",0, "Spira Devexa","806,1265",0, "The Heirs of Tuireann","975,175",0 ); // -------- End configurables variables ------------- var mapID; var mp; var zoomLevel = defaultZoom; var curPos; var browser; var dragObj; var sliderPos = new Array (10, 41, 69, 95); var startDragZoom; var minDragY; var maxDragY; // Set up things like mouse events function init(){ browser = new Browser(); // set the event handlers for clicking on the map mp = new getObj('mappic'); debugMessage("initialized MP"); mp.obj.onclick = mouseLocate; if (mp.obj.captureEvents) mp.obj.captureEvents(Event.CLICK); dragObj = new Object(); dragObj.zIndex = 10; setZoom(zoomLevel); return true; } // Used to bring the Locator back to the initial state function resetState(){ clearSelector(0); document.getElementById('incoord').value = ""; document.getElementById('incoord').select(); document.getElementById('plane').selectedIndex=0; mapID = defaultmapID[0]; document.getElementById('mappic').src = mapPics[mapID]; document.getElementById('maptitle').innerHTML= ' '+mapTitle[mapID]+''; document.getElementById('pointer').style.display='none'; //document.getElementById('maparea').style.height = mapSize[2*mapID+1]; //document.getElementById('maparea').style.width = mapSize[2*mapID]; document.getElementById("selflink").style.display = "none"; setBlocker(false,""); zoomLevel = defaultZoom; sliderSet(zoomLevel); return true; } // The following function is from Mike Hall's code repository, with small // application-specific changes. Mike's code is quite excellent, // particularly at being cross-browser compliant. It can be found at // brainjar.com and I recommend it wholeheartedly. function Browser() { var ua, s, i; this.isIE = false; this.isNS = false; this.isOpera = false; this.version = null; ua = navigator.userAgent; // detect before IE, incase it's masquarading s = "Opera"; if(navigator.userAgent.indexOf('Opera') >= 0) { this.isOpera = true; return; } s = "MSIE"; if ((i = ua.indexOf(s)) >= 0) { this.isIE = true; this.version = parseFloat(ua.substr(i + s.length)); return; } s = "Netscape6/"; if ((i = ua.indexOf(s)) >= 0) { this.isNS = true; this.version = parseFloat(ua.substr(i + s.length)); return; } // Treat any other "Gecko" browser as NS 6.1. s = "Gecko"; if ((i = ua.indexOf(s)) >= 0) { this.isNS = true; this.version = 6.1; return; } } function setPlaneMap(P){ mapID = defaultmapID[P]; document.getElementById('maptitle').innerHTML=''+mapTitle[mapID]+''; document.getElementById('neighbors').innerHTML = ""; document.getElementById('mappic').src = mapPics[mapID]; //document.getElementById('maparea').style.height = mapSize[2*mapID+1]; //document.getElementById('maparea').style.width = mapSize[2*mapID]; clearSelector(0); document.getElementById('pointer').style.display='none'; document.getElementById("selflink").style.display = "none"; setBlocker(false,""); } // A constructor to get around JS's bizarre scoping rules function getObj(name) { if (document.getElementById) { this.obj = document.getElementById(name); this.style = document.getElementById(name).style; } else if (document.all) { this.obj = document.all[name]; this.style = document.all[name].style; } else if (document.layers) { this.obj = document.layers[name]; this.style = document.layers[name]; } } // Called when the map gets clicked on. Finds the location and calls // LOCATE to map it. function mouseLocate(e) { var pix = new Array(0,0) // IE uses window.event if (!e) var e = window.event; if (e.offsetX || e.offsetY){ // IE behavior pix[0] = e.offsetX; pix[1] = e.offsetY; } else if (e.layerX || e.layerY){ // Sane behavior pix[0] = e.layerX; pix[1] = e.layerY; } else { //Undefined behavior return true; } // PIX contains the mouse position relative to // the image in pixel units. Transform to sextant // coordinates and put into SEX var sex = pix2sex(pix); clearSelector(0); locate("("+sex[0]+", "+sex[1]+")"); } function pix2sex(pix){ var sex = new Array(0,0); var X0 = bbox[4*mapID]; var Y0 = bbox[4*mapID+1]; var X1 = bbox[4*mapID+2]; var Y1 = bbox[4*mapID+3]; var DX = mapSize[2*mapID]; var DY = mapSize[2*mapID+1]; sex[0] = Math.round( (pix[0]) / DX * (X1 - X0) + X0); sex[1] = Math.round( (pix[1]) / DY * (Y1 - Y0) + Y0); return sex; } function sex2pix(sex){ var pix = new Array(0,0); var X0 = bbox[4*mapID]; var Y0 = bbox[4*mapID+1]; var X1 = bbox[4*mapID+2]; var Y1 = bbox[4*mapID+3]; var DX = mapSize[2*mapID]; var DY = mapSize[2*mapID+1]; pix[0] = Math.round( (sex[0]-X0) * DX / ( X1 - X0 ) ); pix[1] = Math.round( (sex[1]-Y0) * DY / ( Y1 - Y0 ) ); return pix; } // Pull two numbers out of the input. This should be pretty robust. function parseInput(inputstring){ var twoCoords = new RegExp("([0-9]+)[^0-9]+?([0-9]+)","i"); var ar = twoCoords.exec(inputstring); if(!ar) { throw "invalid coordinates: "+inputstring; } var xCoord=eval(ar[1]); var yCoord=eval(ar[2]); return new Array(xCoord,yCoord); } function setSelect(n,i){ if(n==1) { document.getElementById('sc1').selectedIndex=i; } else if(n==2) { document.getElementById('sc2').selectedIndex=i; } else if(n==3) { document.getElementById('sc3').selectedIndex=i; } return true; } function clearSelector(selNum){ if(selNum != 1) {document.getElementById('sc1').selectedIndex=0;} if(selNum != 2) {document.getElementById('sc2').selectedIndex=0;} if(selNum != 3) {document.getElementById('sc3').selectedIndex=0;} } function idLocate(selNum,ID){ if(selNum==1) { document.getElementById('plane').selectedIndex=slist1[3*ID+2]; locate(slist1[3*ID+1]); } else if(selNum==2) { document.getElementById('plane').selectedIndex=slist2[3*ID+2]; locate(slist2[3*ID+1]); } else if(selNum==3) { document.getElementById('plane').selectedIndex=slist3[3*ID+2]; locate(slist3[3*ID+1]); } return true; } // This function is the engine. It computes where the pointer // should be and puts it there. It takes the string with the // coordinates as input. function locate(inputstring){ // Try to get coordinates, just return if you cant try{ var xyCoord = parseInput(inputstring);} catch(error){ debugMessage(error); document.getElementById("incoord").value=""; document.getElementById('neighbors').innerHTML = ""; return; } // Find the requested plane (rP) var rP = document.getElementById("plane").value; debugMessage("requested plane="+rP); var X=xyCoord[0]; var Y=xyCoord[1]; // Going to look for a map that has those coordinates found = false; for(i=0; 4*i < bbox.length; i++){ if(X >= bbox[4*i+0] && Y >= bbox[4*i+1] && X <= bbox[4*i+2] && Y <= bbox[4*i+3] && rP == mapPlane[i] && zoomLevel >= mapZoom[i] ) { mapID = i; found = true; document.getElementById('mappic').src = mapPics[mapID]; //document.getElementById('maparea').style.height = mapSize[2*mapID+1]; //document.getElementById('maparea').style.width = mapSize[2*mapID]; document.getElementById('maptitle').innerHTML= ''+mapTitle[mapID]+''; break; } else {;} } // Complain if we didnt find one if(! found) { setBlocker(true,"I could not find a map for those coordinates."); document.getElementById('incoord').select(); document.getElementById('neighbors').innerHTML = ""; return true; } else { // Otherwise, carry on. setBlocker(false,""); } // Calculate the pixel positions for this map var pix = sex2pix( [X,Y] ); // Correct for the position of the part of the pointer image // that's actually used to point pix[0] = pix[0] - pointerTipX; pix[1] = pix[1] - pointerTipY; // Place the pointer and turn it on if(mapZoom[mapID]<4) { document.getElementById("pointer").style.left = pix[0] +'px'; document.getElementById("pointer").style.top = pix[1] +'px'; document.getElementById("pointer").style.display = 'block'; } else { document.getElementById("pointer").style.display = 'none'; } // Put our version of the coordinates in the input box document.getElementById("incoord").value="("+X+", "+Y+")"; curPos = " "+X+", "+Y; // Find and display nearby landmarks findNeighbors(X,Y,rP); link = URL + "?xpos="+X+"&ypos="+Y+"&plane="+rP; document.getElementById("selflinkbox").value = link; document.getElementById("selflink").style.display="inline"; return true; } // Iterate over the various selectors, loading them function loadSelectors(){ loadSelector(document.getElementById('sc1'),slist1); loadSelector(document.getElementById('sc2'),slist2); if(loadSelector(document.getElementById('sc3'),slist3)) { document.getElementById('clanhalls').style.display="inline"; document.getElementById('sc3').style.display="inline"; document.getElementById('sc3').disabled=false; } return true; } // Load an individual selector function loadSelector(sc,list){ if(list.length < 1) return false; sc.length = list.length/3+1; // There is an inherent offset between the indexing for the selector // and the value it returns. The value it returns is the position in the // corresponding slist array, starting at 0. However, our first selector // is blank (indicating no selection), which causes this shift by 1. for(i=0; 3*i 0) { for(j=R-1;j>=0;j--){ if(d < minD[j]) { minD[j+1] = minD[j]; nbr[2*j+2] = nbr[2*j]; nbr[2*j+3] = nbr[2*j+1]; selNum[j+1] = selNum[j]; selID[j+1] = selID[j]; minD[j] = d; nbr[2*j] = slist1[3*i]; nbr[2*j+1] = slist1[3*i+1]; selNum[j] = 1; selID[j] = i; } } } } // Search through the second list of stock coordinates for(i=0; 3*i < slist2.length; i++){ if(rP != slist2[3*i+2]) continue; var txy = parseInput(slist2[3*i+1]); d = ((txy[0]-X)*(txy[0]-X)+(txy[1]-Y)*(txy[1]-Y)); if(d > 0) { for(j=R-1;j>=0;j--){ if(d < minD[j]) { minD[j+1] = minD[j]; nbr[2*j+2] = nbr[2*j]; nbr[2*j+3] = nbr[2*j+1]; selNum[j+1] = selNum[j]; selID[j+1] = selID[j]; minD[j] = d; nbr[2*j] = slist2[3*i]; nbr[2*j+1] = slist2[3*i+1]; selNum[j] = 2; selID[j] = i; } } } } // Search through the third list of stock coordinates for(i=0; 3*i < slist3.length; i++){ if(rP != slist3[3*i+2]) continue; var txy = parseInput(slist3[3*i+1]); d = ((txy[0]-X)*(txy[0]-X)+(txy[1]-Y)*(txy[1]-Y)); if(d > 0) { for(j=R-1;j>=0;j--){ if(d < minD[j]) { minD[j+1] = minD[j]; nbr[2*j+2] = nbr[2*j]; nbr[2*j+3] = nbr[2*j+1]; selNum[j+1] = selNum[j]; selID[j+1] = selID[j]; minD[j] = d; nbr[2*j] = slist3[3*i]; nbr[2*j+1] = slist3[3*i+1]; selNum[j] = 3; selID[j] = i; } } } } // Now the squared distances should be in MIND in ascending order // The corresponding locations and names are now in NBR array, in // pairs. MIND(I) corresponds to NBR(2I) and NBR(2I+1) // Start building the innerHTML string str = 'Nearby locations"; debugMessage(str); // And display it. document.getElementById('neighbors').innerHTML = str; } // A function that handles error messages. Blocks the map. function setBlocker(state,message){ if(!state) { document.getElementById('lightbox').style.display = 'none'; return true; } document.getElementById('LBmessage').innerHTML = message; document.getElementById('lightbox').style.display = 'block'; document.getElementById('lightbox').style.height=mapSize[2*mapID+1]; return true; } function debugMessage(message){ if (DEBUG) document.getElementById('report').style.display = "block"; document.getElementById('report').value = message; } // A wrapper that will generally be called from ONLOAD to initialize // the locator to some particular plane and coordinate set. Useful when // you are handling a GET request. function planeLocate(input,rP){ var fxy = parseInput(input); sel = 0; for(i=0; 3*i < slist1.length; i++){ if(rP != slist1[3*i+2]) continue; var txy = parseInput(slist1[3*i+1]); if(fxy[0] == txy[0] && fxy[1] == txy[1]) { sel = 1; index = i+1; break; } } if(sel == 0) { for(i=0; 3*i < slist2.length; i++){ if(rP != slist2[3*i+2]) continue; var txy = parseInput(slist2[3*i+1]); if(fxy[0] == txy[0] && fxy[1] == txy[1]) { sel = 2; index = i+1; break; } } } if(sel == 0) { for(i=0; 3*i < slist3.length; i++){ if(rP != slist3[3*i+2]) continue; var txy = parseInput(slist3[3*i+1]); if(fxy[0] == txy[0] && fxy[1] == txy[1]) { sel = 3; index = i+1; break; } } } document.getElementById('plane').selectedIndex=rP; if(sel >0) { clearSelector(0); setSelect(sel,index); } locate(input); } // The following function is from Mike Hall's code repository, with small // application-specific changes. Mike's code is quite excellent, // particularly at being cross-browser compliant. It can be found at // brainjar.com and I recommend it wholeheartedly. function dragStart(event, id) { var el; var x, y; // If an element id was given, find it. Otherwise use the element being // clicked on. if (id) dragObj.elNode = document.getElementById(id); else { if (browser.isIE) dragObj.elNode = window.event.srcElement; if (browser.isNS) dragObj.elNode = event.target; if (browser.isOpera){ dragObj.elNode = event.target; } // If this is a text node, use its parent element. if (dragObj.elNode.nodeType == 3) dragObj.elNode = dragObj.elNode.parentNode; } // Get cursor position with respect to the page. if (browser.isIE) { x = window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft; y = window.event.clientY + document.documentElement.scrollTop + document.body.scrollTop; } if (browser.isNS) { x = event.clientX + window.scrollX; y = event.clientY + window.scrollY; } if (browser.isOpera) { x = event.clientX; y = event.clientY; } // Save starting positions of cursor and element. dragObj.cursorStartX = x; dragObj.cursorStartY = y; dragObj.elStartLeft = parseInt(dragObj.elNode.style.left, 10); dragObj.elStartTop = parseInt(dragObj.elNode.style.top, 10); if (isNaN(dragObj.elStartLeft)) dragObj.elStartLeft = 0; if (isNaN(dragObj.elStartTop)) dragObj.elStartTop = 0; startDragZoom = sliderPos[zoomLevel-1]; minDragY = dragObj.cursorStartY - sliderPos[zoomLevel-1] + sliderPos[0]; maxDragY = dragObj.cursorStartY - sliderPos[zoomLevel-1] + sliderPos[3]; // Update element's z-index. dragObj.elNode.style.zIndex = ++dragObj.zIndex; // Capture mousemove and mouseup events on the page. if (browser.isIE) { document.attachEvent("onmousemove", dragGo); document.attachEvent("onmouseup", dragStop); window.event.cancelBubble = true; window.event.returnValue = false; } if (browser.isNS) { document.addEventListener("mousemove", dragGo, true); document.addEventListener("mouseup", dragStop, true); event.preventDefault(); } if (browser.isOpera) { document.addEventListener("mousemove", dragGo, true); document.addEventListener("mouseup", dragStop, true); event.preventDefault(); } } // The following function is from Mike Hall's code repository, with small // application-specific changes. Mike's code is quite excellent, // particularly at being cross-browser compliant. It can be found at // brainjar.com and I recommend it wholeheartedly. function dragGo(event) { var x, y; // Get cursor position with respect to the page. if (browser.isIE) { x = window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft; y = window.event.clientY + document.documentElement.scrollTop + document.body.scrollTop; } if (browser.isNS) { x = event.clientX + window.scrollX; y = event.clientY + window.scrollY; } if (browser.isOpera) { x = event.clientX; y = event.clientY; } // Move drag element by the same amount the cursor has moved. y = Math.min(maxDragY,y); y = Math.max(minDragY,y); var newY = (dragObj.elStartTop + y - dragObj.cursorStartY); dragObj.elNode.style.top = newY + "px"; setZoom(y - dragObj.cursorStartY + startDragZoom); if (browser.isIE) { window.event.cancelBubble = true; window.event.returnValue = false; } if (browser.isNS) { event.preventDefault(); } if (browser.isOpera) { event.preventDefault(); } } // The following function is from Mike Hall's code repository, with small // application-specific changes. Mike's code is quite excellent, // particularly at being cross-browser compliant. It can be found at // brainjar.com and I recommend it wholeheartedly. function dragStop(event) { // Stop capturing mousemove and mouseup events. if (browser.isIE) { document.detachEvent("onmousemove", dragGo); document.detachEvent("onmouseup", dragStop); } if (browser.isOpera) { document.removeEventListener("mousemove", dragGo, true); document.removeEventListener("mouseup", dragStop, true); } if (browser.isNS) { document.removeEventListener("mousemove", dragGo, true); document.removeEventListener("mouseup", dragStop, true); } sliderSet(zoomLevel); } // Receives the vertical position of the slider sets ZOOMLEVEL function setZoom(y){ // These are the ranges in which a click registers a certain slider // position. They are specific to the slider image. if(y < 25) { zoomLevel = 1; } else if(y < 55) { zoomLevel = 2; } else if(y < 85) { zoomLevel = 3; } else { zoomLevel = 4; } locate(curPos); } // Sets the slider knob position based on the zoom level represented by its // argument function sliderSet(z){ var sliderPos = new Array (0, 10, 41, 69, 95); document.getElementById("sliderKnob").style.top = sliderPos[z] +'px'; return true; }