<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.seculargames.org/index.php?action=history&amp;feed=atom&amp;title=MediaWiki%3ALocMaps.js</id>
	<title>MediaWiki:LocMaps.js - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.seculargames.org/index.php?action=history&amp;feed=atom&amp;title=MediaWiki%3ALocMaps.js"/>
	<link rel="alternate" type="text/html" href="https://wiki.seculargames.org/index.php?title=MediaWiki:LocMaps.js&amp;action=history"/>
	<updated>2026-07-29T05:22:21Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.45.3</generator>
	<entry>
		<id>https://wiki.seculargames.org/index.php?title=MediaWiki:LocMaps.js&amp;diff=45801&amp;oldid=prev</id>
		<title>imported&gt;Loramin at 20:01, 30 November 2025</title>
		<link rel="alternate" type="text/html" href="https://wiki.seculargames.org/index.php?title=MediaWiki:LocMaps.js&amp;diff=45801&amp;oldid=prev"/>
		<updated>2025-11-30T20:01:08Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;try {&lt;br /&gt;
  (function() {&lt;br /&gt;
&lt;br /&gt;
    var buildImgFromFileName = function(fileName) {&lt;br /&gt;
      return &amp;#039;&amp;lt;img src=&amp;quot;/images/&amp;#039; + fileName.replace(/ /g, &amp;#039;_&amp;#039;) + &amp;#039;&amp;quot;&amp;gt;&amp;#039;&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // TODO: Add support for cropping maps that have multiple maps.&lt;br /&gt;
    // For instance, the following CSS styles show only one of the three maps for &lt;br /&gt;
    // Erudin Palace:&lt;br /&gt;
    // background-image:url(/images/Erudinpalace.jpg); &lt;br /&gt;
    // background-position: 250px 0px; width: 275px; height: 272px&lt;br /&gt;
    // Figure out how to make the Xs line up with those styles added&lt;br /&gt;
&lt;br /&gt;
    var locIsWithinAlternateData = function(loc, alternateData) {&lt;br /&gt;
      return loc.x &amp;lt; alternateData.maxX &amp;amp;&amp;amp;&lt;br /&gt;
      loc.x &amp;gt; alternateData.minX &amp;amp;&amp;amp;&lt;br /&gt;
      loc.y &amp;lt; alternateData.maxY &amp;amp;&amp;amp;&lt;br /&gt;
      loc.y &amp;gt; alternateData.minY;&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Determines if the provided name contains any of the provided words&lt;br /&gt;
     */&lt;br /&gt;
    // TODO: Add a some polyfill to make this a lot simpler&lt;br /&gt;
    var containsAny = function(name/* word1, word2, etc. */) {&lt;br /&gt;
      for(var arg = 1; arg &amp;lt; arguments.length; ++ arg) {&lt;br /&gt;
        var word = arguments[arg];&lt;br /&gt;
        if (name.includes(word)) return true;&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var containsAnyNumber = function(name, number, suffix) {&lt;br /&gt;
      return containsAny.call(null, name, &lt;br /&gt;
       number + suffix,&lt;br /&gt;
       &amp;#039;level &amp;#039; + number,&lt;br /&gt;
       &amp;#039;level: &amp;#039; + number,&lt;br /&gt;
       &amp;#039;floor &amp;#039; + number,&lt;br /&gt;
       &amp;#039;floor: &amp;#039; + number&lt;br /&gt;
       );&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var getZoneLevelData = function(zone, text) {&lt;br /&gt;
      var levels = zone.levels;&lt;br /&gt;
      // Check for part &amp;quot;level&amp;quot; aliases (eg. &amp;quot;1st floor&amp;quot; vs. &amp;quot;Level One&amp;quot;)&lt;br /&gt;
      text = text.toLowerCase();&lt;br /&gt;
      if(levels[&amp;#039;3&amp;#039;]  &amp;amp;&amp;amp; levels[&amp;#039;3&amp;#039;].image.includes(&amp;#039;crystal&amp;#039;)) {&lt;br /&gt;
        // Special case for Crystal Caverns&lt;br /&gt;
        if (containsAny(text, &amp;#039;upper&amp;#039;)) return levels[3];&lt;br /&gt;
        if (containsAny(text, &amp;#039;lower&amp;#039;)) return levels[2];&lt;br /&gt;
        if (containsAny(text, &amp;#039;town&amp;#039;, &amp;#039;coldain&amp;#039;)) return levels[0];&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // In most zones &amp;quot;tunnel&amp;quot; = level 0.  But in Kurn&amp;#039;s there are two level zeroes (tunnels and basement),&lt;br /&gt;
      // so this next line handles the Kurn&amp;#039;s case in particular&lt;br /&gt;
      if (containsAny(text, &amp;#039;tunnel&amp;#039;) &amp;amp;&amp;amp; levels.tunnels) return levels.tunnels;&lt;br /&gt;
&lt;br /&gt;
      if (containsAnyNumber(text, 0, &amp;#039;th&amp;#039;) || containsAny(text, &amp;#039;basement&amp;#039;, &amp;#039;underground&amp;#039;, &amp;#039;tunnel&amp;#039;, &amp;#039;bear pit&amp;#039;, &amp;#039;bear pits&amp;#039;, &amp;#039;cave&amp;#039;, &amp;#039;caves&amp;#039;)) return levels[0];&lt;br /&gt;
      if (containsAnyNumber(text, 1, &amp;#039;st&amp;#039;) || containsAny(text, &amp;#039;one&amp;#039;, &amp;#039;first&amp;#039;, &amp;#039;ground&amp;#039;)) return levels[1];&lt;br /&gt;
      if (containsAnyNumber(text, 2, &amp;#039;nd&amp;#039;) || containsAny(text, &amp;#039;two&amp;#039;, &amp;#039;second&amp;#039;)) return levels[2];&lt;br /&gt;
      if (containsAnyNumber(text, 3, &amp;#039;rd&amp;#039;) || containsAny(text, &amp;#039;three&amp;#039;, &amp;#039;third&amp;#039;)) return levels[3];&lt;br /&gt;
      if (containsAnyNumber(text, 4, &amp;#039;th&amp;#039;) || containsAny(text, &amp;#039;four&amp;#039;, &amp;#039;fourth&amp;#039;)) return levels[4];&lt;br /&gt;
      if (containsAnyNumber(text, 5, &amp;#039;th&amp;#039;) || containsAny(text, &amp;#039;five&amp;#039;, &amp;#039;fifth&amp;#039;)) return levels[5];&lt;br /&gt;
&lt;br /&gt;
      // Special case for Tower of Frozen Shadow, which has two level 6 maps&lt;br /&gt;
      if (containsAny(text, &amp;#039;6a&amp;#039;)) return levels[&amp;#039;6A&amp;#039;];&lt;br /&gt;
      if (containsAny(text, &amp;#039;6b&amp;#039;)) return levels[&amp;#039;6B&amp;#039;];&lt;br /&gt;
&lt;br /&gt;
      if (containsAnyNumber(text, 6, &amp;#039;th&amp;#039;) || containsAny(text, &amp;#039;six&amp;#039;, &amp;#039;sixth&amp;#039;)) return levels[6];&lt;br /&gt;
      if (containsAnyNumber(text, 7, &amp;#039;th&amp;#039;) || containsAny(text, &amp;#039;seven&amp;#039;, &amp;#039;seventh&amp;#039;)) return levels[7];&lt;br /&gt;
      if (containsAnyNumber(text, 8, &amp;#039;th&amp;#039;) || containsAny(text, &amp;#039;eight&amp;#039;, &amp;#039;eighth&amp;#039;)) return levels[8];&lt;br /&gt;
      if (containsAnyNumber(text, 9, &amp;#039;th&amp;#039;) || containsAny(text, &amp;#039;nine&amp;#039;, &amp;#039;ninth&amp;#039;)) return levels[9];&lt;br /&gt;
&lt;br /&gt;
      var levelKeys = Object.keys(levels);&lt;br /&gt;
      var keyOfLastLevel = levelKeys[levelKeys.length - 1];&lt;br /&gt;
      if (containsAny(text, &amp;#039;top&amp;#039;)) return levels[keyOfLastLevel];&lt;br /&gt;
&lt;br /&gt;
      // If we still couldn&amp;#039;t match, and there is a ground/1st floor, use it&lt;br /&gt;
      return levels[1];&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    var findZoneData = function(zoneName, locs, nonLocParts) {&lt;br /&gt;
      // Convert someZone (East) to East someZone&lt;br /&gt;
      if (zoneName.trim().endsWith(&amp;#039;)&amp;#039;)) {&lt;br /&gt;
        try {&lt;br /&gt;
          var nameSplit = zoneName.split(&amp;#039;(&amp;#039;);&lt;br /&gt;
          var newName = nameSplit[0].trim();&lt;br /&gt;
          var direction = nameSplit[1].split(&amp;#039;)&amp;#039;)[0].trim();&lt;br /&gt;
          if ([&amp;#039;east&amp;#039;, &amp;#039;north&amp;#039;, &amp;#039;south&amp;#039;, &amp;#039;west&amp;#039;].includes(direction.toLowerCase())) {&lt;br /&gt;
            zoneName = direction + &amp;#039; &amp;#039; + newName;&lt;br /&gt;
          }&lt;br /&gt;
        } catch(err){ /* just use name (it likely won&amp;#039;t work, but try) */}&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      var zone = zoneData[zoneName];&lt;br /&gt;
&lt;br /&gt;
      // Handle aliases (eg. &amp;quot;North Ro&amp;quot; instead of &amp;quot;Northern Desert of Ro&amp;quot;)&lt;br /&gt;
      if (typeof(zone) === &amp;#039;string&amp;#039;) zone = zoneData[zone];&lt;br /&gt;
&lt;br /&gt;
      if (!zone) return null;&lt;br /&gt;
&lt;br /&gt;
      // Handle Multi-Level Zones (with different maps for 1st, 2nd, etc. floor)&lt;br /&gt;
      if (zone.levels) {&lt;br /&gt;
        var levelOfZone = getZoneLevelData(zone, nonLocParts);&lt;br /&gt;
        return levelOfZone;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // Handle Zones with alternate maps on the same level (eg. Kelethin in GFay)&lt;br /&gt;
      if (!zone.alternateMaps) return zone;&lt;br /&gt;
&lt;br /&gt;
      for (var i = 0; i &amp;lt; zone.alternateMaps.length; i++) {&lt;br /&gt;
        var alternateData = zone.alternateMaps[i];&lt;br /&gt;
        var allLocsAreWithin = true;&lt;br /&gt;
        // wish I had ES6 [].every&lt;br /&gt;
        $.each(locs, function(i, loc) {&lt;br /&gt;
          allLocsAreWithin = allLocsAreWithin &amp;amp;&amp;amp; &lt;br /&gt;
          locIsWithinAlternateData(loc, alternateData);&lt;br /&gt;
        });&lt;br /&gt;
        if (allLocsAreWithin) return alternateData;&lt;br /&gt;
      };&lt;br /&gt;
      return zone;&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
  var addImageUrl = function(zoneData) {&lt;br /&gt;
    zoneData.imageUrl = &amp;#039;/images/&amp;#039; + zoneData.image;&lt;br /&gt;
    return zoneData;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var getZoneData = function(zoneName, locs, nonLocParts) {&lt;br /&gt;
    var zoneData = findZoneData(zoneName, locs, nonLocParts);&lt;br /&gt;
    if (!zoneData) return null;&lt;br /&gt;
    zoneData.zoneName = zoneName;&lt;br /&gt;
    return addImageUrl(zoneData);&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
    var build$MapImage = function(zoneData) {&lt;br /&gt;
      return $(&lt;br /&gt;
        &amp;#039;&amp;lt;img alt=&amp;quot;Map of &amp;#039; + zoneData.zoneName + &amp;#039;&amp;quot; &amp;#039; +&lt;br /&gt;
        &amp;#039;     class=&amp;quot;thumbborder&amp;quot; &amp;#039; +&lt;br /&gt;
        &amp;#039;     height=&amp;quot;&amp;#039;+ zoneData.height + &amp;#039;&amp;quot; &amp;#039; +&lt;br /&gt;
        &amp;#039;     src=&amp;quot;&amp;#039; + zoneData.imageUrl + &amp;#039;&amp;quot; &amp;#039; +&lt;br /&gt;
        &amp;#039;     title=&amp;quot;Map of &amp;#039; + zoneData.zoneName + &amp;#039;&amp;quot;&amp;#039; +&lt;br /&gt;
        &amp;#039;     width=&amp;quot;&amp;#039; + zoneData.width + &amp;#039;&amp;quot; &amp;#039; +&lt;br /&gt;
        &amp;#039;&amp;gt;&amp;#039;&lt;br /&gt;
        );&lt;br /&gt;
    };``&lt;br /&gt;
&lt;br /&gt;
    var build$XContainer = function() {&lt;br /&gt;
      return $(&amp;#039;&amp;lt;div class=&amp;quot;x-container&amp;quot; style=&amp;quot;position: relative&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    var build$LightboxFramedMap = function(zoneData) {&lt;br /&gt;
      var $map = build$XContainer().append(build$MapImage(zoneData));&lt;br /&gt;
      return addFramingStyles($map, zoneData.width, zoneData.height)&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var addFramingStyles = function($el, width, height) {&lt;br /&gt;
      return $el.css({&lt;br /&gt;
          left: &amp;#039;50%&amp;#039;,&lt;br /&gt;
          marginLeft: &amp;#039;-&amp;#039; + (width / 2) + &amp;#039;px&amp;#039;, // centering&lt;br /&gt;
          marginTop: &amp;#039;-&amp;#039; + (height / 2) + &amp;#039;px&amp;#039;,&lt;br /&gt;
          opacity: 1,&lt;br /&gt;
          top: &amp;#039;50%&amp;#039;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var build$AbsolutePositionFrame = function() {&lt;br /&gt;
      return $(&amp;#039;&amp;lt;div style=&amp;quot;position: absolute&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;#039;);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var removeOpenImage = function(e) {&lt;br /&gt;
      // Do nothing if there&amp;#039;s no open map&lt;br /&gt;
      if (!window.$openImg) return false;&lt;br /&gt;
&lt;br /&gt;
      // Do nothing if the user clicked to open a map, then moused out of an area&lt;br /&gt;
      // (otherwise it can close immediately)&lt;br /&gt;
      var position = window.$openImg.css(&amp;#039;position&amp;#039;);&lt;br /&gt;
      if (e &amp;amp;&amp;amp; e.type === &amp;#039;mouseleave&amp;#039; &amp;amp;&amp;amp; position === &amp;#039;fixed&amp;#039;) return false;&lt;br /&gt;
&lt;br /&gt;
      window.$openImg.remove();&lt;br /&gt;
      return false;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    var build$FullScreenFrame = function() {&lt;br /&gt;
      return build$AbsolutePositionFrame()&lt;br /&gt;
        .css({&lt;br /&gt;
          background: &amp;#039;rgba(0,0,0,0.8)&amp;#039;,&lt;br /&gt;
          height: &amp;#039;100%&amp;#039;,&lt;br /&gt;
          left: 0,&lt;br /&gt;
          position: &amp;#039;fixed&amp;#039;,&lt;br /&gt;
          top: 0,&lt;br /&gt;
          width: &amp;#039;100%&amp;#039;,&lt;br /&gt;
          zIndex: 4 // one higher than #p-search&amp;#039;s 3&lt;br /&gt;
        })&lt;br /&gt;
        .click(removeOpenImage);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var build$FullMap = function(zoneData) {&lt;br /&gt;
      var $frame = build$FullScreenFrame();&lt;br /&gt;
      if (zoneData) {&lt;br /&gt;
        $frame.html(build$LightboxFramedMap(zoneData));&lt;br /&gt;
        $frame.zoneData = zoneData;&lt;br /&gt;
      }&lt;br /&gt;
      return $frame;&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var build$SmallMap = function(zoneData) {&lt;br /&gt;
      return build$AbsolutePositionFrame().append(&lt;br /&gt;
        build$XContainer().append(&lt;br /&gt;
          build$MapImage(zoneData)));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
  var baseXFontSizeInEm = 2;&lt;br /&gt;
&lt;br /&gt;
  var buildX = function(left, top, sizeInEm) {&lt;br /&gt;
    sizeInEm = sizeInEm || baseXFontSizeInEm;&lt;br /&gt;
    return $(&amp;#039;&amp;lt;div class=&amp;quot;x&amp;quot;&amp;gt;x&amp;lt;/div&amp;gt;&amp;#039;)&lt;br /&gt;
      .css({&lt;br /&gt;
        color: &amp;#039;red&amp;#039;,&lt;br /&gt;
        fontSize: sizeInEm + &amp;#039;em&amp;#039;,&lt;br /&gt;
        fontWeight: &amp;#039;bold&amp;#039;,&lt;br /&gt;
        left: left,&lt;br /&gt;
        position: &amp;#039;absolute&amp;#039;,&lt;br /&gt;
        top: top&lt;br /&gt;
      })&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /**&lt;br /&gt;
   * Draws a red &amp;quot;X&amp;quot; on the map at the provided coordinate&lt;br /&gt;
   */&lt;br /&gt;
  var addX = function($xContainer, zoneData, x, y, xSize) {&lt;br /&gt;
    var left = (zoneData.zeroX || 0) + x * -1 * (zoneData.zoomX || 0.1);&lt;br /&gt;
    var top = (zoneData.zeroY || 0) + y * -1 * (zoneData.zoomY || 0.1);&lt;br /&gt;
    $xContainer.append(buildX(left, top, xSize));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  var addXs = function($xContainer, zoneData, locs, xSize) {&lt;br /&gt;
    $.each(locs, function(i, loc) {&lt;br /&gt;
      addX($xContainer, zoneData, loc.x, loc.y, xSize);&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  var parseLoc = function(locText) {&lt;br /&gt;
    if (typeof locText !== &amp;#039;string&amp;#039;) return locText;&lt;br /&gt;
    try {&lt;br /&gt;
        var match = locText.match(/\(? *([\+\-]?\d+\.?\d*)\s*, *([\+\-]?\d+\.?\d*)\)?/);&lt;br /&gt;
        return {x: parseFloat(match[2]), y: parseFloat(match[1]) };&lt;br /&gt;
    } catch (err) {&lt;br /&gt;
        return locText;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  var isLoc = function(locBit) {&lt;br /&gt;
      // If we can&amp;#039;t split the string by its comma and find a number on either side, it&amp;#039;s not a loc&lt;br /&gt;
      try {&lt;br /&gt;
        return locBit.split(&amp;#039;,&amp;#039;)[0].match(/\d+/) &amp;amp;&amp;amp; locBit.split(&amp;#039;,&amp;#039;)[1].match(/\d+/);&lt;br /&gt;
      } catch (err) {&lt;br /&gt;
        return false;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Helper function to power parseLocs/extractNonLocText, since they both use&lt;br /&gt;
     * the same logic.&lt;br /&gt;
     *&lt;br /&gt;
     * SIDE NOTE: If only the wiki didn&amp;#039;t have to use 1999 JS, and could&lt;br /&gt;
     *            destructure return values with ES2015, the two related functions&lt;br /&gt;
     *            wouldn&amp;#039;t even need to exist :(&lt;br /&gt;
     */&lt;br /&gt;
    var parseLocString = function(locString) {&lt;br /&gt;
      var nonLocParts = &amp;#039;&amp;#039;;&lt;br /&gt;
      // Old regex (failed to match Undead Legionaires loc on TT page, but keeping it around unless&lt;br /&gt;
      // the new regex has issues&lt;br /&gt;
      //var bits = locString.split(/([\+\-]?\d+\.?\d*[^0-9\)]*,\D*[\+\-]?\d+\.?\d*)/g);&lt;br /&gt;
      var bits = locString.split(/([\+\-]?\d+\.?\d*\s*,\D*[\+\-]?\d+\.?\d*)/g);&lt;br /&gt;
      var relevantBits = bits.filter(function(part) {&lt;br /&gt;
        // Filter out the non-loc parts, but save them&lt;br /&gt;
        if (isLoc(part)) return true;&lt;br /&gt;
        nonLocParts += part; // save as it might be something like &amp;quot;1st floor&amp;quot;&lt;br /&gt;
      });&lt;br /&gt;
      var locs = relevantBits.map(parseLoc);&lt;br /&gt;
      return [locs, nonLocParts];&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Extracts all of the locs (objects with x and y properties) from a provided&lt;br /&gt;
     * string such as:&lt;br /&gt;
     *   &amp;quot;500, 200&amp;quot;&lt;br /&gt;
     *   &amp;quot;(200, 300), (300, 200)&amp;quot;&lt;br /&gt;
     * and returns an array containing any locs found.&lt;br /&gt;
     */&lt;br /&gt;
     var parseLocs = function(locString) {&lt;br /&gt;
      return parseLocString(locString)[0];&lt;br /&gt;
    };&lt;br /&gt;
    &lt;br /&gt;
    /**&lt;br /&gt;
     * Extracts the non-loc parts (eg. &amp;quot;Level 1&amp;quot;) from a provided string of text&lt;br /&gt;
     * (presumably from an NPC&amp;#039;s location &amp;lt;td&amp;gt; or somewhere similar). Although not&lt;br /&gt;
     * actually a part of the loc, this text may still be useful in determining&lt;br /&gt;
     * which map to show for the loc.&lt;br /&gt;
     */&lt;br /&gt;
    var extractNonLocText = function(locString) {&lt;br /&gt;
      return parseLocString(locString)[1];&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    var showImage = function($img, $container) {&lt;br /&gt;
      if ($container) {&lt;br /&gt;
        $img.css({ marginTop: 40 });&lt;br /&gt;
        var isAboveHalf = $container.position().top &amp;lt; ($(&amp;#039;body&amp;#039;).height() / 2);&lt;br /&gt;
        var isLeftOfHalf = $container.position().left &amp;lt; ($(&amp;#039;body&amp;#039;).width() / 2);&lt;br /&gt;
        $container.css(&amp;#039;position&amp;#039;, &amp;#039;relative&amp;#039;)&lt;br /&gt;
                  .prepend($img);&lt;br /&gt;
        $img.css({ &lt;br /&gt;
          [isAboveHalf  ? &amp;#039;top&amp;#039; : &amp;#039;bottom&amp;#039;]: &amp;#039;1.5em&amp;#039;, &lt;br /&gt;
          [isLeftOfHalf  ? &amp;#039;left&amp;#039; : &amp;#039;right&amp;#039;]: &amp;#039;1.5em&amp;#039; &lt;br /&gt;
        });&lt;br /&gt;
        // TODO: Now, check if the map image is off the page, and if so by how much?&lt;br /&gt;
        //       Add/subtract that much from the top/left/bottom/right to make sure &lt;br /&gt;
        //       the whole map is shown ... but also make sure the link itself isn&amp;#039;t covered&lt;br /&gt;
      }&lt;br /&gt;
      else $(&amp;#039;body&amp;#039;).append($img);&lt;br /&gt;
&lt;br /&gt;
      window.$openImg = $img;&lt;br /&gt;
      return $img;&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Adds a loc map image to the page.&lt;br /&gt;
     */&lt;br /&gt;
    var showMapWithLocs = function(zoneData, locs, $container) {&lt;br /&gt;
      removeOpenImage();&lt;br /&gt;
      &lt;br /&gt;
      zoneData = addImageUrl(zoneData);&lt;br /&gt;
      var $map = $container ? build$SmallMap(zoneData) : build$FullMap(zoneData);&lt;br /&gt;
&lt;br /&gt;
      var $xContainer = $map.find(&amp;#039;.x-container&amp;#039;);&lt;br /&gt;
      var xSize = baseXFontSizeInEm;&lt;br /&gt;
      if (locs.length &amp;gt; 5) xSize = 0.8 * baseXFontSizeInEm;&lt;br /&gt;
      if (locs.length &amp;gt; 15) xSize = 0.5 * baseXFontSizeInEm;&lt;br /&gt;
      $map.find(&amp;#039;img&amp;#039;).load(function() {&lt;br /&gt;
        addXs($xContainer, zoneData, locs, xSize);&lt;br /&gt;
      });&lt;br /&gt;
      return showImage($map, $container);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var mapIconHtml = &amp;#039;&amp;lt;img &amp;#039; +&lt;br /&gt;
      &amp;#039;alt=&amp;quot;Map Icon.png&amp;quot; &amp;#039; +&lt;br /&gt;
      &amp;#039;height=&amp;quot;12&amp;quot; &amp;#039; +&lt;br /&gt;
      &amp;#039;src=&amp;quot;/images/thumb/Map_Icon.png/12px-Map_Icon.png&amp;quot; &amp;#039; +&lt;br /&gt;
      &amp;#039;width=&amp;quot;12&amp;quot;&amp;gt;&amp;#039;;&lt;br /&gt;
    var addLocLinksToTable = function($table, sharedZone, zoneColumnIndex) {&lt;br /&gt;
      var areaColumnIndex = $table.find(&amp;#039;th:contains(&amp;quot;Area&amp;quot;), th:contains(&amp;quot;Location&amp;quot;)&amp;#039;).index();&lt;br /&gt;
      var locationColumnIndex =&lt;br /&gt;
        $table.find(&amp;#039;th:contains(&amp;quot;Location&amp;quot;), th:contains(&amp;quot;Loc&amp;quot;)&amp;#039;).index();&lt;br /&gt;
      &lt;br /&gt;
&lt;br /&gt;
      $table.find(&amp;#039;tr&amp;#039;).each(function(i, tr) {&lt;br /&gt;
        var $tr = $(tr);&lt;br /&gt;
        var $loc = $tr.find(&amp;#039;td:eq(&amp;#039; + locationColumnIndex + &amp;#039;)&amp;#039;);&lt;br /&gt;
        var loc = $loc.text().trim();&lt;br /&gt;
        if (!isLoc(loc)) return; // Non-loc text (eg. &amp;quot;Various&amp;quot;) in loc column&lt;br /&gt;
        &lt;br /&gt;
        // Include the area in case it contains relevant non-loc location info&lt;br /&gt;
        // (such as a floor number)&lt;br /&gt;
        var nonLoc = $tr.find(&amp;#039;td:eq(&amp;#039; + areaColumnIndex + &amp;#039;)&amp;#039;).text().trim();&lt;br /&gt;
        var zone = sharedZone ||&lt;br /&gt;
                   $tr.find(&amp;#039;td:eq(&amp;#039; + zoneColumnIndex + &amp;#039;)&amp;#039;).text().trim();&lt;br /&gt;
                   var $locLink = $(&amp;#039;&amp;lt;a class=&amp;quot;loc-link&amp;quot; href=&amp;quot;&amp;#039; + zone + &amp;#039;&amp;quot; &amp;#039; +&lt;br /&gt;
                   &amp;#039;   data-loc=&amp;quot;&amp;#039; + nonLoc + &amp;#039;, &amp;#039; + loc + &amp;#039;&amp;quot; &amp;#039; +&lt;br /&gt;
                   &amp;#039;   data-zone=&amp;quot;&amp;#039; + zone + &amp;#039;&amp;quot;&amp;#039; +&lt;br /&gt;
                   &amp;#039;&amp;gt;&amp;#039; + loc + &amp;#039;&amp;lt;/a&amp;gt;&amp;#039;);&lt;br /&gt;
        $loc.html($locLink);&lt;br /&gt;
        // If we actually have zone data, add the map icon&lt;br /&gt;
        if (getZoneData(zone, loc, nonLoc)) $loc.append(mapIconHtml);&lt;br /&gt;
      });&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Handle &amp;quot;Where to obtain&amp;quot; rows for spells&lt;br /&gt;
    var handleWhereToObtainRows = function() {&lt;br /&gt;
      $(&amp;#039;table&amp;#039;+&lt;br /&gt;
        &amp;#039;:has(th:contains(&amp;quot;Area&amp;quot;))&amp;#039; +&lt;br /&gt;
        &amp;#039;:has(th:contains(&amp;quot;Location&amp;quot;))&amp;#039;+&lt;br /&gt;
        &amp;#039;:has(th:contains(&amp;quot;Loc&amp;quot;))&amp;#039; +&lt;br /&gt;
        &amp;#039;:has(th:contains(&amp;quot;Zone&amp;quot;))&amp;#039;+&lt;br /&gt;
        &amp;#039;:not(.zoneTopTable)&amp;#039; +&lt;br /&gt;
        &amp;#039;:not(:has(th:contains(&amp;quot;Zone Spawn Timer:&amp;quot;)))&amp;#039;&lt;br /&gt;
      )&lt;br /&gt;
        .each(function(i, table) {&lt;br /&gt;
          var $table = $(table);&lt;br /&gt;
          var zoneColumnIndex = $table.find(&amp;#039;th:contains(&amp;quot;Zone&amp;quot;)&amp;#039;).index();&lt;br /&gt;
          addLocLinksToTable($table, null, zoneColumnIndex);&lt;br /&gt;
        });&lt;br /&gt;
    };&lt;br /&gt;
    &lt;br /&gt;
    // Handle Zone NPC Locs&lt;br /&gt;
    var handleZoneNPCRows = function() {&lt;br /&gt;
      var $npcsInZone = $(&amp;#039;i:contains(&amp;quot;NPCs that spawn in&amp;quot;)&amp;#039;);&lt;br /&gt;
      if ($npcsInZone.length) {&lt;br /&gt;
        var match = $npcsInZone.text().match(/\d+ NPCs that spawn in (.*):/);&lt;br /&gt;
        if (!match) return;&lt;br /&gt;
&lt;br /&gt;
        var zone = match[1];&lt;br /&gt;
        var $table = $npcsInZone.parent().next()&lt;br /&gt;
        addLocLinksToTable($table, zone);&lt;br /&gt;
      }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var getZoneName = function() {&lt;br /&gt;
      try {&lt;br /&gt;
        return $(&amp;#039;b:contains(&amp;quot;Zone:&amp;quot;)&amp;#039;).parents(&amp;#039;tr:first&amp;#039;).text().split(&amp;#039;Zone:&amp;#039;)[1].trim();&lt;br /&gt;
      } catch (err) {&lt;br /&gt;
        return null;&lt;br /&gt;
      };&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Determines whether the page has a &amp;quot;Location&amp;quot; &amp;lt;td&amp;gt; (as all NPC pages do)&lt;br /&gt;
     * with parsable locs inside it (as only some do).  Also checks that&lt;br /&gt;
     * &amp;quot;loc-mapped&amp;quot; zone data exists for the NPC&amp;#039;s zone.&lt;br /&gt;
     */&lt;br /&gt;
    var getZoneDataIfThereIsALocBox = function($locTd, zoneName) {&lt;br /&gt;
      if (!$locTd.length) return false; // page isn&amp;#039;t an NPC page (no loc box)&lt;br /&gt;
      &lt;br /&gt;
      var locs = parseLocs($locTd.text());&lt;br /&gt;
      if (!locs.length) return false; // no locs existed (or could be parsed)&lt;br /&gt;
&lt;br /&gt;
      var nonLocParts = extractNonLocText($locTd.text());&lt;br /&gt;
      return !!getZoneData(zoneName, locs, nonLocParts); // does zone data exist?&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var handleLocBoxes = function() {&lt;br /&gt;
      var zoneName = getZoneName();&lt;br /&gt;
      var $bold = $(&amp;#039;b:contains(&amp;quot;Location:&amp;quot;), b:contains(&amp;quot;Loc:&amp;quot;)&amp;#039;).not(&amp;#039;.zoneTopTable b, .toc b&amp;#039;);&lt;br /&gt;
      if(!$bold.length) return;&lt;br /&gt;
&lt;br /&gt;
      var label = $bold.text().trim();&lt;br /&gt;
      var labelNoColon = label.substr(0, label.length - 1);&lt;br /&gt;
&lt;br /&gt;
      var $locTd = $bold.parent();&lt;br /&gt;
      // The TD can either have the label and the locs, or there could&lt;br /&gt;
      // be one TD for the label, and a sibling for the loc&lt;br /&gt;
      var isLabelTd = $locTd.text().trim().endsWith(&amp;#039;:&amp;#039;);&lt;br /&gt;
      if (isLabelTd) $locTd = $locTd.closest(&amp;#039;tr&amp;#039;).children(&amp;#039;td:last&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
      var zoneData = getZoneDataIfThereIsALocBox($locTd, zoneName);&lt;br /&gt;
      if (!zoneData) return;&lt;br /&gt;
      &lt;br /&gt;
      // Get the mob&amp;#039;s loc(s)&lt;br /&gt;
      var locs = parseLocs($locTd.text());&lt;br /&gt;
      var nonLocParts = extractNonLocText($locTd.text());&lt;br /&gt;
      &lt;br /&gt;
      // Do we have data for that zone&amp;#039;s map?&lt;br /&gt;
      var zoneData = getZoneData(zoneName, locs, nonLocParts);&lt;br /&gt;
      if (!zoneData) return;  // If not, stop here&lt;br /&gt;
&lt;br /&gt;
      // Add the mouse-enter link&lt;br /&gt;
      var $link = $(&amp;#039; &amp;lt;a href=&amp;quot;#&amp;quot;&amp;gt;(Map)&amp;lt;/a&amp;gt;&amp;#039;)&lt;br /&gt;
        // When it&amp;#039;s moused-over, show the map nearby&lt;br /&gt;
        .on(&amp;#039;mouseenter&amp;#039;, function(e) {&lt;br /&gt;
          // If there is already a map &amp;quot;open&amp;quot; (because of a click), do nothing&lt;br /&gt;
          if (window.$openImg &amp;amp;&amp;amp; window.$openImg.parent().length) return false;&lt;br /&gt;
&lt;br /&gt;
          var $map = showMapWithLocs(zoneData, locs, $locTd);&lt;br /&gt;
          $map.parent().one(&amp;#039;mouseleave&amp;#039;, removeOpenImage);&lt;br /&gt;
        })&lt;br /&gt;
        // When it&amp;#039;s clicked, show the map full-screen/lightbox style&lt;br /&gt;
        .on(&amp;#039;click&amp;#039;, function(e) {&lt;br /&gt;
          showMapWithLocs(zoneData, locs);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
      $bold&lt;br /&gt;
        .html($(&amp;#039;&amp;lt;span&amp;gt;&amp;#039; + labelNoColon + &amp;#039; &amp;lt;/span&amp;gt;&amp;#039;)&lt;br /&gt;
        .append($link)&lt;br /&gt;
        .append(&amp;#039;&amp;lt;span&amp;gt;:&amp;lt;/span&amp;gt;&amp;#039;));&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Setup event listeners (code &amp;quot;starts&amp;quot; here)&lt;br /&gt;
    var startLocFunctionality = function() {&lt;br /&gt;
      handleWhereToObtainRows();&lt;br /&gt;
      handleZoneNPCRows();&lt;br /&gt;
      handleLocBoxes();&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Sometimes zoneData isn&amp;#039;t ready when this code is, so retry for 10 secs in hope that it becomes available&lt;br /&gt;
    var tries = 10;&lt;br /&gt;
    var timeBetweenTries = 1000; // 1 second&lt;br /&gt;
    var interval = window.setInterval(function() {&lt;br /&gt;
      if (window.zoneData) {&lt;br /&gt;
         window.clearInterval(interval);&lt;br /&gt;
         startLocFunctionality();&lt;br /&gt;
      } else {&lt;br /&gt;
        tries -= 1;&lt;br /&gt;
        if (!tries) window.clearInterval(interval); // Failure :(&lt;br /&gt;
      }&lt;br /&gt;
    }, timeBetweenTries);&lt;br /&gt;
&lt;br /&gt;
    var showMapForData = function(data, $target) {&lt;br /&gt;
      if (!data.loc || ! data.zone) return;&lt;br /&gt;
&lt;br /&gt;
      var locs = parseLocString(data.loc)[0];&lt;br /&gt;
      var nonLocParts = parseLocString(data.loc)[1];&lt;br /&gt;
      var zoneData = getZoneData(data.zone, locs, nonLocParts);&lt;br /&gt;
      if (!zoneData) {&lt;br /&gt;
        // If it was a click and not a mouseover&lt;br /&gt;
        if ($target) return false;&lt;br /&gt;
&lt;br /&gt;
        return confirm(&amp;#039;We\&amp;#039;re sorry, but &amp;#039; + data.zone + &amp;#039; has not been loc mapped yet ... &amp;#039; +&lt;br /&gt;
          &amp;#039;please see &amp;quot;/Loc_Maps&amp;quot; for more information.  You can now click &amp;quot;OK&amp;quot; to be taken &amp;#039; +&lt;br /&gt;
          &amp;#039;to the zone\&amp;#039;s page instead, where you can use its map as a guide, or &amp;quot;Cancel&amp;quot; to stay here.&amp;#039;); &lt;br /&gt;
      }&lt;br /&gt;
      showMapWithLocs(zoneData, locs, $target);&lt;br /&gt;
    };&lt;br /&gt;
   // TODO: Add a new class/event handler/template to trigger this&lt;br /&gt;
   var addCharacterToMap = function(data, $target) {&lt;br /&gt;
      if (!data.loc || ! data.zone) return;&lt;br /&gt;
&lt;br /&gt;
      var locs = parseLocString(data.loc)[0];&lt;br /&gt;
      var nonLocParts = parseLocString(data.loc)[1];&lt;br /&gt;
      var zoneData = getZoneData(data.zone, locs, nonLocParts);&lt;br /&gt;
      if (!zoneData) {&lt;br /&gt;
        // If it was a click and not a mouseover&lt;br /&gt;
        if ($target) return false;&lt;br /&gt;
&lt;br /&gt;
        alertNoData(data.zone);&lt;br /&gt;
        return true;&lt;br /&gt;
      }&lt;br /&gt;
      // TODO: do next line, but replace $target with a query for the existing map on the page&lt;br /&gt;
      // Also, instead of making an X, make a number that&amp;#039;s provided (via link&amp;#039;s data attr)&lt;br /&gt;
      // showMapWithLocs(zoneData, locs, $target);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // TODO: this is in here because fashion links and loc links share global&lt;br /&gt;
    // event watchers, but this file needs to be renamed now&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Shows an image (triggered by clicking on a link, such&lt;br /&gt;
     * as a thumbnail or a fashion show link, which contains&lt;br /&gt;
     * data about the file&amp;#039;s URL).&lt;br /&gt;
     */&lt;br /&gt;
    var showImageFromData = function(data, $container) {&lt;br /&gt;
       if (!data.file) return;&lt;br /&gt;
       var $img = $(buildImgFromFileName(data.file));&lt;br /&gt;
       var $div = $(&amp;#039;&amp;lt;div class=&amp;quot;framed-image&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;#039;)&lt;br /&gt;
                    .html($img)&lt;br /&gt;
       var $framedImage;&lt;br /&gt;
       if ($container) {&lt;br /&gt;
        var $outerFrame = build$AbsolutePositionFrame();&lt;br /&gt;
        var $innerFrame = $(&amp;#039;&amp;lt;div style=&amp;quot;position:relative&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;#039;);&lt;br /&gt;
         $framedImage = $outerFrame.append($innerFrame.append($div));&lt;br /&gt;
       } else {&lt;br /&gt;
         $framedImage = build$FullScreenFrame().html($div);&lt;br /&gt;
       }&lt;br /&gt;
       showImage($framedImage, $container);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Hook up loc-link events&lt;br /&gt;
    $(&amp;#039;body&amp;#039;)&lt;br /&gt;
      .on(&amp;#039;click&amp;#039;, &amp;#039;.loc-link, .fashion-link, .thumbnail-link&amp;#039;, function() {&lt;br /&gt;
        removeOpenImage(); // get rid of any mouseover images first&lt;br /&gt;
        var data = $(this).data();&lt;br /&gt;
        var theClickedLinkShouldRedirect = showMapForData(data) || showImageFromData(data);&lt;br /&gt;
        return theClickedLinkShouldRedirect || false;&lt;br /&gt;
      })&lt;br /&gt;
      // Temporarily (?) disable mouseover showing of images&lt;br /&gt;
      //.on(&amp;#039;mouseenter&amp;#039;, &amp;#039;.loc-link, .fashion-link, .thumbnail-link&amp;#039;, function(e) {&lt;br /&gt;
      //  var $this = $(e.target).on(&amp;#039;mouseleave&amp;#039;, removeOpenImage);&lt;br /&gt;
      //  var data = $(this).data();&lt;br /&gt;
      //  showMapForData(data, $this);&lt;br /&gt;
      //  showFashionForData(data, $this);&lt;br /&gt;
      //});&lt;br /&gt;
&lt;br /&gt;
      &lt;br /&gt;
    // *** HELPER FUNCTIONS ***&lt;br /&gt;
    // Define two helper functions for building new zone definitions&lt;br /&gt;
&lt;br /&gt;
    // 1) Use this function to find the correct 0,0 point&lt;br /&gt;
    window.testZero = window.testZeroZero = function(zoneData) {&lt;br /&gt;
      removeOpenImage(); // test functions don&amp;#039;t clean up properly&lt;br /&gt;
      var locs = [{ x: 0, y: 0 }];&lt;br /&gt;
      showMapWithLocs(zoneData, locs);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // 2) Use this function to generate a grid of alignment of X&amp;#039;s&lt;br /&gt;
    window.testGrid = function(zoneData) {&lt;br /&gt;
      removeOpenImage(); // test functions don&amp;#039;t clean up properly&lt;br /&gt;
      var $locTd = $(&amp;#039;b:contains(&amp;quot;Location:&amp;quot;)&amp;#039;).parent();&lt;br /&gt;
      var locs = [];&lt;br /&gt;
      for (var x = zoneData.maxX; x &amp;gt;= zoneData.minX; x -= zoneData.interval) {&lt;br /&gt;
        for (var y = zoneData.maxY; y &amp;gt;= zoneData.minY; y -= zoneData.interval) {&lt;br /&gt;
          locs.push({ x: x, y: y });&lt;br /&gt;
        }&lt;br /&gt;
      };&lt;br /&gt;
      showMapWithLocs(zoneData, locs);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
  })();&lt;br /&gt;
  } catch (err) {&lt;br /&gt;
    console.error(err); // If anything goes wrong, move on but log the error&lt;br /&gt;
  }&lt;/div&gt;</summary>
		<author><name>imported&gt;Loramin</name></author>
	</entry>
</feed>