view src/worldmap.js @ 2728:4e2f9e5b0579

Change mapGetWindowSize() to mapGetElementSize(elem) and adjust users accordingly.
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 07 Mar 2024 11:07:24 +0200
parents b68157126f41
children
line wrap: on
line source

//
// Worldmap utility functions in JavaScript for BatMUD maps
// Programmed by Matti 'ccr' Hamalainen <ccr@tnsp.org>
// (C) Copyright 2007-2024 Tecnic Software productions (TNSP)
//
var mapElem = null;
var mapDragEnable = false, mapDragGoing = false;
var mapDragPos = { x: 0, y: 0, mx: 0, my: 0 };
var mapCurrPos = null;
var mapCurrLoc = null;
var mapScrollTimerID = -1;
var mapScrollPosStart, mapScrollPosEnd;
var mapScrollIndex = 0.0, mapScrollSteps = 50;


function mapAddEventListener(obname, evtype, evcallback)
{
  document.getElementById(obname).addEventListener(evtype, evcallback, false);
}


function mapGetElementCoords(elem)
{
  var xc = yc = 0;
  if (elem.offsetParent)
  {
    xc = elem.offsetLeft;
    yc = elem.offsetTop;
    while (elem = elem.offsetParent)
    {
      xc += elem.offsetLeft;
      yc += elem.offsetTop;
    }
  }

  return { x: xc, y: yc, mx: 0, my: 0 };
}


function mapGetElementSize(elem)
{
  var elemW, elemH;
  if (elem.clientWidth || elem.clientHeight)
  {
    elemW = elem.clientWidth;
    elemH = elem.clientHeight;
  }
  else
  if (typeof(elem.innerWidth) == 'number')
  {
    elemW = elem.innerWidth;
    elemH = elem.innerHeight;
  }
  else
  {
    elemW = elemH = 0;
  }

  return { w: elemW, h: elemH };
}


function mapSetWindowPos(px, py)
{
  const dim = mapGetElementSize(mapElem);
  mapDragPos.x = px - (dim.w / 2);
  mapDragPos.y = py - (dim.h / 2);

  mapElem.scrollLeft = mapDragPos.x;
  mapElem.scrollTop = mapDragPos.y;
}


function mapClamp10(a)
{
  return (a < 0.0 ? 0.0 : (a > 1.0 ? 1.0 : a));
}


function mapSCurve(t)
{
  return (t * t * (3.0 - 2.0 * t));
}


function mapLerp(t, a, b)
{
  return (a + t * (b - a));
}


function mapScrollToPos()
{
  if (mapScrollIndex <= 1.0)
  {
    const n = mapSCurve(mapClamp10(mapScrollIndex));
    const px = mapLerp(n, mapScrollPosStart.x, mapScrollPosEnd.x);
    const py = mapLerp(n, mapScrollPosStart.y, mapScrollPosEnd.y);

    mapSetWindowPos(px, py);
    mapScrollIndex += 1.0 / mapScrollSteps;
  }
  else
  {
    clearInterval(mapScrollTimerID);
  }
}


function mapSetActiveLocation(newLoc)
{
  // Change CSS class state for prev/curr/new selected element
  if (mapCurrLoc != null)
  {
    var celem = document.getElementById(mapCurrLoc);
    var mcelem = document.getElementById("m"+ mapCurrLoc);
    if (celem && mcelem)
    {
      celem.classList.remove("nactive");
      mcelem.classList.remove("nactive");
    }
  }

  if (newLoc != null)
  {
    var celem = document.getElementById(newLoc);
    var mcelem = document.getElementById("m"+ newLoc);
    if (celem && mcelem)
    {
      celem.classList.add("nactive");
      mcelem.classList.add("nactive");

      mapCurrLoc = newLoc;
      mapCurrPos = mapGetElementCoords(celem);
    }
  }

  // Set the active item in the location dropdown
  var ssel = document.getElementById("slocation");
  if (ssel)
  {
    var found = false;
    for (var opt, i = 0; opt = ssel.options[i]; i++)
    {
      if (opt.value == newLoc)
      {
        ssel.selectedIndex = i;
        found = true;
        break;
      }
    }

    if (!found)
      ssel.selectedIndex = 0;
  }

  // Set browser window href
  var slink = window.location.href;
  var spos;
  if ((spos = slink.indexOf("#")) >= 0)
    slink = slink.substr(0, spos);
  slink += newLoc != null ? "#" + newLoc : "";

  window.location.href = slink;
}


