[ Disclaimer, Create new user --- Wiki markup help, Install P99 ]
Difference between revisions of "MediaWiki:Zones.js"
From Project 1999 Wiki
| Line 1: | Line 1: | ||
| − | + | (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; | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | ' | + | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | ] | + | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | ' | + | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | height | + | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | width | + | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | } | + | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | } | + | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | height | + | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
} | } | ||
| − | + | 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); | ||
| + | }; | ||
| + | |||
| + | })(); | ||
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);
};
})();