This is a wiki for a reason. Anyone can contribute. If you see something that is inaccurate or can be improved, don't ask that it be fixed--just improve it.
[ Disclaimer, Create new user --- Wiki markup help, Install P99 ]

Difference between revisions of "MediaWiki:Zones.js"

From Project 1999 Wiki
Jump to: navigation, search
Line 1: Line 1:
try {
+
(function() {
  
window.zoneData = {
+
   // TODO: Add support for cropping maps that have multiple maps.
   'Ak\'Anon': {
+
   // For instance, the following CSS styles show only one of the three maps for
    height: 614,
+
   // Erudin Palace:
    image: 'Akanon.jpg',
+
   // background-image:url(/images/Erudinpalace.jpg);
    interval: 250,
+
  // background-position: 250px 0px; width: 275px; height: 272px
    maxX: 250,
+
  // Figure out how to make the Xs line up with those styles added
    maxY: 2250,
+
 
    minX: 1250,
+
   var locIsWithinAlternateData = function(loc, alternateData) {
    minY: 250,
+
     return loc.x < alternateData.maxX &&
    width: 384,
+
    loc.x > alternateData.minX &&
    zeroX: 62,
+
    loc.y < alternateData.maxY &&
    zeroY: 567,
+
    loc.y > alternateData.minY;
    zoomX: 0.25,
+
  };
    zoomY: 0.25
+
 
   },
+
 
   'Beholder\'s Maze': 'Gorge of King Xorbb',
+
  /**
   'Butcherblock Mountains': {
+
  * Determines if the provided name contains any of the provided words
    height: 480,
+
  */
    image: 'Butcherblock-v3.jpg',
+
  // TODO: Add a some polyfill to make this a lot simpler
    interval: 1000,
+
  var containsAny = function(name/* word1, word2, etc. */) {
    maxX: 3000,
+
    for(var arg = 1; arg < arguments.length; ++ arg) {
    maxY: 3000,
+
       var word = arguments[arg];
    minX: 3000,
+
       if (name.includes(word)) return true;
    minY: 3000,
+
    width: 500,
+
    zeroX: 251,
+
    zeroY: 240,
+
    zoomX: .073,
+
    zoomY: .0727
+
   },
+
  'Clan Runnyeye': {
+
     levels: {
+
      1: {
+
        height: 488,
+
        width: 431,
+
        image: 'Runnyeye1.jpg',
+
        zeroX: 204,
+
        zeroY: 347,
+
        interval: 100,
+
        maxX: 200,
+
        maxY: 400,
+
        minX: -200,
+
        minY: -100,
+
        zoomX: 0.85,
+
        zoomY: 0.85
+
      },
+
      2: {
+
        height: 594,
+
        width: 493,
+
        image: 'Runnyeye2.jpg',
+
        zeroX: 198.5,
+
        zeroY: 340,
+
        interval: 100,
+
        maxX: 200,
+
        maxY: 400,
+
        minX: -300,
+
        minY: -200,
+
        zoomX: 0.86,
+
        zoomY: 0.86
+
      },
+
      3: {
+
        height: 609,
+
        width: 415,
+
        image: 'Runnyeye3.jpg',
+
        zeroX: 207,
+
        zeroY: 359,
+
        interval: 100,
+
        maxX: 200,
+
        maxY: 400,
+
        minX: -200,
+
        minY: -200,
+
        zoomX: 0.86,
+
        zoomY: 0.85
+
       },
+
       4: {
+
        height: 569,
+
        width: 462,
+
        image: 'Runnyeye4.jpg',
+
        zeroX: 187.5,
+
        zeroY: 287,
+
        interval: 100,
+
        maxX: 200,
+
        maxY: 300,
+
        minX: -300,
+
        minY: -200,
+
        zoomX: 0.863,
+
        zoomY: 0.86
+
      },
+
 
     }
 
     }
   },
+
    return false;
   'Crypt of Dalnir': {
+
   };
     levels: {
+
 
      1: {
+
   var containsAnyNumber = function(name, number, suffix) {
        height: 332,
+
     return containsAny.call(null, name,  
        image: 'Map_dalnir1.jpg',  
+
    number + suffix,
        interval: 100,
+
    'level ' + number,
        maxX: 100,
+
    'level: ' + number
        maxY: 300,
+
    );
        minX: -800,
+
  };
        minY: -100,
+
 
        width: 582,
+
  var getZoneLevelData = function(levels, text) {
        zeroX: 72,
+
    // Check for part "level" aliases (eg. "1st floor" vs. "Level One")
        zeroY: 198,
+
    text = text.toLowerCase();
        zoomX: 0.59,
+
    if (containsAnyNumber(text, 0, 'th') || containsAny(text, 'basement', 'underground')) return levels[0];
        zoomY: 0.61
+
     if (containsAnyNumber(text, 1, 'st') || containsAny(text, 'one', 'first', 'ground')) return levels[1];
      },
+
     if (containsAnyNumber(text, 2, 'nd') || containsAny(text, 'two', 'second')) return levels[2];
      2: {
+
     if (containsAnyNumber(text, 3, 'rd') || containsAny(text, 'three', 'third')) return levels[3];
        height: 493,
+
     if (containsAnyNumber(text, 4, 'th') || containsAny(text, 'four', 'fourth')) return levels[4];
        image: 'Map_dalnir2.jpg',  
+
     if (containsAnyNumber(text, 5, 'th') || containsAny(text, 'five', 'fifth')) return levels[5];
        interval: 100,
+
     if (containsAnyNumber(text, 6, 'th') || containsAny(text, 'six', 'sixth')) return levels[6];
        maxX: -700,
+
     if (containsAnyNumber(text, 7, 'th') || containsAny(text, 'seven', 'seventh')) return levels[7];
        maxY: 300,
+
     if (containsAnyNumber(text, 8, 'th') || containsAny(text, 'eight', 'eighth')) return levels[8];
        minX: -1000,
+
     if (containsAnyNumber(text, 9, 'th') || containsAny(text, 'nine', 'ninth')) return levels[9];
        minY: 0,
+
 
        width: 462,
+
     // If we still couldn't match, and there is a ground/1st floor, use it
        zeroX: -693,
+
     return levels[1];
        zeroY: 375,
+
  };
        zoomX: 1.072,
+
 
        zoomY: 1.07
+
 
      },
+
 
      3: {
+
  var findZoneData = function(zoneName, locs, nonLocParts) {
        height: 471,
+
     var zone = zoneData[zoneName];
        image: 'Map_dalnir3.jpg',
+
 
        interval: 100,
+
   // Handle aliases (eg. "North Ro" instead of "Northern Desert of Ro")
        maxX: 0,
+
   if (typeof(zone) === 'string') zone = zoneData[zone];
        maxY: 400,
+
 
        minX: -800,
+
  if (!zone) return null;
        minY: -300,
+
 
        width: 531,
+
  // Handle Multi-Level Zones (with different maps for 1st, 2nd, etc. floor)
        zeroX: 44,
+
  if (zone.levels) return getZoneLevelData(zone.levels, nonLocParts);
        zeroY: 253,
+
 
        zoomX: 0.55,
+
   // Handle Zones with alternate maps on the same level (eg. Kelethin in GFay)
        zoomY: 0.55
+
   if (!zone.alternateMaps) return zone;
      },
+
 
     },
+
  for (var i = 0; i < zone.alternateMaps.length; i++) {
  },
+
     var alternateData = zone.alternateMaps[i];
  'Dalnir': 'Crypt of Dalnir',
+
     var allLocsAreWithin = true;
  'East Commonlands': {
+
     // wish I had ES6 [].every
    height: 279,
+
     $.each(locs, function(i, loc) {
    image: 'Map_eastcommons.jpg',
+
      allLocsAreWithin = allLocsAreWithin &&
     interval: 1000,
+
      locIsWithinAlternateData(loc, alternateData);
    maxX: 5000,
+
     });
    maxY: 2000,
+
     if (allLocsAreWithin) return alternateData;
    minX: 2000,
+
  };
     minY: 2000,
+
  return zone;
    width: 642,
+
};
    zeroX: 469.3,
+
 
    zeroY: 133,
+
var addImageUrl = function(zoneData) {
    zoomX: 0.0887,
+
   zoneData.imageUrl = '/images/' + zoneData.image;
    zoomY: 0.0887
+
  return zoneData;
  },
+
};
  'Eastern Plains of Karana': {
+
 
    height: 554,
+
var getZoneData = function(zoneName, locs, nonLocParts) {
    image: 'Map_ekarana.jpg',
+
  var zoneData = findZoneData(zoneName, locs, nonLocParts);
     interval: 1000,
+
  if (!zoneData) return null;
    maxX: 2000,
+
  zoneData.zoneName = zoneName;
    maxY: 2000,
+
  return addImageUrl(zoneData);
    minX: 4000,
+
};
     minY: 5000,
+
 
    width: 450,
+
   /**
    zeroX: 119.5,
+
  * This basic function serves as the click handler for "lightbox-style" maps
    zeroY: 193,
+
  */
    zoomX: 0.0793,
+
  var removeSelf = function() {
    zoomY: 0.0788
+
     $(this).remove();
  },
+
  };
  'Eastern Wastes': {
+
 
    height: 452,
+
 
    image: 'Map_eastern_wastes.jpg',
+
   var build$MapImage = function(zoneData) {
     interval: 1000,
+
    return $(
    maxX: 7000,
+
      '<img alt="Map of ' + zoneData.zoneName + '" ' +
    maxY: 1000,
+
      '     class="thumbborder" ' +
    minX: 6000,
+
      '    height="'+ zoneData.height + '" ' +
    minY: 9000,
+
      '     src="' + zoneData.imageUrl + '" ' +
    width: 550,
+
      '     title="Map of ' + zoneData.zoneName + '"' +
    zeroX: 284,
+
      '     width="' + zoneData.width + '" ' +
    zeroY: 62,
+
      '>'
    zoomX: 0.038,
+
      );
    zoomY: 0.038
+
   };``
  },
+
 
  'Erudin': {
+
 
    alternateMaps: [
+
   var build$WrappedMap = function(zoneData) {
      {
+
     return $('<div class="x-container"></div>')
        height: 564,
+
      .css({ position: 'relative' })
        image: 'Erudindocks.jpg',
+
      .append(build$MapImage(zoneData));
        interval: 100,
+
   }
        maxX: 300,
+
 
        maxY: 200,
+
 
        minX: -400,
+
   var build$LightboxFramedMap = function(zoneData) {
        minY: -400,
+
     return build$WrappedMap(zoneData)
        width: 450,
+
       .css({
        zeroX: 122,
+
         left: '50%',
        zeroY: 223,
+
         marginLeft: '-' + (zoneData.width / 2) + 'px', // centering
        zoomX: 0.731,
+
         marginTop: '-' + (zoneData.height / 2) + 'px',
        zoomY: 0.731
+
         opacity: 1,
      }
+
         top: '50%'
    ],
+
      });
     height: 663,
+
  };
    image: 'Erudin.jpg',
+
 
    interval: 100,
+
  var build$MapFrame = function() {
    maxX: 0,
+
    return $('<div class="map-wrapper"></div>')
    maxY: -600,
+
            .css({ position: 'absolute' });
    minX: -500,
+
  };
    minY: -1500,
+
 
    width: 370,
+
  var build$FullMap = function(zoneData) {
    zeroX: 29,
+
    var $frame = build$MapFrame()
    zeroY: -336,
+
      .css({
    zoomX: 0.63,
+
         background: 'rgba(0,0,0,0.8)',
    zoomY: 0.624
+
         height: '100%',
  },
+
         left: 0,
  'Erud\'s Crossing': {
+
         position: 'fixed',
    height: 452,
+
         top: 0,
    image: 'Map_erudsxing.jpg',
+
         width: '100%',
     maxX: 2000,
+
         zIndex: 4 // one higher than #p-search's 3
    maxY: -600,
+
      })
    minX: 600,
+
      .click(removeSelf);
    minY: -2000,
+
    if (zoneData) {
    interval: 200,
+
      $frame.html(build$LightboxFramedMap(zoneData));
    width: 550,
+
       $frame.zoneData = zoneData;
    zeroX: 669.5,
+
    zeroY: -122,
+
    zoomX: 0.307,
+
    zoomY: 0.2725
+
  },
+
  'Everfrost Peaks': {
+
    alternateMaps: [
+
      {
+
        height: 404,
+
        image: 'Map_everfrostcaves.jpg',
+
        maxX: -3000,
+
        maxY: 5500,
+
        minX: -5500,
+
        minY: 3000,
+
        interval: 500,
+
        width: 400,
+
        zeroX: -359,
+
        zeroY: 780,
+
        zoomX: 0.1356,
+
        zoomY: 0.136
+
      }
+
    ],
+
     height: 425,
+
    image: 'Map_everfrost.jpg',
+
    maxX: 2000,
+
    maxY: 4000,
+
    minX: -8000,
+
    minY: -3000,
+
    interval: 1000,
+
    width: 550,
+
    zeroX: 101.5,
+
    zeroY: 230,
+
    zoomX: 0.0565,
+
    zoomY: 0.0566
+
  },
+
  'Firiona Vie': {
+
    alternateMaps:[{
+
      height: 539,
+
      image: 'Map_firionaviedocks.jpg',
+
      interval: 500,
+
      maxX: 3500,
+
      maxY: -2000,
+
      minX: 1000,
+
      minY: -4500,
+
      width: 550,
+
      zeroX: 714,
+
      zeroY: -380,
+
      zoomX: 0.194,
+
      zoomY: 0.194
+
    }],
+
    height: 555,
+
     image: 'Map_firionavie.jpg',
+
    interval: 1000,
+
     maxX: 6000,
+
    maxY: 5000,
+
    minX: 4000,
+
    minY: 5000,
+
    width: 540,
+
    zeroX: 324,
+
    zeroY: 282,
+
     zoomX: 0.053,
+
    zoomY: 0.0525
+
   },
+
   'Gorge of King Xorbb': {
+
    height: 357,
+
    image: 'Map_gxorbb.jpg',
+
    maxX: 1000,
+
    maxY: 1500,
+
    minX: -2000,
+
    minY: -1500,
+
    interval: 500,
+
    width: 400,
+
    zeroX: 112.5,
+
    zeroY: 173.5,
+
    zoomX: 0.12545,
+
    zoomY: 0.12545
+
   },
+
   'Greater Faydark': {
+
    alternateMaps: [
+
      {
+
        height: 519,
+
        image: 'Kelethin.jpg',
+
        interval: 200,
+
        maxX: 1000,
+
        minX: -700,
+
        maxY: 1200,
+
        minY: -800,
+
        width: 442,
+
        zeroX: 237,
+
        zeroY: 312,
+
        zoomX: 0.264,
+
        zoomY: 0.264
+
      }
+
     ],
+
     height: 532,
+
     image: 'Greaterfaydark.jpg',
+
     interval: 1000,
+
    maxX: 3000,
+
    maxY: 2000,
+
    minX: 3000,
+
     minY: 2000,
+
     width: 550,
+
    zeroX: 269,
+
    zeroY: 255,
+
    zoomX: .089,
+
    zoomY: .089
+
  },
+
   'Grobb': {
+
    height: 456,
+
    image: 'Grobb.jpg',
+
    interval: 200,
+
    maxX: 200,
+
    maxY: 600,
+
    minX: 800,
+
    minY: 200,
+
    width: 550,
+
    zeroX: 109,
+
    zeroY: 341,
+
    zoomX: 0.442,
+
    zoomY: 0.439
+
  },
+
   'Halas': {
+
    height:434,
+
    image: 'Zone_halas.jpg',
+
    interval: 200,
+
    maxX: 400,
+
    maxY: 600,
+
    minX: -600,
+
    minY: 0,
+
    width: 550,
+
     zeroX: 252.5,
+
    zeroY: 343,
+
    zoomX: 0.458,
+
    zoomY: 0.46
+
   },
+
  'Kael': 'Kael Drakkel',
+
  'Kael Drakkal': 'Kael Drakkel',
+
  'Kael Drakkel': {
+
     height: 600,
+
    image: 'Map_kael_drakkal.jpg',
+
     interval:250,
+
    maxX: 3250,
+
    maxY: 2750,
+
    minX: -500,
+
    minY: -2000,
+
    width: 507,
+
    zeroX: 404,
+
    zeroY: 344,
+
    zoomX: 0.12,
+
    zoomY: 0.1205
+
  },
+
  'Kerra Island': {
+
     height: 504,
+
    image: 'Kerra.jpg',
+
    interval: 500,
+
    maxX: 1000,
+
    maxY: 1500,
+
    minX: 1000,
+
    minY: 1000,
+
     width: 415,
+
    zeroX: 146.5,
+
    zeroY: 277,
+
    zoomX: 0.238,
+
    zoomY: 0.238
+
   },
+
   'Kithicor Forest': {
+
     height: 333,
+
    image: 'Map_kithicor.jpg',
+
    interval: 1000,
+
    maxX: 5000,
+
    maxY: 3000,
+
    minX: 1000,
+
    minY: 2000,
+
    width: 560,
+
    zeroX: 449.5,
+
    zeroY: 198,
+
    zoomX: 0.0875,
+
    zoomY: 0.089
+
   },
+
   'High Keep': {
+
     levels: {
+
       0: {
+
         height: 293,
+
         image: 'Highkeepbasement.png',
+
         interval: 100,
+
        maxX: 0,
+
        maxY: 200,
+
        minX: -600,
+
        minY: -100,
+
        width: 500,
+
        zeroX: 26,
+
        zeroY: 169,
+
        zoomX: 0.728,
+
         zoomY: 0.724
+
      },
+
      1: {
+
        height: 294,
+
         image: 'Highkeepfirstlevel.png',
+
        interval: 100,
+
        maxX: 100,
+
        maxY: 200,
+
        minX: -500,
+
        minY: -100,
+
        width: 500,
+
        zeroX: 96,
+
        zeroY: 169,
+
        zoomX: 0.728,
+
        zoomY: 0.724
+
      },
+
      2: {
+
        height: 235,
+
        image: 'Highkeepseocondlevel.png',
+
        interval: 100,
+
        maxX: -200,
+
        maxY: 100,
+
        minX: -400,
+
        minY: -100,
+
         width: 247,
+
        zeroX: -120,
+
        zeroY: 97,
+
        zoomX: 0.836,
+
        zoomY: 0.88
+
      },
+
      4: {
+
         height: 305,
+
        image: 'Highkeepfourthlevel.png',
+
         interval: 100,
+
        maxX: 0,
+
         maxY: 100,
+
         minX: -400,
+
         minY: -100,
+
         width: 475,
+
        zeroX: 50,
+
        zeroY: 131,
+
        zoomX: 0.88,
+
        zoomY: 0.88
+
       }
+
 
     }
 
     }
  },
+
     return $frame;
  'Innothule Swamp': {
+
   };
     height: 621,
+
 
    image: 'Map_innothule.jpg',
+
   var build$SmallMap = function(zoneData) {
    maxX: 2000,
+
     return build$MapFrame().append(build$WrappedMap(zoneData));
    maxY: 3000,
+
    minX: -1000,
+
    minY: -3000,
+
    interval: 1000,
+
    width: 325,
+
    zeroX: 210.5,
+
    zeroY: 297,
+
    zoomX: 0.099,
+
    zoomY: 0.117
+
   },
+
  'Lake of Ill Omen': {
+
    height: 542,
+
    image: 'Map_lakeofillomen.jpg',
+
    maxX: 4000,
+
    maxY: 6000,
+
    minX: -8000,
+
    minY: -4000,
+
    interval: 2000,
+
    width: 550,
+
    zeroX: 191.5,
+
    zeroY: 327,
+
    zoomX: 0.0438,
+
    zoomY: 0.0436
+
   },
+
  'Lake Rathetear': {
+
     height: 450,
+
    image: 'Map_lakerathetear.jpg',
+
    interval: 1000,
+
    maxX: 3000,
+
    maxY: 5000,
+
    minX: 3000,
+
    minY: 2000,
+
    width: 450,
+
    zeroX: 247.3,
+
    zeroY: 342.4,
+
    zoomX: 0.0755,
+
    zoomY: 0.0755
+
  },
+
  'Lavastorm Mountains' : {
+
    height: 472,
+
    image: 'Map_lavastorm.jpg',
+
    maxX: 1500,
+
    maxY: 1500,
+
    minX: -1500,
+
    minY: -2000,
+
    interval: 500,
+
    width: 400,
+
    zeroX: 222.3,
+
    zeroY: 214,
+
    zoomX: 0.1325,
+
    zoomY: 0.1325
+
  },
+
  'Misty Thicket': {
+
    height: 341,
+
    image: 'Map_mistythicket.jpg',
+
    maxX: 1500,
+
    maxY: 1500,
+
    minX: -3000,
+
    minY: -1500,
+
    interval: 500,
+
    width: 550,
+
    zeroX: 197,
+
    zeroY: 163.3,
+
    zoomX: 0.1208,
+
    zoomY: 0.1208
+
  },
+
  'Nektulos Forest': {
+
    height: 519,
+
    image: 'Map_nektulos.jpg',
+
    interval: 1000,
+
    maxX: 2000,
+
    maxY: 3000,
+
    minX: 2000,
+
    minY: 3000,
+
    width: 271,
+
    zeroX: 137.4,
+
    zeroY: 264.8,
+
    zoomX: 0.091,
+
    zoomY: 0.091
+
  },
+
  'Northern Felwithe': {
+
    height: 385,
+
    image: 'Nfelwithe.jpg',
+
    interval: 100,
+
    maxX: 100,
+
    maxY: 400,
+
    minX: 800,
+
    minY: 300,
+
    width: 589,
+
    zeroX: 109,
+
    zeroY: 217,
+
    zoomX: 0.547,
+
    zoomY: 0.55
+
  },
+
  'Northern Karana': {
+
    height: 476,
+
    image: 'Map_nkarana.jpg',
+
    interval: 1000,
+
    maxX: 4000,
+
    maxY: 2000,
+
    minX: 3000,
+
    minY: 5000,
+
    width: 500,
+
    zeroX: 263.3,
+
    zeroY: 135.5,
+
    zoomX: 0.071,
+
    zoomY: 0.0705
+
  },
+
  'Northern Desert of Ro': {
+
    height: 569,
+
    image: 'Map_northro.jpg',
+
    interval: 1000,
+
    maxX: 2000,
+
    maxY: 5000,
+
    minX: -2000,
+
    minY: -2000,
+
    width: 296,
+
    zeroX: 169.5,
+
    zeroY: 378.8,
+
    zoomX: 0.087,
+
    zoomY: 0.087
+
  },
+
  'North Qeynos': {
+
    height: 319,
+
    image: 'Zone_nqeynos.jpg',
+
    interval: 200,
+
    maxX: 600,
+
    maxY: 600,
+
    minX: -600,
+
    minY: -400,
+
    width: 458,
+
    zeroX: 204,
+
    zeroY: 189,
+
    zoomX: 0.39,
+
    zoomY: 0.39
+
  },
+
  'North Ro': 'Northern Desert of Ro',
+
  'Oasis of Marr': {
+
    height: 508,
+
    image: 'Map_oasisofmarr.jpg',
+
    interval: 1000,
+
    maxX: 2000,
+
    maxY: 3000,
+
    minX: -2000,
+
    minY: -2000,
+
    width: 315,
+
    zeroX: 172.5,
+
    zeroY: 271,
+
    zoomX: 0.103,
+
    zoomY: 0.1017
+
  },
+
  'Oggok': {
+
    height: 440,
+
    image: 'Oggok.jpg',
+
    interval: 200,
+
    maxX: 1000,
+
    maxY: 800,
+
    minX: 400,
+
    minY: 400,
+
    width: 536,
+
    zeroX: 361,
+
    zeroY: 305,
+
    zoomX: .3155,
+
    zoomY: .315
+
  },
+
  'Overthere': 'The Overthere',
+
  'Paineel': {
+
    height: 520,
+
    image: 'Paineelin.jpg',
+
    interval: 100,
+
    maxX: 1000,
+
    maxY: 1400,
+
    minX: 0,
+
    minY: 400,
+
    width: 492,
+
    zeroX: 507,
+
    zeroY: 716,
+
    zoomX: 0.5215,
+
    zoomY: 0.5212
+
  },
+
  'Qeynos Hills': {
+
    height: 575,
+
    image: 'Qeynoshills.jpg',
+
    interval: 1000,
+
    maxX: 2000,
+
    maxY: 6000,
+
    minX: 2000,
+
    minY: 1000,
+
    width: 324,
+
    zeroX: 136,
+
    zeroY: 516.3,
+
    zoomX: 0.0945,
+
    zoomY: 0.094
+
  },
+
  'Rathe Mountains': {
+
    height: 660,
+
    image: 'Zone_rathemtns.jpg',
+
    interval: 1500,
+
    maxX: 3000,
+
    maxY: 7500,
+
    minX: 4500,
+
    minY: 3000,
+
    width: 490,
+
    zeroX: 219.5,
+
    zeroY: 453.5,
+
    zoomX: 0.0707,
+
    zoomY: 0.0702
+
  },
+
  'Rivervale': {
+
    height: 540,
+
    image: 'Rivervale.jpg',
+
    interval: 200,
+
    maxX: 200,
+
    maxY: 600,
+
    minX: 800,
+
    minY: 400,
+
    width: 484,
+
    zeroX: 85,
+
    zeroY: 287,
+
    zoomX: 0.522,
+
    zoomY: 0.517
+
  },
+
  'Runnyeye':  'Clan Runnyeye',
+
  'RunnyEye':  'Clan Runnyeye',
+
  'RunnyEye Citadel':  'Clan Runnyeye',
+
  'South Kaladim': {
+
    height: 341,
+
    image: 'Skaladim.jpg',
+
    interval: 100,
+
    maxX: 600,
+
    maxY: 500,
+
    minX: -500,
+
    minY: -200,
+
    width: 550,
+
    zeroX: 291,
+
    zeroY: 238,
+
    zoomX: 0.51,
+
    zoomY: 0.52
+
  },
+
  'South Qeynos': {
+
    height: 426,
+
    image: 'Zone_sqeynos.jpg',
+
    interval: 200,
+
    maxX: 200,
+
    maxY: 600,
+
    minX: -600,
+
    minY: -400,
+
    width: 423,
+
    zeroX: 107,
+
    zeroY: 259,
+
    zoomX: .395,
+
    zoomY: .39,
+
  },
+
  'Southern Felwithe': {
+
    height: 377,
+
    image: 'Sfelwithe.jpg',
+
    interval: 100,
+
    maxX: 0,
+
    maxY: 800,
+
    minX: 900,
+
    minY: 0,
+
    width: 400,
+
    zeroX: 155,
+
    zeroY: 473,
+
    zoomX: 0.58,
+
    zoomY: 0.584
+
  },
+
  'Stonebrunt Mountains': {
+
    height: 619,
+
    image: 'Map_stonebrunt_mountains.jpg',
+
    interval: 1000,
+
    maxX: 4000,
+
    maxY: 5000,
+
    minX: 4000,
+
    minY: 6000,
+
    width: 529,
+
    zeroX: 253.3,
+
    zeroY: 285.5,
+
    zoomX: 0.0578,
+
    zoomY: 0.0576
+
  },
+
  'Surefall Glade': {
+
    height: 539,
+
    image: 'Surefallglade.jpg',
+
    interval: 200,
+
    maxX: 400,
+
    maxY: 1000,
+
    minX: 800,
+
    minY: 800,
+
    width: 330,
+
    zeroX: 97,
+
    zeroY: 306,
+
    zoomX: 0.323,
+
    zoomY: 0.32
+
  },
+
  'The Hole': {
+
    height: 465,
+
    image: 'Map_thehole.jpg',
+
    interval: 200,
+
    maxX: 1200,
+
    maxY: 1200,
+
    minX: 1200,
+
    minY: 600,
+
    width: 600,
+
    zeroX: 292,
+
    zeroY: 292,
+
    zoomX: 0.261,
+
    zoomY: 0.261
+
  },
+
  'The Overthere': {
+
    height: 644,
+
    image: 'Map_overthere.jpg',
+
    interval: 1000,
+
    maxX: 4000,
+
    maxY: 4000,
+
    minX: 4000,
+
    minY: 4000,
+
    width: 600,
+
    zeroX: 284.5,
+
    zeroY: 318,
+
    zoomX: 0.0628,
+
    zoomY: 0.0628
+
  },
+
  'The Warrens': {
+
    height: 314,
+
    image: 'Warrens.jpg',
+
    interval: 200,
+
    maxX: 1200,
+
    maxY: 1200,
+
    minX: 1200,
+
    minY: 400,
+
    width: 550,
+
    zeroX: 263.9,
+
    zeroY: 222.5,
+
    zoomX: 0.2185,
+
    zoomY: 0.22
+
  },
+
  'Thurgadin': {
+
    height: 557,
+
    image: 'Map_thurgadin.jpg',
+
    interval:200,
+
    maxX: 800,
+
    maxY: 200,
+
    minX: -600,
+
    minY: -1200,
+
    width: 550,
+
    zeroX: 300.5,
+
    zeroY: 104,
+
    zoomX: 0.343,
+
    zoomY: 0.344
+
  },
+
  'Toxxulia Forest': {
+
    height: 537,
+
    image: 'Toxxulia.jpg',
+
    interval: 1000,
+
    maxX: 3000,
+
    maxY: 3000,
+
    minX: 2000,
+
    minY: 3000,
+
    width: 441,
+
    zeroX: 262.3,
+
    zeroY: 261.5,
+
    zoomX: 0.0915,
+
    zoomY: 0.091
+
  },
+
  'Upper Guk': {
+
    height: 644,
+
    image: 'Upperguk.jpg',
+
    interval: 250,
+
    maxX: 750,
+
    maxY: 1500,
+
    minX: 500,
+
    minY: 0,
+
    width: 450,
+
    zeroX: 234,
+
    zeroY: 564,
+
    zoomX: 0.324,
+
    zoomY: 0.323
+
  },
+
  'West Commonlands': {
+
    height: 284,
+
    image: 'Zone_westcommons.jpg',
+
    interval: 1000,
+
    maxX: 5000,
+
    maxY: 2000,
+
    minX: 2000,
+
    minY: 2000,
+
    width: 649,
+
    zeroX: 435.5,
+
    zeroY: 126.5,
+
    zoomX: 0.096,
+
    zoomY: 0.096
+
  },
+
  'Western Karana': 'Western Plains of Karana',
+
  'Western Plains of Karana': {
+
    height: 422,
+
    image: 'Zone_westkarana.jpg',
+
    interval: 1000,
+
    maxX: -1000,
+
    maxY: 1000,
+
    minX: -16000,
+
    minY: -4000,
+
    width: 1058,
+
    zeroX: -23,
+
    zeroY: 107,
+
    zoomX: 0.0638,
+
    zoomY: 0.0641
+
 
   }
 
   }
};
 
  
} catch(err) {
+
 
   // Something went wrong; make sure we at least "export" our objects
+
var buildX = function(left, top, sizeInEm) {
  // so nothing else (except loc maps) breaks
+
   sizeInEm = sizeInEm || 2.5;
  console.error(err);
+
  return $('<div class="x">x</div>')
  window.zoneData = {};
+
    .css({
  window.zoneAliases = {};
+
      color: 'red',
 +
      fontSize: sizeInEm + 'em',
 +
      fontWeight: 'bold',
 +
      left: left,
 +
      position: 'absolute',
 +
      top: top
 +
    })
 
}
 
}
 +
 +
/**
 +
* Draws a red "X" on the map at the provided coordinate
 +
*/
 +
var addX = function($xContainer, zoneData, x, y, xSize) {
 +
  var left = (zoneData.zeroX || 0) + x * -1 * (zoneData.zoomX || 0.1);
 +
  var top = (zoneData.zeroY || 0) + y * -1 * (zoneData.zoomY || 0.1);
 +
  $xContainer.append(buildX(left, top, xSize));
 +
}
 +
 +
var addXs = function($xContainer, zoneData, locs, xSize) {
 +
  $.each(locs, function(i, loc) {
 +
    addX($xContainer, zoneData, loc.x, loc.y, xSize);
 +
  });
 +
}
 +
 +
 +
var parseLoc = function(locText) {
 +
  if (typeof locText !== 'string') return locText;
 +
 +
  var match = locText.match(/\(? *([\+\-]?\d+\.?\d*), *([\+\-]?\d+\.?\d*)\)?/);
 +
  return {x: parseFloat(match[2]), y: parseFloat(match[1]) };
 +
}
 +
 +
var isLoc = function(locBit) {
 +
    // If we can't split the string by its comma and find a number on either side, it's not a loc
 +
    try {
 +
      return locBit.split(',')[0].match(/\d+/) && locBit.split(',')[1].match(/\d+/);
 +
    } catch (err) {
 +
      return false;
 +
    }
 +
  }
 +
 +
  /**
 +
  * Extracts all of the locs (objects with x and y properties) from a provided
 +
  * string such as:
 +
  * "500, 200"
 +
  * "(200, 300), (300, 200)"
 +
  */
 +
  var getLocs = function(locString) {
 +
    var nonLocParts = '';
 +
    return locString
 +
    .split(/([\+\-]?\d+\.?\d*\D*,\D*[\+\-]?\d+\.?\d*)/g)
 +
    .filter(function(part) {
 +
        // Filter out the non-loc parts, but save them
 +
        if (isLoc(part)) return true;
 +
        nonLocParts += part; // save as it might be something like "1st floor"
 +
      })
 +
    .map(parseLoc)
 +
    .concat(nonLocParts);
 +
  };
 +
 +
  var buildMap = function(mapSize, zoneData) {
 +
    var $map;
 +
    switch(mapSize){
 +
      case 'full': return build$FullMap(zoneData);
 +
      case 'small': return build$SmallMap(zoneData);
 +
    }
 +
  };
 +
 +
  /**
 +
  * Adds a loc map image to the page.
 +
  */
 +
  var showMapWithLocs = function(mapSize, zoneData, locs, $container) {
 +
    zoneData = addImageUrl(zoneData);
 +
    var $map = buildMap(mapSize, zoneData);
 +
    $('body').append($map);
 +
    var $xContainer = $map.find('.x-container');
 +
    $map.find('img').load(function() {
 +
      addXs($xContainer, zoneData, locs);
 +
    });
 +
    if (mapSize === 'small') $map.css({ marginTop: 25 });
 +
    if ($container) $container.prepend($map);
 +
    else $('body').append($map);
 +
    return $map;
 +
  };
 +
 +
 
 +
  // Handle "loc links" (generated from {{loc}} templates)
 +
  var handleLocLinks = function() {
 +
    $('.loc-link').click(function() {
 +
      var data = $(this).data();
 +
      var locs = [parseLoc(data.loc)];
 +
      var zoneData = getZoneData(data.zone, locs);
 +
      if (zoneData) {
 +
        showMapWithLocs('full', zoneData, locs);
 +
      } else {
 +
        // If not, stop here
 +
        alert('We\'re sorry, but ' + data.zone + ' has not been loc mapped ' +
 +
              'yet ... please see Category:Loc_Mapped for more information');
 +
      }
 +
      return false
 +
    })
 +
    .on('mouseover', function(e) {
 +
      var data = $(this).data();
 +
      var zoneData = getZoneData(data.zone, [parseLoc(data.loc)]);
 +
      if (!zoneData) return;
 +
 +
      var locs = [parseLoc(data.loc)];
 +
      this.$map = showMapWithLocs('small', zoneData, locs, $(e.target));
 +
    })
 +
    .on('mouseleave', function(e) {
 +
      if (this.$map) this.$map.remove();
 +
      return false;
 +
    });
 +
  };
 +
 +
  var getZoneName = function() {
 +
    return $('b:contains("Zone:")').parent().text().split('Zone:')[1].trim();
 +
  };
 +
 +
  var handleLocBoxes = function() {
 +
    // Get the mob's loc(s)
 +
    var $locTd = $('b:contains("Location:")').parent();
 +
    var locs = getLocs($locTd.text());
 +
    if (locs.length <= 1) return; // No loc box to hook up
 +
 +
    var nonLocParts = locs[locs.length - 1];
 +
    locs = locs.slice(0, locs.length - 1);
 +
 +
    // Find the zone name
 +
    var zoneName = getZoneName();
 +
    // Do we have data for that zone's map?
 +
    var zoneData = getZoneData(zoneName, locs, nonLocParts);
 +
    if (!zoneData) return;  // If not, stop here
 +
 +
    // Add the mouseover link
 +
    var $link = $(' <a href="#">(Map)</a>');
 +
 +
    // When it's moused-over, show the map
 +
    $link
 +
    .on('mouseover', function(e) {
 +
      this.$map = showMapWithLocs('small', zoneData, locs, $locTd);
 +
    })
 +
    .on('mouseleave', function(e) {
 +
      this.$map.remove();
 +
      return false;
 +
    })
 +
    .on('click', function(e) {
 +
      showMapWithLocs('full', zoneData, locs);
 +
    });
 +
 +
    // $locTd.find('b').html($('<span>Location </span>').append($link).append('<span>:</span>'));
 +
    $locTd
 +
      .find('b')
 +
      .html($('<span>Location </span>')
 +
      .append($link)
 +
      .append('<span>:</span>'));
 +
  };
 +
 +
 +
  try {
 +
    handleLocLinks();
 +
    handleLocBoxes();
 +
  } catch (err) {/* Didn't work, move on */}
 +
 +
 +
  // Define two helper functions for building new zone definitions
 +
 +
  // 1) Use this function to find the correct 0,0 point
 +
  window.testZero = window.testZeroZero = function(zoneData) {
 +
    $('.map-wrapper').remove(); // test functions don't clean up properly
 +
    var locs = [{ x: 0, y: 0 }];
 +
    showMapWithLocs('full', zoneData, locs);
 +
  };
 +
 +
 +
  // 2) Use this function to generate a grid of alignment of X's
 +
  window.testGrid = function(zoneData) {
 +
    $('.map-wrapper').remove(); // test functions don't clean up properly
 +
    var $locTd = $('b:contains("Location:")').parent();
 +
    var locs = [];
 +
    for (var x = zoneData.maxX; x >= zoneData.minX; x -= zoneData.interval) {
 +
      for (var y = zoneData.maxY; y >= zoneData.minY; y -= zoneData.interval) {
 +
        locs.push({ x: x, y: y });
 +
      }
 +
    };
 +
    showMapWithLocs('full', zoneData, locs);
 +
  };
 +
 +
})();

