changeset 2562:2b81727da194

Refactor some of the javascript snippets out from the HTML files themselves and into a separate citymap.js. Also implement panning and other features into citymaps.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 27 Jan 2024 04:01:35 +0200
parents 94ced9325593
children 49f74e990a92
files Makefile.common misc/misc.css misc/misc.html src/citymap.js src/mkcitymap.c
diffstat 5 files changed, 331 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.common	Sat Jan 27 04:02:00 2024 +0200
+++ b/Makefile.common	Sat Jan 27 04:01:35 2024 +0200
@@ -79,7 +79,7 @@
 ###
 ### Special targets
 ###
-upload: src/tooltip.js src/util.js $(MISC_MAPS_PATH)misc.css \
+upload: src/tooltip.js src/citymap.js src/util.js $(MISC_MAPS_PATH)misc.css \
 	$(addprefix $(MISC_MAPS_PATH),$(addsuffix .html,$(MISC_MAPS))) \
 	$(addprefix $(MISC_MAPS_PATH),$(addsuffix .map,$(MISC_MAPS))) \
 	$(addprefix $(MISC_MAPS_PATH),$(addsuffix .loc,$(MISC_MAPS)))
--- a/misc/misc.css	Sat Jan 27 04:02:00 2024 +0200
+++ b/misc/misc.css	Sat Jan 27 04:01:35 2024 +0200
@@ -55,7 +55,7 @@
 pre.map {
 }
 
-div.map {
+#smap {
   position: absolute;
   left: 0;
   right: 20%;
@@ -67,30 +67,30 @@
   text-align: center;
 }
 
-div.map.old {
+#smap.old {
 }
 
-div.map.old .loc {
+#smap.old .loc {
   color: white !important;
 }
 
-div.map.new {
+#smap.new {
   font-weight: bold;
 }
 
-div.map a.loc {
+#smap a.loc {
   background: black;
   color: red;
   text-decoration: none;
 }
 
-div.map a.loc:hover {
+#smap a.loc:hover {
   background: white;
   color: red;
 }
 
 
-div.loctab {
+#loclist {
   background: black;
   color: white;
   position: absolute;
@@ -106,53 +106,53 @@
   padding: 0.5em;
 }
 
-div.loctab a.loc {
+#loclist a.item {
   display: block;
   color: red;
   text-decoration: none;
   padding: 0.1em;
 }
 
-div.loctab a.loc:hover {
+#loclist a.item:hover {
   background: #666;
 }
 
-div.loctab a.loc.ltguild.lt1  {
+#loclist a.item.ltguild.lt1  {
   color: #080;
 }
 
-div.loctab a.loc.ltguild.lt2  {
+#loclist a.item.ltguild.lt2  {
   color: #0f0;
 }
 
-div.loctab a.loc.ltgov.lt0 {
+#loclist a.item.ltgov.lt0 {
   color: #f0f;
 }
 
-div.loctab a.loc.ltgov.lt1 {
+#loclist a.item.ltgov.lt1 {
   color: #a0a;
 }
 
-div.loctab a.loc.ltgov.lt2 {
+#loclist a.item.ltgov.lt2 {
   color: #ff0;
 }
 
-div.loctab a.loc.ltshop {
+#loclist a.item.ltshop {
   color: #cc0;
 }
 
-div.loctab a.loc.ltprimary {
+#loclist a.item.ltprimary {
 }
 
-div.loctab a.loc.ltsubitems {
+#loclist a.item.ltsubitems {
   text-decoration: underline;
 }
 
-div.loctab a.loc.ltsecondary {
+#loclist a.item.ltsecondary {
   padding-left: 1em;
 }
 
-div.loctab a.loc.ltsecondary::before {
+#loclist a.item.ltsecondary::before {
   content: "•";
   padding-right: 0.5em;
 }
