[ Disclaimer, Create new user --- Wiki markup help, Install P99 ]
MediaWiki:Zones.js
From Project 1999 Wiki
Note: After saving, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Clear the cache in Tools → Preferences
(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);
};
})();