Revision as of 19:30, 3 September 2019

(function() {

  // TODO: Add support for cropping maps that have multiple maps.
  // For instance, the following CSS styles show only one of the three maps for 
  // Erudin Palace:
  // background-image:url(/images/Erudinpalace.jpg); 
  // background-position: 250px 0px; width: 275px; height: 272px
  // Figure out how to make the Xs line up with those styles added

  var locIsWithinAlternateData = function(loc, alternateData) {
    return loc.x < alternateData.maxX &&
    loc.x > alternateData.minX &&
    loc.y < alternateData.maxY &&
    loc.y > alternateData.minY;
  };


  /**
   * Determines if the provided name contains any of the provided words
   */
  // TODO: Add a some polyfill to make this a lot simpler
  var containsAny = function(name/* word1, word2, etc. */) {
    for(var arg = 1; arg < arguments.length; ++ arg) {
      var word = arguments[arg];
      if (name.includes(word)) return true;
    }
    return false;
  };

  var containsAnyNumber = function(name, number, suffix) {
    return containsAny.call(null, name, 
     number + suffix,
     'level ' + number,
     'level: ' + number
     );
  };

  var getZoneLevelData = function(levels, text) {
    // Check for part "level" aliases (eg. "1st floor" vs. "Level One")
    text = text.toLowerCase();
    if (containsAnyNumber(text, 0, 'th') || containsAny(text, 'basement', 'underground')) return levels[0];
    if (containsAnyNumber(text, 1, 'st') || containsAny(text, 'one', 'first', 'ground')) return levels[1];
    if (containsAnyNumber(text, 2, 'nd') || containsAny(text, 'two', 'second')) return levels[2];
    if (containsAnyNumber(text, 3, 'rd') || containsAny(text, 'three', 'third')) return levels[3];
    if (containsAnyNumber(text, 4, 'th') || containsAny(text, 'four', 'fourth')) return levels[4];
    if (containsAnyNumber(text, 5, 'th') || containsAny(text, 'five', 'fifth')) return levels[5];
    if (containsAnyNumber(text, 6, 'th') || containsAny(text, 'six', 'sixth')) return levels[6];
    if (containsAnyNumber(text, 7, 'th') || containsAny(text, 'seven', 'seventh')) return levels[7];
    if (containsAnyNumber(text, 8, 'th') || containsAny(text, 'eight', 'eighth')) return levels[8];
    if (containsAnyNumber(text, 9, 'th') || containsAny(text, 'nine', 'ninth')) return levels[9];

    // If we still couldn't match, and there is a ground/1st floor, use it
    return levels[1];
  };

  

  var findZoneData = function(zoneName, locs, nonLocParts) {
    var zone = zoneData[zoneName];

  // Handle aliases (eg. "North Ro" instead of "Northern Desert of Ro")
  if (typeof(zone) === 'string') zone = zoneData[zone];

  if (!zone) return null;

  // Handle Multi-Level Zones (with different maps for 1st, 2nd, etc. floor)
  if (zone.levels) return getZoneLevelData(zone.levels, nonLocParts);

  // Handle Zones with alternate maps on the same level (eg. Kelethin in GFay)
  if (!zone.alternateMaps) return zone;

  for (var i = 0; i < zone.alternateMaps.length; i++) {
    var alternateData = zone.alternateMaps[i];
    var allLocsAreWithin = true;
    // wish I had ES6 [].every
    $.each(locs, function(i, loc) {
      allLocsAreWithin = allLocsAreWithin && 
      locIsWithinAlternateData(loc, alternateData);
    });
    if (allLocsAreWithin) return alternateData;
  };
  return zone;
};

var addImageUrl = function(zoneData) {
  zoneData.imageUrl = '/images/' + zoneData.image;
  return zoneData;
};

var getZoneData = function(zoneName, locs, nonLocParts) {
  var zoneData = findZoneData(zoneName, locs, nonLocParts);
  if (!zoneData) return null;
  zoneData.zoneName = zoneName;
  return addImageUrl(zoneData);
};

  /**
   * This basic function serves as the click handler for "lightbox-style" maps
   */
  var removeSelf = function() {
    $(this).remove();
  };


  var build$MapImage = function(zoneData) {
    return $(
      '<img alt="Map of ' + zoneData.zoneName + '" ' +
      '     class="thumbborder" ' +
      '     height="'+ zoneData.height + '" ' +
      '     src="' + zoneData.imageUrl + '" ' +
      '     title="Map of ' + zoneData.zoneName + '"' +
      '     width="' + zoneData.width + '" ' +
      '>'
      );
  };``


  var build$WrappedMap = function(zoneData) {
    return $('<div class="x-container"></div>')
      .css({ position: 'relative' })
      .append(build$MapImage(zoneData));
  }


  var build$LightboxFramedMap = function(zoneData) {
    return build$WrappedMap(zoneData)
      .css({
        left: '50%',
        marginLeft: '-' + (zoneData.width / 2) + 'px', // centering
        marginTop: '-' + (zoneData.height / 2) + 'px',
        opacity: 1,
        top: '50%'
      });
  };

  var build$MapFrame = function() {
    return $('<div class="map-wrapper"></div>')
            .css({ position: 'absolute' });
  };

  var build$FullMap = function(zoneData) {
    var $frame = build$MapFrame()
      .css({
        background: 'rgba(0,0,0,0.8)',
        height: '100%',
        left: 0,
        position: 'fixed',
        top: 0,
        width: '100%',
        zIndex: 4 // one higher than #p-search's 3
      })
      .click(removeSelf);
    if (zoneData) {
      $frame.html(build$LightboxFramedMap(zoneData));
      $frame.zoneData = zoneData;
    }
    return $frame;
  };

  var build$SmallMap = function(zoneData) {
    return build$MapFrame().append(build$WrappedMap(zoneData));
  }


var buildX = function(left, top, sizeInEm) {
  sizeInEm = sizeInEm || 2.5;
  return $('<div class="x">x</div>')
    .css({
      color: 'red',
      fontSize: sizeInEm + 'em',
      fontWeight: 'bold',
      left: left,
      position: 'absolute',
      top: top
    })
}

/**
 * Draws a red "X" on the map at the provided coordinate
 */
var addX = function($xContainer, zoneData, x, y, xSize) {
  var left = (zoneData.zeroX || 0) + x * -1 * (zoneData.zoomX || 0.1);
  var top = (zoneData.zeroY || 0) + y * -1 * (zoneData.zoomY || 0.1);
  $xContainer.append(buildX(left, top, xSize));
}

var addXs = function($xContainer, zoneData, locs, xSize) {
  $.each(locs, function(i, loc) {
    addX($xContainer, zoneData, loc.x, loc.y, xSize);
  });
}


var parseLoc = function(locText) {
  if (typeof locText !== 'string') return locText;

  var match = locText.match(/\(? *([\+\-]?\d+\.?\d*), *([\+\-]?\d+\.?\d*)\)?/);
  return {x: parseFloat(match[2]), y: parseFloat(match[1]) };
}

var isLoc = function(locBit) {
    // If we can't split the string by its comma and find a number on either side, it's not a loc
    try {
      return locBit.split(',')[0].match(/\d+/) && locBit.split(',')[1].match(/\d+/);
    } catch (err) {
      return false;
    }
  }

  /**
   * Extracts all of the locs (objects with x and y properties) from a provided
   * string such as:
   * "500, 200"
   * "(200, 300), (300, 200)"
   */
   var getLocs = function(locString) {
    var nonLocParts = '';
    return locString
    .split(/([\+\-]?\d+\.?\d*\D*,\D*[\+\-]?\d+\.?\d*)/g)
    .filter(function(part) {
        // Filter out the non-loc parts, but save them
        if (isLoc(part)) return true;
        nonLocParts += part; // save as it might be something like "1st floor"
      })
    .map(parseLoc)
    .concat(nonLocParts);
  };

  var buildMap = function(mapSize, zoneData) {
    var $map;
    switch(mapSize){
      case 'full': return build$FullMap(zoneData);
      case 'small': return build$SmallMap(zoneData);
    } 
  };

  /**
   * Adds a loc map image to the page.
   */
  var showMapWithLocs = function(mapSize, zoneData, locs, $container) {
    zoneData = addImageUrl(zoneData);
    var $map = buildMap(mapSize, zoneData);
    $('body').append($map);
    var $xContainer = $map.find('.x-container');
    $map.find('img').load(function() {
      addXs($xContainer, zoneData, locs);
    });
    if (mapSize === 'small') $map.css({ marginTop: 25 });
    if ($container) $container.prepend($map);
    else $('body').append($map);
    return $map;
  };

  
  // Handle "loc links" (generated from {{loc}} templates)
  var handleLocLinks = function() {
    $('.loc-link').click(function() {
      var data = $(this).data();
      var locs = [parseLoc(data.loc)];
      var zoneData = getZoneData(data.zone, locs);
      if (zoneData) {
        showMapWithLocs('full', zoneData, locs);
       } else {
        // If not, stop here
        alert('We\'re sorry, but ' + data.zone + ' has not been loc mapped ' +
              'yet ... please see Category:Loc_Mapped for more information'); 
      }
      return false
    })
    .on('mouseover', function(e) {
      var data = $(this).data();
      var zoneData = getZoneData(data.zone, [parseLoc(data.loc)]);
      if (!zoneData) return;

      var locs = [parseLoc(data.loc)];
      this.$map = showMapWithLocs('small', zoneData, locs, $(e.target));
    })
    .on('mouseleave', function(e) {
      if (this.$map) this.$map.remove();
      return false;
    });
  };

  var getZoneName = function() {
    return $('b:contains("Zone:")').parent().text().split('Zone:')[1].trim();
  };

  var handleLocBoxes = function() {
    // Get the mob's loc(s)
    var $locTd = $('b:contains("Location:")').parent();
    var locs = getLocs($locTd.text());
    if (locs.length <= 1) return; // No loc box to hook up

    var nonLocParts = locs[locs.length - 1];
    locs = locs.slice(0, locs.length - 1);

    // Find the zone name
    var zoneName = getZoneName();
    // Do we have data for that zone's map?
    var zoneData = getZoneData(zoneName, locs, nonLocParts);
    if (!zoneData) return;  // If not, stop here

    // Add the mouseover link
    var $link = $(' <a href="#">(Map)</a>');

    // When it's moused-over, show the map
    $link
    .on('mouseover', function(e) {
      this.$map = showMapWithLocs('small', zoneData, locs, $locTd);
    })
    .on('mouseleave', function(e) {
      this.$map.remove();
      return false;
    })
    .on('click', function(e) {
      showMapWithLocs('full', zoneData, locs);
    });

    // $locTd.find('b').html($('<span>Location </span>').append($link).append('<span>:</span>'));
    $locTd
      .find('b')
      .html($('<span>Location </span>')
      .append($link)
      .append('<span>:</span>'));
  };


  try {
    handleLocLinks();
    handleLocBoxes();
  } catch (err) {/* Didn't work, move on */}


  // Define two helper functions for building new zone definitions

  // 1) Use this function to find the correct 0,0 point
  window.testZero = window.testZeroZero = function(zoneData) {
    $('.map-wrapper').remove(); // test functions don't clean up properly
    var locs = [{ x: 0, y: 0 }];
    showMapWithLocs('full', zoneData, locs);
  };


  // 2) Use this function to generate a grid of alignment of X's
  window.testGrid = function(zoneData) {
    $('.map-wrapper').remove(); // test functions don't clean up properly
    var $locTd = $('b:contains("Location:")').parent();
    var locs = [];
    for (var x = zoneData.maxX; x >= zoneData.minX; x -= zoneData.interval) {
      for (var y = zoneData.maxY; y >= zoneData.minY; y -= zoneData.interval) {
        locs.push({ x: x, y: y });
      }
    };
    showMapWithLocs('full', zoneData, locs);
  };

})();