MapsExtended

adds new functionality and features to Interactive Maps.
MapsExtended runs on both Map-namespace pages, and pages with one or more embedded maps. It will not run in the Interactive Maps Editor, but does function in the regular source editor, and will handle live previews correctly.
The script is not intended as a replacement or a recreation of interactive maps, and does not affect the core functionality of maps by design. Many of the features it provides are made to enhance the experience for desktop users, whilst retaining full compatibility with mobile maps where this script does not apply. Features that may affect parity between platforms (by requiring modifications to the map that are utilised on desktop but not mobile) will be marked as such). When this is the case, and the resulting mobile map (typically with placeholder elements) is not suitable, editors may instead opt to use a separate map for desktop and mobile.
:This script is in beta, and - due to the reliance on a feature that is constantly evolving - may suddenly break. Install it at your own peril! Feedback, bug reports, feature suggestions, and requests for custom functionality are [ welcome].
== Installation ==
==Overview==
===Features===
* Match marker definitions to marker elements in the map instance.
* Hide some categories by default — Specific categories may be hidden or disabled outright as soon as the map is loaded. This may help reduce clutter and improve the performance on a map with many markers.
* Nested and/or grouped categories — Define subcategories and category headings to give more structure to the filters list.
* Open popups on hover — Optionally open popups when cursoring over their associated marker.
* Custom marker icon anchors — Change the alignment of custom map marker icons from the default bottom-center.
* Automatically sort marker z-order — Rearrange marker elements automatically so that they are rendered in a certain order.
* Rearrange controls — Reposition and reorder the buttons in the corners of the map.
* View maps in fullscreen — Adds a fullscreen button to maps.
* Search markers and categories — Adds a search box to maps, allowing users to quickly search for a specific marker, as well as providing a place for a complete listing of all markers.
* New sidebar interface — An alternative expanded way to control the map.
* Tooltips — Shows the title of a marker in a small popup when the marker is hovered.
* Collectibles — A simple way for users to keep track of things they've collected on the map (in the case of a map for a video game, for example).
* Zoom layers — Separate markers into layers which are visible within specific zoom thresholds.
* Configuration — Tiered configuration with fallbacks and overrides, allowing a configuration to be applies to all maps, a specific map, or even a specific embed of a map.
* Adds lightbox support to popup images (in regular Interactive Maps, clicking on a popup simply follows the link to the image (not file page)).
* Easier navigation of tightly packed markers, by bringing icons to the front while they are hovered over.
* Speeds up filtering of markers by category, significantly reducing the lag experienced when toggling categories on and off.
===Matching===
:This section is for reference purposes only
Matching is the automatic process of associating marker elements in the DOM with marker definitions in the JSON. A big part of the functionality of the script is keeping track of Leaflet elements, which are created and destroyed frequently. The Leaflet-created marker elements have no distinguishing features (other than their position and color), so they can be matched in one of three ways.
These methods are in order of presedence. If matching by position, the marker couldn't be matched by index. If matching by popup, the marker couldn't be matched by position or index.
#Match by index — If all categories are enabled (which currently is always the case), the order of the marker elements in the DOM and the marker definitions in the JSON will be the same. We're then able to associate the two based on the index of the element within the marker panel, and the index of the marker definition in the "markers" array.
#Match by hint — These hints aren't actually used to match elements to their definitions, but are instead used to invalidate possible mismatches if they're obviously not correct, speeding up the matching process significantly.
#* ID: The value in the data-testid attribute on the element SVG is compared to the id field of the definition. This would be the most reliable way to match markers IF this attribute was present on all marker elements, however it is only present on markers that use the default marker icon.
#* Color: For default markers, the style on the element SVG contains a "--marker-icon-color" variable. This is compared to the color field on the category that the marker definition belongs to.
#* Icon: For markers that use an icon (be it on the marker itself, or on the category that the marker belongs to), the file name of the icon may be compared to the name in the definition.
#Match by position — The primary way to match markers, by taking the position of the marker element relative to the the background image and comparing it to the position of the marker in the definition. The only flaw here is that markers of the same category, ID, and position will fail to match.
#Match by popup — This is a form of "late" matching, where we can only determine the marker definition after the user clicks on it. This compares the content of the popup with the "popup" definition in the marker object (being title, description, url, and label). This can be unreliable if you have multiple markers that contain the same content, and in this case we check to see which one is the closest to the popup position.
The script will also wait for the appearance of markers that don't already exist (say, markers within clusters which don't appear until further zoomed).
==Category customisation==
===Hide/show categories===
Certain categories may be hidden by default, which unchecks them in the filters dropdown and hides all associated markers. In order to show the markers again, the user must manually go and check the category in the filters dropdown.
You can mark a category as hidden by either:
* Adding the category ID to the hiddenCategories array of the config object in the JSON.
* Adding a __hidden suffix to the category ID in the JSON (making sure to change the categoryId on any marker objects too).
* Setting "hidden": true on the category group that the category belongs to.
May be used in conjunction with embed-level configs to create transclusions of your map with specific categories disabled. For example, {{Map:MyMap}} will hide categories with the ID's "foo" and "bar".
As an inverse to the above, you may also use the visibleCategories array, which may be used if you want to only show specific categories and hide all others. Categories present in both arrays will be hidden.
===Disable categories===
Categories may also be disabled entirely. These categories are removed from the filters dropdown, and their markers are permanently removed from the map. Disabled categories are inherently also hidden, so there is no need to mark a category as hidden if it is already disabled.
You can mark a category as disabled by either:
* Adding the category ID to the disabledCategories array of the config object in the JSON.
* Adding a __disabled suffix to the category ID in the JSON (making sure to change the categoryId on any marker objects too).
===Remove empty categories===
Categories that contain no markers will be removed from the filters list. Category groups where all categories and nested groups' categories contain no markers will also be removed from the category group tree.
===Grouped categories===
Categories can be grouped into logical sets which may be toggled on and off all at once. These groups may be defined via the categoryGroups array of the config object. If this array is not present, categories are not grouped and will appear as normal. Each object in the array should have the following fields:
;label (required, string): A label, something to call the group.
;collapsible (optional, boolean): Whether this group will collapse when the collapse arrow is clicked. Defaults to true.
;collapsed (optional, boolean): The initial collapsed state (false for uncollapsed, true for collapsed). Defaults to false.
;collapseIfHidden (optional, boolean): If true, if all categories are hidden initially (not as a result of the user changing them), the category group will start collapsed - but only if it is collapsible. Defaults to false.
;hidden (optional, boolean): Whether the markers of categories in this group are hidden initially (false for shown, true for hidden). Any categories in hiddenCategories will still be hidden if this is false. Defaults to false.
;children (required, array of string/object): An array, which may be a mix of category ids to place in this group, or further group definitions to allow nesting groups. If a category group does not contain any valid categories, it is not added.
An example of a categoryGroups config:
==Popup customisation==
===Open popups on hover===
Set the config option openPopupsOnHover to forgo the need to click on a marker in order to open its popup. While the user is hovered over the marker, or the displayed popup, the popup will stay on screen. When the cursor leaves either the marker or the popup, after 0.5 seconds, the popup is automatically hidden.
While this option is enabled, opening a popup by clicking on a marker is disabled.
Two further options may be use to customize the behaviour: popupHideDelay and popupShowDelay (see the table below for more information.
===Popup display options===
The following options may be used to change the behaviour of popups:
;enablePopups: If you want a map with no popups, set enablePopups to false. 
;useCustomPopups: This option is only used to circumvent an issue with Interactive Maps that causes the map to freeze with an error Uncaught RangeError: Maximum call stack size exceeded. It simply replaces Leaflet-created popups with popups that look and feel like the real thing, but are completely disconnected from Leaflet, therefore providing no opportunity for this issue to occur. The down side to this is that:
:* Marker reports will no longer function.
:* The map will no longer "bounce back" on popups so they are always visible within the viewport. For markers near the edge of the viewport, this may result in part of the popup being obscured when it is opened.
==Marker customisation==
===Custom marker icon anchors===
Interactive Maps centers all markers on the bottom edge of the icon, meaning the icon is actually shown above the marker coordinate. This can be problematic when zoomed out, as the offset is much more obvious - particuarly when accuracy is required. Custom CSS can fix this easily (i.e. .MapMarker-module_markerCustomIcon__YfQnB { margin-top: -13px !important; } and .leaflet-popup { margin-bottom: 8px; }, but isn't a perfect solution, particularly if you want to have different anchors for each map, or if you use a mix of default pins and custom icons (specifically because of the popup offset), or if you have markers that have non-square icons with a width greater than their height, as the offset of -13px will not be perfectly centered.
The config option iconAnchor may be used to influence this, and tells the script which point on the icon image will align with the coordinate of the marker (i.e. the position of the marker coordinate relative to the icon), and the offset of the popup relative to the marker. For example bottom-center will align the icon such the middle of its bottom edge is on the marker coordinate (the icon itself will then be situated above the coordinate vertically, and horizontally centered on it).
Valid icon anchors are: top-left, top-center, top-right, center-left, center (default), center-right, bottom-left, bottom-center, bottom-right.
If desired, you may alternatively use the option iconPosition, which is the exact opposite; the position of the icon relative to the marker coordinate. When this is present, iconAnchor will be ignored.
iconAnchor can be used with maps that have a mix of default pins and custom icons, as it only applies to custom icons.
In an original non-MapsExtended Interactive Map, the default is bottom-center, but in MapsExtended the default for custom icons is center. Keep this in mind if you have custom "pin-shaped" icon graphics, as you will need to manually specify bottom-center.
iconAnchor must not be used in combination the CSS above, as it will not be able to set the icon offset.
====Dealing with icon flicker/jumping====
Using a non-default marker position may cause the markers to "jump" when the map is loaded and MapsExtended is applied. If you know that all your maps will use a specific icon anchor, you may mitigate the jumping effect by defining the offset beforehand in your wiki's CSS, making sure to use !important to override inline styles.
As per the iconAnchor above, the following combination of styles should be used:
iconAnchor	Style
Vertical 	Vertical
top-* 	margin-top: 0
center-* 	margin-top: -13px
bottom-* 	margin-top: -26px
Horizontal	Horizontal
*-left 	margin-left: 0px
*-center 	margin-left: -13px
*-right 	margin-left: -26px
So for example when using an iconAnchor of bottom-right, you may add the following CSS. Make sure to use the selector verbatim, and include both styles regardless of if they are redundant (i.e. bottom-center):
Once MapsExtended loads, it will apply whatever anchor the map specifies, and append the uses-icon-anchor class to the marker, thus removing the style.
Non-square markers may still jump a little bit, unfortunately there's no catch-all solution to this, as icons have different offsets depending on their size/aspect ratio. In addition, you may only define one anchor this way, so if you have multiple maps with different anchors on your wiki it may be better to add the CSS for the center iconAnchor, as then at least the jumping effect can be kept to a minimum.
===Sort markers===
With the config option sortMarkers, the markers may be rendered in a different order than the order in which they appear in the JSON (currently, a marker defined last in the markers array will always appear on top of a marker defined earlier). A consistent sort order allows the map to retain visual consistency. The following options are available:
;latitude (default): Sort based on the marker's y position. Markers further towards the top of the map are placed behind markers towards the bottom. Alias is latitude-asc, may be reversed with latitude-desc
;longitude: Sort based on the marker's x position. Markers further towards the left of the map are placed behind markers towards the right. Alias is longitude-asc, may be reversed with longitude-desc
;category: Sort based on the position of the category in the categories array of the JSON, then by latitude. This may be useful if you want certain categories to always appear above others, say if they're more important. When using category or category-asc, markers in categories that are defined first (i.e. at the top of the category list) will appear on top of markers in categories that are defined last. When using category-desc, markers in categories that are defined first will appear on the bottom.
;unsorted: Retain the original sort order, markers defined last in the JSON will appear on top as they are at the bottom of the DOM.
==Map interface customisation==
===Rearrange controls===
Around the edge of the map are controls, buttons which perform various functions on the map. You can change the default position, order, and presence of these controls using the option mapControls, a multidimensional string array of [4][n], representing the order and position of the buttons, where the first array is top-left, second is top-right, third is bottom-right, and the fourth is bottom-left. All four arrays must be present.
The another option hiddenControls is an array for controls that should be hidden, which may be omitted if you don't want to hide any controls. If a button is hidden, depending on the button this may not completely prevent functionality. For example if "zoom" is omitted, the zoom controls will not be shown, but the user can still zoom in with the mouse wheel, plus and minus keys, etc.
Each string in either array should be one of the following (this list may change as new controls are added in the future):
:"zoom", "fullscreen", "edit".
Some other notes:
* The buttons are ordered from top-to-bottom in the order they are present in the array.
* Buttons in the array are placed after any buttons that aren't specified in the array, if they appear there by default.
* Values omitted from all arrays will still be present in their default positions.
* Repeat values are valid , and will end up appearing in whatever position is used last.
The default mapControls is an empty array (which indicates no changes to the default), but an identical value is: [ [], ["edit"], ["fullscreen", "zoom"], []], which shows no controls in the top left, the edit control in the top right, the fullscreen and zoom controls in the bottom right (with the fullscreen control at the top), and then nothing in the bottom left.
===Toggle fullscreen===
By default, MapsExtended adds a fullscreen toggle button above the plus/minus zoom buttons. When clicked, the map will enter or exit fullscreen. If you don't wish to allow the user to fullscreen a map, use set the config option enableFullscreen to false.
Two modes of fullscreen are supported via the fullscreenMode config option, which determines the default action of the fullscreen button.
;window: Windowed fullscreen mode expands the map to fill the browser window. In this mode, the map is displayed over the entire page, with several Fandom elements hidden, like the global and sticky navigation bars, toolbar elements, as well as the page content itself. It may be exited by pressing Escape, or by clicking on the fullscreen button again. While in windowed fullscreen, you may also use the browser's built-in fullscreen function (F11 by default), which may be exited by pressing with F11.
:This is often faster than regular fullscreen, and may reset the map depending on its dimensions.
;screen: Regular fullscreen expands the map to fill the entire screen/monitor through the use of the Fullscreen API. It can be exited with either Escape, F11, or by clicking on the fullscreen button again.
:In this mode, Leaflet will reinitialize, causing the zoom level and current position to be lost. This unfortunately cannot be worked around.
:Certain overlay elements like banners, prompts, and the lightbox will be added to the top layer when they are created.
When using either mode, holding Ctrl (or Command ⌘ on Mac) while clicking the button will activate fullscreen in the opposite mode than the mode defined in the config (i.e. window mode if screen mode is being used, and vice versa). Both modes can be active at the same time.
===Search and marker listing===
:Search does not currently work when using marker clustering!
A new dropdown will be added to the map to the left of the "Filters" dropdown, which provides a list of all markers on the map, and gives users the ability to quickly find a marker by searching for it. This control is added to all maps by default, though this behaviour can be changed using the enableSearch config option, if you wish to disable search on specific or all maps.
Markers will be grouped into categories, which can be collapsed by clicking on the category header. Categories are sorted in the same order they appear in the filters dropdown, and markers are sorted first alphabetically and then by the order they appear in the JSON.
When hovering over a marker in the list, a "highlight" circle will appear around the marker on the map to show the location of the marker on the map, and the marker will be rendered above all others. When hovering over a category, the same occurs but for all markers in the category. Clicking on a marker in the list will highlight the marker permanently (or at least until it is toggled off) and will display its popup. Clicking on a category, or toggling its visibility off in the filters list will collapse it.
A search works as follows:
* Categories are searched, and are a valid result if the search term is contained in the "name" field of the category. If a match is found, all markers under that category are added to the results.
* Markers are searched, and are a valid result if the search term is contained in the popup field "title".
The search is updated live as the input changes, and for performance are memoized; searches are always based upon the results of the closest-matching previous search. Markers that do not match the term will be hidden from the listing, and their icons will be removed from the map. Categories that are filtered out (by unchecking them in the filters dropdown) will appear in the search results, but will be greyed out.
===Sidebar===
The sidebar is an "expanded" alternative interface to the top bar and its list of dropdowns. It is less compact than the top bar, but at the same time may be more usable and visually cohesive, particularly for maps with many categories and markers as the categories won't be hidden away in a long list that needs to be scrolled through. As it's a rather big change to maps, the sidebar is disabled by default, and may be enabled by setting the config option enableSidebar to true.
The sidebar may be shown by clicking the hamburger menu (≡) in the topbar, and may be hidden by clicking it again, or by clicking the cross in the top right corner of the sidebar itself. While the sidebar is visible, the search and filter menus in the top bar are greyed out and become unusable (because their function is replicated in the sidebar).
When the sidebar is brought into view, the map is shifted over such that it doesn't appear obscured. This way the entire map may be visible at once while a search is being performed, or categories are being toggled. If you wish to keep the map the same size and make the sidebar an overlay instead, set the option sidebarOverlay to true.
The width of the sidebar is 400px, which may be modified in CSS. If the sidebar is visible and the map width is less than half the width of the sidebar (200px), the sidebar becomes an overlay automatically and it covers the entire width of the map container.
sidebarSide may be used to switch the side of the map that the sidebar is shown on:
;left (default): The sidebar appears on the left.
;right: The sidebar appears on the right.
sidebarBehaviour controls the responsive behaviour of the sidebar. In the default mode autoInitial, the sidebar is shown automatically (in response to the user resizing the page) when the map container becomes greater than or equal to double the width of the sidebar (800px). Note that this only occurs when the width crosses the threshold, not every time the width changes. It continues to be responsive until the user manually toggles the sidebar, after which the sidebar will persist in whatever state the user left it in regardless of the size of the map container.
;autoInitial (default): The sidebar is responsive. When the user toggles the sidebar on or off, from then on the responsive behaviour is disabled and the sidebar is manually controlled. 
;autoAlways: The sidebar is responsive. When the map container size crosses the threshold, the sidebar is toggled on or off regardless.
;manual: The sidebar must be manually controlled, and is never shown or hidden without direct user input.
sidebarInitialState controls whether the sidebar is shown initially:
;auto (default): If the condition to show the sidebar is met (as above), it is shown.
;show: The sidebar is always initially shown, regardless of the size of the map.
;hide: The sidebar is always initially hidden.
====Sidebar search====
While shown, the search dropdown is moved to the sidebar, and can be expanded by clicking anywhere on the "Search" field, and collapsed by clicking the arrow on the right of the field. The height of the results list is set such that when expanded, the buttons and categories toggle is still visible. It can be also resized vertically by dragging the resize handle in the bottom right corner.
====Sidebar categories====
Categories shown in the sidebar are defined by categoryGroups, but are flattened instead of nested, with sub-categories denoted with a prefix. Ungrouped categories appear before all other groups.
Take for example the following category group:
This is automatically converted to the following groups for use in the sidebar:
Categories are displayed in a two column layout. However, if there are less than 10 categories on the map, a single column layout is used.
==Other customisation==
===Tooltips===
Leaflet tooltips are small containers that display the title of the popup, and are shown immediately when hovering over a marker on the map. They may be useful in situations where the action of clicking on a popup may be too tedious (particularly for popups which only contain a title, where the information shown is minimal), or for maps where the type of marker may be too ambiguious from just a glance.
Tooltips are enabled by default, and may be disabled with the config option enableTooltips: false.
tooltipDirection is used to set the side on which the tooltip opens relative to the marker. Available options are "left", "right", "top", "bottom", "center", "auto". "auto" is the default, and will dynamically switch between right and left according to the tooltip position in the viewport, in order to avoid situations where a tooltip is displayed outside of the view. Tooltips for markers on the right side of the viewport are shown on the left of the marker, and vice versa.
tooltipOffset is a pixel offset of the tooltip position, expressed as an array of two numbers, e.g. [ 12, 34 ]. Negative X values shift the tooltip left, and positive shift it right. Negative Y values shift the tooltip up, and positive shift it down. When used with tooltipDirection "auto", the x offset is inverted when the tooltip is shown on the right of the marker.
Some notes about tooltips:
* Hovering over a marker with tooltips enabled will display the marker on top of all others while the tooltip is active.
* The tooltip for a marker is temporarily hidden when the popup for that marker is shown, as to avoid crowding the screen.
* It is not recommended to use tooltips with openPopupsOnHover, without setting popupShowDelay to a particularly high value.
* The origin of the tooltip is always adjusted to be the center of the marker image, not the coordinate of the marker. This means the tooltip position does not change if you have a non-center iconAnchor.
===Collectibles===
:The option useCustomPopups must be enabled when using collectibles.
Allows map markers of specific categories to be marked as collected. This feature may be used, for example, in a map for a video game, giving users a simple way to keep track of things they have collected in the world, places they've been, or characters they've talked to.
Categories can be set as collectible by either:
* Including the category ID in the collectibleCategories array of the config object.
* Adding the __collectible suffix to the category ID. If a category should be hidden by default and be collectible, you can use __collectible_hidden or __hidden_collectible instead. Make sure that all of the categoryId fields on markers in this category are updated too.
Collectibles includes some optional features that were added to the March 2024 collectibles experiment for Fandom Interactive Maps. These features may be toggled with the options enableYourProgressFilter and collectibleCheckboxStyle.
====Features====
* Markers may be marked as collected by toggling the checkbox in the top right of the popup, or by Ctrl-clicking (Command ⌘ on Mac) the marker.
* Collected markers will be dimmed to 50% opacity by default indicate to the user that it has been collected. This may be controlled by using your own CSS rule to target .mapsExtended_collectedMarker.
* The label of collectible categories will show "<numCollected> of <numTotal> collected", indicating the amount of markers that have been collected so far.
* Markers can be un-collected en masse with the Clear collected button at the bottom of the filters dropdown. An OOUI confirmation dialogue appears to ask the user if they're sure.
* The collected state of all markers in a specific category can be cleared/marked as collected en masse by Ctrl-clicking (Command ⌘ on Mac) the category in the filters dropdown. This occurs without prompting the user for confirmation (as it's a rather intentional action), so use caution!
* When all markers of a category are collected, a BannerNotification will appear to notify (and congratulate) the user. You can disable this behaviour with enableCollectedAllNotification.
* The collected states of markers in a map will persist across sessions, as they are saved to the browser's localStorage, keyed by <wgDBname>_<map_name>_collected. This means that a transcluded map will load the same states as a non-transcluded map, or the same map across different pages. States are saved when the user navigates away from the page.
* Collectible states expire a month (2629743 seconds) after they are last accessed (retrieved or stored). The time that the collectible states were last set is also stored in localStorage, under the same key but prefixed with _EXPIRY_. The config collectibleExpiryTime may be used to set a custom time in seconds. 0 may be used to not store states (they will only be valid for the current session. -1 may be used to store states without an expiry time. This feature is only available on wikis running Mediawiki 1.39.
MapsExtended_collectible_popup.png | A popup for a collected collectible marker, note the checkbox and the marker opacity.
MapsExtended_collected_all_banner.png | Banner which appears after collecting all markers
===Zoom layers===
Zoom layers allow you to organize markers into distinct categories based on zoom level. This functionality loosely mimics how tools like Google Maps display content. Zoom layers ensure only relevant and appropriate information is displayed based on the current zoom level. This declutters the map, improves performance (by having less markers on the screen to render), and makes it easier to focus on the specific area of interest.
For example, at a high zoom level (zoomed out), you could have a layer with broader markers like countries or regions, while zooming in could reveal layers with more granular details like towns, streets, and individual buildings. This provides a natural hierarchy to your map data.
Zoom layers are defined in the zoomLayers config option. Each layer in the array has the following fields:
;id (required, string|number): An identifying label.
;minZoom (optional, number): The minimum zoom scale required to show this layer. If omitted, minZoom is 0, meaning the layer is always shown when completely zoomed out. The layer will be shown when the current zoom scale is ≥ this value, and hidden when it is < this value. A higher minZoom value means you must zoom further in to show this layer, and lower means it'll be shown while zoomed further out.
:In most setups, you'll want your more "important" markers on a layer with a minZoom of 0, and then gate each layer of lessening importance by increasing their minZoom value.
;maxZoom (optional, number): The maximum zoom scale required to show this layer. If omitted, maxZoom is Infinity, meaning the layer always shown when zoomed completely in. The layer will be shown when the current zoom scale is < this value, and hidden when it is ≥ this value. A higher maxZoom value means that you must zoom further in to hide the layer, and lower means it'll be hidden while zoomed further out. Generally you maxZoom should always be greater than minZoom.
:In most setups, it's okay to omit this value to have no maximum and therefore keep showing a layer no matter how far you zoom in. Depending on your needs, you might give some layers a maxZoom value, so that broader markers are hidden as you zoom in, or even make the maxZoom of one layer match the minZoom of another to completely switch between layers.
;categories (optional, array of string|number): An array of categories IDs whose markers should be part of this layer. Alternatively, instead of an ID, you can use a regex pattern to match category IDs against. Regex patterns must start and end with a / (which will be stripped).
;markers (optional, array of string|number): An array of marker IDs which should be part of this layer. Like above, you can use a regex pattern to match marker IDs against, with the same requirements.
An example of a zoomLayers config:
zoomLayers:
[
 {
 "id": 1,
 "minZoom": 0.125,
 "categories": [ "2", "3", "6", "7", "9", "13", "15", "16", "21", "22" ]
 },
 {
 "id": 2,
 "minZoom": 0.353554,
 "categories": [ "4", "5", "10", "11", "12", "14", "17", "18", "19", "20" ]
 },
 {
 "id": 3,
 "minZoom": 0.75,
 "categories": [ "20", "23" ]
 }
]
====Notes====
* The visibility of zoom layers is recalculated at the end of a zoom in, and at the start of a zoom out.
* Markers that aren't in a layer are unaffected by zoom layers, they are always shown.
* A zoom scale of 1.0 means that the map background is being shown at its original resolution, 0.5 is half-resolution, 2.0 is double resolution etc. It might take some playing around to get to a set of values that feel right. As a starting point, your minZoom values should typically be in the range of 0 to 1, e.g. 0.125, 0.35, 0.75, in order of most zoomed out to least.
* While performing a search, zoom layers are temporarily disabled so that all of the results of a search may be shown at once without markers being hidden by the current zoom level.
==Configuration==
In order to change the default behaviours of MapsExtended, one or more configurations ("configs") may be defined. Configs are comprised of a series of key-value pairs called options which represent something that may influence a behaviour, or include data that is necessary to a specific feature.
Configurations are split into three separate scopes: Global which applies to all maps on the wiki, Local (also called "per-map") which applies to all instances of a specific map, i.e. the map on the Map namespace page and any transclusions of it, and Embed which applies only to an embedded/transcluded instance of a specific map.
All of these configurations may be present at once, which means a map will obtain it's configuration from all sources, depending on which options are present in each. You may also choose to not use a configuration at all, or omit options from all scopes if you don't wish to change the default behaviour. The order of prescendence is:
:Embed → Local → Global → Defaults
Options set in the embed config will always override the same options set in local, global, and defaults. Options set in the local config will always override options set in the global, and the defaults. Options set in the global config will always override defaults.
===Validation===
Extensive validation is performed on the loaded configurations of all maps, which checks to see whether the value provided for an option is valid and expected. If an option is invalid for whatever reason, it will "fall back" to the next available option. Strings will be coerced into numbers, boolean values, arrays, and objects if needed (so "0.5" and 0.5 are valid, "true" and true are valid, and "[\"foo\", \"bar\"]" and ["foo", "bar"] are treated the same).
A detailed breakdown of the config validation for each config is shown in two places, and only while in edit mode (or if using the URL query parameter debugMapsExtended=1:
* Below the source editor on a page in the map namespace (global, local)
* Below the source editor on any page that the map is transcluded (embed only)
===Configuration methods===
MapsExtended provides many different ways to define a configuration:
* JavaScript (local and global scopes), set on any MediaWiki ".js" page on your wiki (e.g. MediaWiki:Common.js)
** The window.mapsExtendedConfig object can be used to define a global configuration that is used by all maps.
** The window.mapsExtendedConfigs object can be used to define local/per-map configurations. Each config must be a separate object keyed by the map name exactly as it appears on the page (without the 'Map:' prefix). When using "configs", the special key "global" may be used as the global config, instead of needing to define two separate mapsExtendedConfig and mapsExtendedConfigs objects.
* JSON (local scope), set within the Map pages themselves, where the options are in the "config" object of any marker object in the markers array.
* JSON (local and global scopes), set within a special system message page MediaWiki:Custom-MapsExtended/<map>.json (where map is the name of your map without the 'Map:' prefix), similar to how I18n-js stores its messages.
* Wikitext (embed scope), on the page that the map is transcluded/embedded on, by wrapping the transclusion in a div block {{Map:<name>}}</data>, where each option is a data-* attribute on the div.
Only one config from each scope is loaded, where present. Configurations are checked for presence in the following order:
* Embed:
*# Wikitext
* Local:
*# JavaScript (mapsExtendedConfigs["Map Name"])
*# JSON (in map definition)
*# JSON (in system message)
* Global:
*# JavaScript (mapsExtendedConfigs["global"])
*# JavaScript (mapsExtendedConfig)
*# JSON (in system message)
* Defaults:
*# The defaults built into MapsExtended.js
===JavaScript configuration===
A configuration may be provided via JavaScript, in one of the script pages on your wiki (typically in MediaWiki:Common.js), stored in either the window.mapsExtendedConfig object, which applies to the global scope (to all maps on the wiki), or in window.mapsExtendedConfigs which applies to the local scope.
When using the latter, each config must be a separate object keyed by the map name exactly as it appears on the page (without the 'Map:' prefix). The special key "global" may be used as the global config, instead of needing to define two separate mapsExtendedConfig and mapsExtendedConfigs objects.
The code below contains an example of a configuration added to the MediaWiki:Common.js page on your wiki.
===JSON configuration (map definition)===
Maps may also be configured via the JSON definition of the map itself. This may be accomplished by adding a "config" object to any single marker in the markers array of the JSON. This might seem a little unorthodox, but it is done because marker objects are able to recieve any number of custom properties, unlike all other parts of the JSON which are sanitized when the map is saved, stripping out any properties that aren't part of the regular specification.
This config should persist through Interactive Map Editor edits, with the following caveats:
* Options using a boolean value must be stored as a string instead, for example true becomes "true" and false becomes "false", otherwise they will be replaced with an empty string (if true) or removed entirely (if false) when the IME edit is saved.
* If the marker that the config is attached to is removed, the config will be removed.
Ideally a config should be attached to an existing marker, instead of creating a new marker just to contain it, however if this is required, you can remove the category containing the config object by including it in the disabledCategories array.
===JSON configuration (system message)===
If required, a config may also be created in a "standalone" configuration page, a subpage of MediaWiki:Custom-MapsExtended with the suffix ".json". The suffix is used in order to indicate that MediaWiki should treat the page as JSON, without manually changing the content model).
A global scope configuration may be provided in the MediaWiki:Custom-MapsExtended/global.json subpage. Local scope configurations (i.e. ones that apply to a specific map) may be provided in a subpage with the same name as the map itself (without the "Map:" namespace prefix), e.g. MediaWiki:Custom-MapsExtended/Some map.json.
MediaWiki-namespace pages typically store system messages which are unable to be edited, but those prefixed with "Custom-" are whitelisted such that they can be edited by users with the "editsitejson" and "editinterface" right. This prefix is essentially a "free-for-use" space, and many scripts use it as a place to store configurations and such in JSON format.
The format of a system message JSON configuration is the same as the config object in a map definition configuration.
===Wikitext configuration===
The only option available for embed level map configuration is in wikitext. This is accomplished by wrapping the map in a tag and using data-* attributes for each option to be configured.
Alternatively, a single attribute data-config may be specified which contains the entire config object. If this is present, no other attributes will be used.
====Formatting and quotes====
Data attributes be named using either camelcase (e.g. data-openPopupsOnHover), or with hyphen notation (e.g. data-open-popups-on-hover), depending on preference.
Attribute values should be a serialized JSON token. As per the HTML standard, "the attribute value can remain unquoted if it doesn't contain ASCII whitespace or any of " ' ` = < or >. Otherwise, it has to be quoted using either single or double quotes."
* This works: data-some-boolean=true
* This works too: data-some-boolean="true" (will be coerced to a boolean)
* This will NOT work: data-some-string=foo bar
* This will work: data-some-string="foo bar"
However, note that JSON only supports double-quoted strings (i.e. strings inside the data value such as an object's keys and properties, and elements in a string array). So when you want double quotes to appear inside the string, the quotes surrounding the data attribute itself should be single quotes, so that an internal double quote doesn't close the attribute.
* This will work: data-some-array='["foo", "bar"]'
* This will work: data-some-array="[&quot;foo&quot;, &quot;bar&quot;]"
* This will NOT work (valid HTML attribute, but invalid JSON): data-some-array="['foo', 'bar']"
* This will NOT work (closes HTML attribute within value): data-some-array="["foo", "bar"]"
If you need to use single quotes inside a JSON string, but need to keep the outer single quotes, the single quote should be substituted with the equivalent HTML entity instead (&#39; or &apos;), likewise for any other values that may interfere with the formatting.
====Examples====
The above JSON config may be represented with the following wikitext:
Attributes may also be indented for clarity, for example the following is an equally valid attribute in wikitext as what is shown above for the config option categoryGroups:
===Comparison of different configuration methods===
Method 	Location	Pros	Cons
JavaScript 	MediaWiki:Common.js
* Local (when using mapsExtendedConfigs)
* Global (when using mapsExtendedConfig)	
* All configs may be located in one place.
* No chance of the config being affected by IME edits.
* No extra API calls required to fetch config, it is made available to MapsExtended immediately.	
* Pollutes your site JS with arbitrary data.
* Cannot easily preview changes without saving.
* Requires a script review via Special:JSPages before the changes are live.
* May only be edited by those allowed to edit .js pages ("editsitejs" permission)
* Syntax may be difficult for those unfamiliar with JS.
* Config keys must be renamed if the map page is moved.
JSON (in map definition)	Map:<name> (in config object of any marker)
* Local (applies to the very same map that the JSON is defining)	
* No external configuration required, the map and its config are unified.
* No extra API calls required to fetch config, it is made available to MapsExtended immediately.
* May be edited by anyone with the "editinteractivemap" permission (typically all users)
* Allows preview easily without saving the page.	
* Pollutes the Map pages with non-standard data.
* Long configs can suffer from excessive nesting, and disjointed data.
* Config cannot be placed anywhere in the JSON, it has to reside under a marker.
* Config may be lost or changed by IME edits.
JSON (as system message)	MediaWiki:Custom-MapsExtended/<name>.json
* Per map (applies to the Map of the same <name>)
* Global (when "Global" is used as the <name>)	
* No chance of the config being affected by IME edits.
* Keeps both the Map JSON and JS pages unpolluted.
* Most explicitly defined config, may be easily expanded and edited without affecting the actual map definition.
* Allows the use of a custom editor.	
* Requires a new page for every map that needs a per-map config.
* May only be edited by those allowed to edit .json pages ("editsitejson" permission).
* Doubles the amount of pages required for a map (one for the map data, one for the config).
* Requires a separate API call, and is therefore the slowest to load, at least until it is cached.
* Config page must be moved if the map page is ever moved.
Wikitext (inline) 	In the wikitext for a page the map is embedded on
* Embed (to the map within the div)	
* Allows fine-tune control of each embedded instance of the map.
* No extra API calls required to fetch config, it is made available to MapsExtended immediately.
* May be edited with anyone with permission to edit the page, unless a template with restricted permission is used.
* Allows preview easily without saving the page.	
* Inherantly only applies to the one embedded map, which may not be ideal if you also want to configure the map on the Map page itself.
* Using three layers of syntax may be confusing and cumbersome (JSON, in HTML, in Wikitext).
===List of config options===
Variable 	Description 	Data type 	Default 	
Core 	Core 	Core 	Core 	
disabled 	If set to true, MapsExtended will not run on this map. This may be used to quickly disable the script either per-map if you want to exclude certain maps, or to disable it wiki-wide without removing its entry from ImportJS. 	Boolean 	false 	
Marker customisation 	Marker customisation 	Marker customisation 	Marker customisation 	
iconAnchor 	The anchor point for custom icons. This is the position of the marker coordinate relative to the icon. Valid anchors are: top-left, top-center, top-right, center-left, center, center-right, bottom-left, bottom-center, bottom-right. 	String 	"center" 	
iconPosition 	An alias for the opposite of iconAnchor, and if present will override the iconAnchor (both should not be defined, you should use whichever you prefer). This is the position of the icon relative to the marker coordinate. Valid positions as as above. 	String 	undefined 	
sortMarkers 	Controls the order in which markers are rendered. Must be one of "latitude", "longitude", "category" or "unsorted" 	String 	"latitude" 	
Popup customisation 	Popup customisation 	Popup customisation 	Popup customisation 	
enablePopups 	May be used to disable popups entirely, in which case clicking on a marker will do nothing. Any poup-related functions will no-longer work. 	Boolean 	true 	
openPopupsOnHover 	Enables triggering popups when cursoring over markers. 	Boolean 	false 	
popupHideDelay 	(Only when openPopupsOnHover is true) Amount of time (in seconds) before the popup starts to close after the marker or popup is left. This is considered to be the "timeout" before a popup is hidden. Hiding a popup by selecting another popup will ignore this value, unless using useCustomPopups, in which case multiple popups may be on the screen at once.	Number 	0.5 	
popupShowDelay 	(Only when openPopupsOnHover is true) Amount of time (in seconds) before the popup opens after the marker is hovered over. 	Number 	0.0 	
useCustomPopups 	See popup display options above. 	Boolean 	false 	
Category customisation 	Category customisation 	Category customisation 	Category customisation 	
hiddenCategories 	List of category ID's that will be hidden by default. These categories are "unchecked" and must be toggled back on manually in the filters dropdown. 	Array of string 	- 	
visibleCategories 	List of category ID's that will be visible by default, all other categories will be hidden. This is essentially an inverse to the above. 	Array of string 	- 	
disabledCategories 	List of category ID's that will be disabled by default. These categories will be removed completely from the filters list. 	Array of string 	 	
categoryGroups 	Array of category group definitions which define the hierarchical layout of categories. Each definition must be either an object containing a label (string) and children (array of object/string) (and optionally the boolean fields hidden, collapsible and collapsed), or a string with the ID of a category. 	Array of object/string 	- 	
Map interface customisation 	Map interface customisation 	Map interface customisation	Map interface customisation 	Map interface customisation
minimalLayout 	Optional "minimal" layout. When true, the padding around the map will be removed, and the sidebar/search/filter controls will be overlayed as if the map is fullscreened. 	Boolean 	false 	
mapControls 	An array of four arrays of strings representing the map controls in each corner, in the order top-left, top-right, bottom-right, bottom-left. 	Array[4] of array of string	[[], ["edit"], ["fullscreen", "zoom"], []]	
hiddenControls 	Array of controls that should be hidden. 	Array of string 	[] 	
enableFullscreen 	Whether the map should show a fullscreen button. 	Boolean 	true 	
fullscreenMode 	The default function of the fullscreen button. Must be either "window" or "screen" 	String 	"window" 	
Sidebar 	Sidebar 	Sidebar 	Sidebar 	Sidebar
enableSidebar 	Controls whether the sidebar should be added to the map. 	Boolean 	false 	
sidebarOverlay 	If true, the sidebar will be overlayed instead of being side-by-side with the map 	Boolean 	false 	
sidebarSide 	The side of the map on which the sidebar is shown. Must be either "left" or "right". 	String 	"left" 	
sidebarBehaviour 	Controls the auto-show/hide behaviour of the sidebar (see the documentation above for more info). Valid values are autoAlways, autoInitial, and manual. 	String 	"autoInitial" 	
sidebarInitialState 	The initial state of the sidebar (see the documentation above for more info). Valid values are auto, show, hide 	String 	"auto" 	
Search 	Search 	Search 	Search 	
enableSearch 	Controls whether a search box should be added to the map. 	Boolean 	true 	
Tooltips 	Tooltips 	Tooltips 	Tooltips 	
enableTooltips 	Enable the use of tooltips shown when hovering over map markers. 	Boolean 	true 	
tooltipDirection 	Direction where to open the tooltip. Possible values are: "left", "right", "top", "bottom", "center", "auto". "auto" will dynamically switch between right and left according to the tooltip position on the map. 	String 	"auto" 	
tooltipOffset 	Optional offset of the tooltip position. When used with tooltipDirection "auto", the x offset is inverted when the tooltip is shown on the right of the marker. 	Array[2] of number 	[ 0, 0 ] 	
Collectibles 	Collectibles 	Collectibles 	Collectibles 	
collectibleCategories 	List of category ID's that may be collected (using the Collectibles module) 	Array of string 	- 	
enableCollectedAllNotification	Whether a banner notification should be shown when all markers of a category are collected. 	Boolean 	true 	
collectibleExpiryTime 	The time in seconds to keep collectible states in the browser's localStorage. -1 may be used to store the states forever, 0 may be used to not store the states at all. The default of 2629743 seconds equals 1 month. 	Number 	2629743 	
collectibleCheckboxStyle 	Used to select between MapsExtended and Fandom styled collectible checkboxes. The MX style uses a small simple checkbox next to the dropdown in the popup, while the Fandom style uses a more descriptive bar at the bottom of the popup.
Valid values are mx and fandom 	String 	"mx" 	
enableYourProgressFilter 	Used to enable (or disable) Fandom's "Your Progress" filter, which adds a section to the filters dropdown with two new filters "Complete" and "Incomplete" (these strings can be changed in your i18n overrides). When this is enabled, the "Clear complete" button will be moved inside the section. 	Boolean 	true 	
enableClearCollectedButton 	Used to enable (or disable) the "Clear collected" button shown in the filters dropdown. 	Boolean 	true 	
Zoom layers 	Zoom layers 	Zoom layers 	Zoom layers 	
zoomLayers 	An array of zoom layer objects, each of which can contain id, minZoom, maxZoom, categories, and markers. 	Array of object 	- 	
===URL parameters===
The following URL parameters may be passed to any page with an embedded map, and are used to influence the behaviour of MapExtended. These may also be set in localStorage, which may be useful if the URL parameters cannot be easily set.
* debugMapsExtended – Enables logging output to console if set to "1". By default only the "Loaded MapsExtended!" message is shown.
* disableMapsExtended – Disables MapsExtended on this page if set to "1". You can also use usesitejs=0 if disabling other scripts doesn't matter to you.
==Custom messages==
If you want to use customize the messages shown in your maps, add following code to anywhere on the MediaWiki:Common.js page on your wiki:
If you wish to add your own translations, feel free to contribute: MediaWiki:Custom-MapsExtended/i18n.json
==Dependencies==
MapsExtended depends on the following resources:
* i18n-js
* BannerNotification
* WDSIcons
And uses the following modules
* jquery.makeCollapsible
* oojs-ui-core
* oojs-ui-windows
==Examples==
If you make use of this script on your wiki, or create a map that uses its features, feel free to add it here!
* Community Wiki (DE)
* Conan Exiles Wiki
* Escape from Tarkov Wiki
* Genshin Impact Wiki (RU)
* Ghosts of Tabor Wiki
* Helldivers Wiki
* Hermitcraft Wiki
* Honkai: Star Rail Wiki (forked)
* The Infected Wiki
* Need for Speed Wiki
* NoPixel Wiki
* Pillars of Eternity Wiki (collectibles test)
* Warframe Wiki
==Planned features==
* Coordinates - Show the position of markers and the cursor in a custom coordinate system.
* Multi-level maps - Multiple background images per map, and the ability to switch between sets of markers for each image.
* Other types of markers - Including text, image, polygons, polylines, polypaths.
* Background tiling, which should drastically improve the performance and visual fidelity of maps with large backgrounds.
* Optional docked sidebar, instead of a dropdown menu.
* Scaling markers which do not stay the same size when zoomed in and out.
* Map report support for custom popups.
* Ruler, allowing users to measure distances on the map.
* HTML5 canvas support, allowing maps to render tens of thousands of markers with little to no lag.
==Known issues==
* Occasionally if the map is loaded in another tab (i.e. such that the page loads in the background), the script will not run. - Fixed (February 3, 2023)
* Clicking entries in the search box does not display marker popups for markers that are hidden outside of the viewport.
* The filters dropdown may start empty. If this occurs, clear your browser cache or wait until the cached CSS is renewed - the issue should not reoccur.
* ...