function mapSetPosToElem(nelem)
{
  const pos = mapGetElementCoords(nelem);
  mapSetWindowPos(pos.x, pos.y);
}


function mapSetLocation(newLoc)
{
  if (newLoc == "")
  {
    mapCurrLoc = null;
  }
  else
  if (mapCurrLoc != newLoc || mapCurrPos != mapDragPos)
  {
    var nelem = document.getElementById(newLoc);
    if (nelem)
    {
      //const spanning = document.getElementById("spanning").checked;
      var celem = mapCurrLoc != null ? document.getElementById(mapCurrLoc) : null;
      //if (spanning && celem)
      if (celem)
      {
        if (mapScrollTimerID != -1)
          clearInterval(mapScrollTimerID);

        if (mapCurrPos != null)
          mapScrollPosStart = mapCurrPos;
        else
          mapScrollPosStart = mapGetElementCoords(celem);

        mapScrollPosEnd = mapGetElementCoords(nelem);
        mapScrollIndex = 0.0;
        mapScrollTimerID = setInterval(mapScrollToPos, 10);
      }
      else
      {
        mapSetPosToElem(nelem);
      }
    }
  }

  mapSetActiveLocation(newLoc);
}


function mapGotoLocation(ev)
{
  mapSetLocation(document.getElementById("slocation").value);
}


function mapLocationLabelClick(ev)
{
  if (!mapDragGoing)
  {
    setTimeout(function()
    {
      mapSetActiveLocation(ev.target.id);
      mapSetPosToElem(ev.target);
    },
    50);
  }
}


function mapUpdateLabelVisibility()
{
  var vcl = document.all ? document.styleSheets[0].rules[0] : document.styleSheets[0].cssRules[0];
  vcl.style.visibility = document.getElementById("slabels").checked ? "visible" : "hidden";
}


function mapDragUpHandler(ev)
{
  if (mapDragGoing)
  {
    mapDragGoing = false;
    mapElem.style.cursor = "grab";
    mapElem.style.removeProperty("user-select");
  }
}


function mapDragMoveHandler(ev)
{
  if (mapDragGoing)
  {
    const dx = ev.clientX - mapDragPos.mx;
    const dy = ev.clientY - mapDragPos.my;

    mapElem.scrollLeft = mapDragPos.x - dx;
    mapElem.scrollTop = mapDragPos.y - dy;
  }
}


function mapDragStartPan(ev)
{
  if (mapDragEnable)
  {
    mapElem.style.cursor = "grabbing";
    mapElem.style.userSelect = "none";

    if (mapScrollTimerID != -1)
      clearInterval(mapScrollTimerID);

    mapDragPos = {
      x: mapElem.scrollLeft,
      y: mapElem.scrollTop,
      mx: ev.clientX,
      my: ev.clientY,
    };

    mapDragGoing = true;
  }
}


function mapUpdateDragPan()
{
  mapDragEnable = document.getElementById("spanning").checked;
  mapElem.style.cursor = mapDragEnable ? "grab" : "default";
}


function mapOnLoad()
{
  mapElem = document.getElementById("smap");

  var slink = window.location.href;
  var spos;
  if ((spos = slink.indexOf("#")) >= 0)
  {
    var eid = unescape(slink.substr(spos + 1).toLowerCase()).trim();
    var nelem = document.getElementById(eid);
    if (nelem)
    {
      setTimeout(function()
      {
        mapSetActiveLocation(eid);
        mapSetPosToElem(nelem);
      },
      50);
    }
  }

  mapAddEventListener("slabels", "change", mapUpdateLabelVisibility);
  mapAddEventListener("slocation", "change", mapGotoLocation);
  mapAddEventListener("spanning", "change", mapUpdateDragPan);
  mapAddEventListener("smap", "mousedown", mapDragStartPan);
  document.addEventListener("mousemove", mapDragMoveHandler);
  document.addEventListener("mouseup", mapDragUpHandler);

  // Add event listeners to locations
  var elems = mapElem.getElementsByClassName("label");
  for (let i = 0; i < elems.length; i++)
  {
    var aelems = elems[i].getElementsByTagName("a");
    if (aelems.length > 0)
    {
      aelems[0].addEventListener("dblclick", mapLocationLabelClick);
    }
  }

  mapUpdateLabelVisibility();
  mapUpdateDragPan();
}