--- a/misc/misc.html	Sat Jan 27 04:02:00 2024 +0200
+++ b/misc/misc.html	Sat Jan 27 04:01:35 2024 +0200
@@ -1,2 +1,2 @@
- <script type="text/javascript" src="tooltip.js"></script>
+ <script type="text/javascript" src="citymap.js"></script>
  <link rel="stylesheet" href="misc.css" type="text/css" />
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/citymap.js	Sat Jan 27 04:01:35 2024 +0200
@@ -0,0 +1,302 @@
+//
+// Positioning, tooltip etc utility functions in JavaScript for BatMUD maps
+// Programmed by Matti 'ccr' Hamalainen <ccr@tnsp.org>
+// (C) Copyright 2006-2024 Tecnic Software productions (TNSP)
+//
+var mapTipPrev = null;
+var mapTipItem = null;
+var mapTipXC = 0;
+var mapTipYC = 0;
+
+var mapElem = null, listElem = null;
+var mapDragEnable = true, mapDragGoing = false;
+var mapDragPos = { x: 0, y: 0, mx: 0, my: 0 };
+var mapCurrID;
+
+
+function mapFindElemCoords(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 mapGetWindowSize()
+{
+  var winW = 0, winH = 0;
+  if (typeof(window.innerWidth) == 'number')
+  {
+    // Non-MSIE
+    winW = window.innerWidth;
+    winH = window.innerHeight;
+  }
+  else
+  if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight))
+  {
+    // MSIE 6+ in 'standards compliant mode'
+    winW = document.documentElement.clientWidth;
+    winH = document.documentElement.clientHeight;
+  }
+  else
+  if (document.body && (document.body.clientWidth || document.body.clientHeight))
+  {
+    // MSIE 4 compatible
+    winW = document.body.clientWidth;
+    winH = document.body.clientHeight;
+  }
+
+  return { w: winW, h: winH };
+}
+
+
+// Add or remove given CSS class to specified element
+function mapSetStyle(uid, ustyle, uset)
+{
+  var uelem = document.getElementById(uid);
+  if (uelem)
+  {
+    if (uset)
+      uelem.classList.add(ustyle);
+    else
+      uelem.classList.remove(ustyle);
+  }
+}
+
+
+// Update the tooltip box
+function mapUpdateTooltip(ev)
+{
+  var x = document.all ? (window.event.x + document.body.scrollLeft) : ev.pageX;
+  var y = document.all ? (window.event.y + document.body.scrollTop)  : ev.pageY;
+  if (mapTipItem != null)
+  {
+    const dim = mapGetWindowSize();
+    var boxW = mapTipItem.clientWidth + 25, boxH = mapTipItem.clientHeight + 25;
+
+    if (x + boxW + 15 >= dim.w)
+      x -= boxW;
+    else
+      x += 15;
+
+    if (y + boxH + 15 >= dim.h)
+      y -= boxH;
+    else
+      y += 15;
+
+    mapTipXC = x;
+    mapTipYC = y;
+
+    mapTipItem.style.left = x + "px";
+    mapTipItem.style.top  = y + "px";
+  }
+}
+
+
+// Set or unset active element style
+function mapSetActive(eid, uset)
+{
+  mapSetStyle('maploc'+eid, "locactive", uset);
+  mapSetStyle('listloc'+eid, "locactive", uset);
+}
+
+
+// Hilite map and list elements of given id
+function qh(ev)
+{
+  var eid = ev.target.dataset.id;
+
+  if (mapTipPrev != null) mapSetActive(mapTipPrev, false);
+  mapSetActive(eid, true);
+  mapTipPrev = eid;
+}
+
+
+function qn(ev)
+{
+  if (mapTipPrev != null) mapSetActive(mapTipPrev, false);
+  mapTipPrev = null;
+  htt(ev);
+}
+
+
+function sttq(ev)
+{
+  const eid = ev.target.dataset.id;
+  qh(ev);
+  stt(ev);
+}
+
+
+// Show tooltip
+function stt(ev)
+{
+  const eid = ev.target.dataset.id;
+
+  htt(ev);
+
+  mapTipItem = document.getElementById("tt"+ eid);
+
+  mapTipItem.style.left    = mapTipXC + "px";
+  mapTipItem.style.top     = mapTipYC + "px";
+  mapTipItem.style.display = "block";
+}
+
+
+// Hide tooltip
+function htt(ev)
+{
+  if (mapTipItem)
+  {
+    mapTipItem.style.display = "none";
+  }
+}
+
+
+function mapSetMapPosToID(eid)
+{
+  const dim = mapGetWindowSize();
+  var nelem = document.getElementById('maploc'+eid);
+  if (nelem)
+  {
+    const pos = mapFindElemCoords(nelem);
+    mapDragPos.x = pos.x - (dim.w / 2);
+    mapDragPos.y = pos.y - (dim.h / 2);
+
+    mapElem.scrollLeft = mapDragPos.x;
+    mapElem.scrollTop = mapDragPos.y;
+  }
+
+  nelem = document.getElementById('listloc'+eid);
+  if (nelem)
+  {
+    const pos = mapFindElemCoords(nelem);
+    listElem.scrollTop = pos.y - 10;
+  }
+}
+
+
+function perse(ev)
+{
+  const eid = ev.target.dataset.id;
+  console.log(eid);
+  if (mapCurrID != eid)
+  {
+    mapSetStyle('maploc'+mapCurrID, "lochilite", false);
+    mapSetStyle('listloc'+mapCurrID, "lochilite", false);
+  }
+  mapCurrID = eid;
+
+  mapSetStyle('maploc'+eid, "lochilite", true);
+  mapSetStyle('listloc'+eid, "lochilite", true);
+  mapSetMapPosToID(eid);
+}
+
+
+function mapDragUpHandler(ev)
+{
+  if (mapDragGoing)
+  {
+    mapDragGoing = false;
+    mapElem.style.cursor = "grab";
+    mapElem.style.removeProperty("user-select");
+  }
+}
+
+
+function mapDragMoveHandler(ev)
+{
+  mapUpdateTooltip(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 mapStartDragPan(ev)
+{
+  if (mapDragEnable)
+  {
+    mapElem.style.cursor = "grabbing";
+    mapElem.style.userSelect = "none";
+
+    mapDragPos = {
+      x: mapElem.scrollLeft,
+      y: mapElem.scrollTop,
+      mx: ev.clientX,
+      my: ev.clientY,
+    };
+
+    mapDragGoing = true;
+  }
+}
+
+
+function mapOnLoad()
+{
+  mapElem = document.getElementById("smap");
+  listElem = document.getElementById("loclist");
+
+  mapElem.style.cursor = "grab";
+
+  // Add event listeners to locations
+  var elems = document.getElementsByClassName("loc");
+  for (let i = 0; i < elems.length; i++)
+  {
+    var elem = elems[i];
+    elem.addEventListener("mouseover", sttq);
+    elem.addEventListener("mouseout", qn);
+    elem.addEventListener("click", perse);
+  }
+
+  // .. and location list items
+  var elems = document.getElementsByClassName("item");
+  for (let i = 0; i < elems.length; i++)
+  {
+    var elem = elems[i];
+    elem.addEventListener("mouseover", elem.dataset.info == "true" ? sttq : qh);
+    elem.addEventListener("mouseout", qn);
+    elem.addEventListener("click", perse);
+  }
+
+  // And map navigation / tooltip stuff
+  mapElem.addEventListener("mousedown", mapStartDragPan);
+  document.addEventListener("mousemove", mapDragMoveHandler);
+  document.addEventListener("mouseup", mapDragUpHandler);
+
+  // Finally, scroll to position
+  var slink = window.location.href;
+  if ((spos = slink.indexOf("#")) >= 0)
+  {
+    var eid = unescape(slink.substr(spos + 1).toLowerCase()).trim();
+    var elem = document.getElementById('maploc'+eid);
+    if (elem)
+    {
+      mapCurrID = eid;
+
+      mapSetStyle('maploc'+eid, "lochilite", true);
+      mapSetStyle('listloc'+eid, "lochilite", true);
+
+      setTimeout(function()
+      {
+        mapSetMapPosToID(eid);
+      },
+      50);
+    }
+  }
+}
--- a/src/mkcitymap.c	Sat Jan 27 04:02:00 2024 +0200
+++ b/src/mkcitymap.c	Sat Jan 27 04:01:35 2024 +0200
@@ -131,7 +131,7 @@
     }
 
     fprintf(outFile,
-        "<div id=\"smap\" class=\"map %s\">"
+        "<div id=\"smap\" class=\"%s\">"
         "<pre class=\"map\">",
         optUseOldFormat ? "old" : "new"
         );
@@ -192,7 +192,7 @@
     fprintf(outFile,
     "</pre>"
     "</div>\n"
-    "<div class=\"loctab\">\n"
+    "<div id=\"loclist\">\n"
     );
 
     qsort(locs->locations, locs->nlocations, sizeof(LocMarker *), compareLocation);
@@ -208,17 +208,15 @@
 
         for (int i = 0; i < marker->nnames; i++)
         {
-
             fprintf(outFile,
-                "<a class=\"loc %s%s lt%s lt%d\" id=\"listloc%d_%d\" href=\"?%d_%d\" "
-                "onmouseover=\"%s('%d_%d');\" onmouseout=\"qn();\">",
+                "<a class=\"item %s%s lt%s lt%d\" id=\"listloc%d_%d\" data-id=\"%d_%d\" data-info=\"%s\" href=\"#%d_%d\">",
                 i == 0 ? "ltprimary" : "ltsecondary",
                 i == 0 && marker->nnames > 1 ? " ltsubitems" : "",
                 getCityLocationType(marker),
                 marker->align,
                 marker->xc, marker->yc,
                 marker->xc, marker->yc,
-                (marker->freeform /* be less spammy in the list || marker->nnames > 1 */) ? "sttq" : "qh",
+                marker->freeform ? "true" : "false",
                 marker->xc, marker->yc);
 
             fputse(marker->names[i].name, outFile);
@@ -243,9 +241,10 @@
             return;
 
         fprintf(outFile,
-        "<div class=\"tooltip holder\" id=\"tt%d_%d\">"
+        "<div class=\"tooltip holder\" id=\"tt%d_%d\" data-id=\"%d_%d\">"
         "<h1>%s%s</h1>",
         marker->xc, marker->yc,
+        marker->xc, marker->yc,
         marker->names[0].name,
         tmps);
 
@@ -332,10 +331,8 @@
                 {
                     fprintf(outFile,
                     "<span class=\"%c\">"
-                    "<a onmouseover=\"sttq('%d_%d');\" "
-                    "onmouseout=\"qn();\" "
-                    "class=\"loc\" id=\"maploc%d_%d\" "
-                    "href=\"?%d_%d\">%c</a></span>",
+                    "<a class=\"loc\" id=\"maploc%d_%d\" data-id=\"%d_%d\""
+                    "href=\"#%d_%d\">%c</a></span>",
                     'a' + color,
                     marker->xc, marker->yc,
                     marker->xc, marker->yc,