summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/Maps
diff options
context:
space:
mode:
authorYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
committerYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
commitfc7369835258467bf97eb64f184b93691f9a9fd5 (patch)
treedaabd60089d2dd76d9f5fb416b005fbe159c799d /www/wiki/extensions/Maps
first commit
Diffstat (limited to 'www/wiki/extensions/Maps')
-rw-r--r--www/wiki/extensions/Maps/.gitignore13
-rw-r--r--www/wiki/extensions/Maps/.scrutinizer.yml12
-rw-r--r--www/wiki/extensions/Maps/.travis.install.sh67
-rw-r--r--www/wiki/extensions/Maps/.travis.yml34
-rw-r--r--www/wiki/extensions/Maps/COPYING347
-rw-r--r--www/wiki/extensions/Maps/DefaultSettings.php365
-rw-r--r--www/wiki/extensions/Maps/INSTALL.md267
-rw-r--r--www/wiki/extensions/Maps/ISSUE_TEMPLATE.md15
-rw-r--r--www/wiki/extensions/Maps/Maps.php14
-rw-r--r--www/wiki/extensions/Maps/MapsRegistration.php57
-rwxr-xr-xwww/wiki/extensions/Maps/Maps_Settings.php17
-rw-r--r--www/wiki/extensions/Maps/README.md105
-rw-r--r--www/wiki/extensions/Maps/RELEASE-NOTES.md1302
-rw-r--r--www/wiki/extensions/Maps/composer.json74
-rw-r--r--www/wiki/extensions/Maps/extension.json280
-rw-r--r--www/wiki/extensions/Maps/i18n/af.json27
-rw-r--r--www/wiki/extensions/Maps/i18n/aliases/Aliases.php129
-rw-r--r--www/wiki/extensions/Maps/i18n/aln.json19
-rw-r--r--www/wiki/extensions/Maps/i18n/ar.json227
-rw-r--r--www/wiki/extensions/Maps/i18n/arc.json15
-rw-r--r--www/wiki/extensions/Maps/i18n/arz.json13
-rw-r--r--www/wiki/extensions/Maps/i18n/ast.json223
-rw-r--r--www/wiki/extensions/Maps/i18n/az.json13
-rw-r--r--www/wiki/extensions/Maps/i18n/ba.json15
-rw-r--r--www/wiki/extensions/Maps/i18n/bar.json8
-rw-r--r--www/wiki/extensions/Maps/i18n/be-tarask.json153
-rw-r--r--www/wiki/extensions/Maps/i18n/bg.json33
-rw-r--r--www/wiki/extensions/Maps/i18n/bjn.json11
-rw-r--r--www/wiki/extensions/Maps/i18n/bn.json114
-rw-r--r--www/wiki/extensions/Maps/i18n/br.json127
-rw-r--r--www/wiki/extensions/Maps/i18n/bs.json51
-rw-r--r--www/wiki/extensions/Maps/i18n/ca.json28
-rw-r--r--www/wiki/extensions/Maps/i18n/ce.json34
-rw-r--r--www/wiki/extensions/Maps/i18n/cs.json91
-rw-r--r--www/wiki/extensions/Maps/i18n/cu.json8
-rw-r--r--www/wiki/extensions/Maps/i18n/cv.json8
-rw-r--r--www/wiki/extensions/Maps/i18n/da.json64
-rw-r--r--www/wiki/extensions/Maps/i18n/de.json231
-rw-r--r--www/wiki/extensions/Maps/i18n/diq.json36
-rw-r--r--www/wiki/extensions/Maps/i18n/dsb.json77
-rw-r--r--www/wiki/extensions/Maps/i18n/el.json165
-rw-r--r--www/wiki/extensions/Maps/i18n/en-gb.json19
-rw-r--r--www/wiki/extensions/Maps/i18n/en.json225
-rw-r--r--www/wiki/extensions/Maps/i18n/eo.json15
-rw-r--r--www/wiki/extensions/Maps/i18n/es.json233
-rw-r--r--www/wiki/extensions/Maps/i18n/et.json40
-rw-r--r--www/wiki/extensions/Maps/i18n/eu.json78
-rw-r--r--www/wiki/extensions/Maps/i18n/fa.json194
-rw-r--r--www/wiki/extensions/Maps/i18n/fi.json158
-rw-r--r--www/wiki/extensions/Maps/i18n/fr.json243
-rw-r--r--www/wiki/extensions/Maps/i18n/frp.json84
-rw-r--r--www/wiki/extensions/Maps/i18n/fur.json12
-rw-r--r--www/wiki/extensions/Maps/i18n/fy.json11
-rw-r--r--www/wiki/extensions/Maps/i18n/ga.json8
-rw-r--r--www/wiki/extensions/Maps/i18n/gl.json220
-rw-r--r--www/wiki/extensions/Maps/i18n/grc.json9
-rw-r--r--www/wiki/extensions/Maps/i18n/gsw.json49
-rw-r--r--www/wiki/extensions/Maps/i18n/he.json217
-rw-r--r--www/wiki/extensions/Maps/i18n/hi.json26
-rw-r--r--www/wiki/extensions/Maps/i18n/hr.json92
-rw-r--r--www/wiki/extensions/Maps/i18n/hsb.json172
-rw-r--r--www/wiki/extensions/Maps/i18n/ht.json7
-rw-r--r--www/wiki/extensions/Maps/i18n/hu.json89
-rw-r--r--www/wiki/extensions/Maps/i18n/ia.json161
-rw-r--r--www/wiki/extensions/Maps/i18n/id.json139
-rw-r--r--www/wiki/extensions/Maps/i18n/ig.json8
-rw-r--r--www/wiki/extensions/Maps/i18n/it.json163
-rw-r--r--www/wiki/extensions/Maps/i18n/ja.json176
-rw-r--r--www/wiki/extensions/Maps/i18n/jv.json9
-rw-r--r--www/wiki/extensions/Maps/i18n/ka.json75
-rw-r--r--www/wiki/extensions/Maps/i18n/km.json18
-rw-r--r--www/wiki/extensions/Maps/i18n/ko.json208
-rw-r--r--www/wiki/extensions/Maps/i18n/ksh.json69
-rw-r--r--www/wiki/extensions/Maps/i18n/ku-latn.json13
-rw-r--r--www/wiki/extensions/Maps/i18n/lad.json13
-rw-r--r--www/wiki/extensions/Maps/i18n/lb.json123
-rw-r--r--www/wiki/extensions/Maps/i18n/lt.json119
-rw-r--r--www/wiki/extensions/Maps/i18n/lv.json13
-rw-r--r--www/wiki/extensions/Maps/i18n/magic/MagicWords.php387
-rw-r--r--www/wiki/extensions/Maps/i18n/map-bms.json13
-rw-r--r--www/wiki/extensions/Maps/i18n/mg.json24
-rw-r--r--www/wiki/extensions/Maps/i18n/mk.json226
-rw-r--r--www/wiki/extensions/Maps/i18n/ml.json9
-rw-r--r--www/wiki/extensions/Maps/i18n/mr.json9
-rw-r--r--www/wiki/extensions/Maps/i18n/ms.json158
-rw-r--r--www/wiki/extensions/Maps/i18n/mt.json48
-rw-r--r--www/wiki/extensions/Maps/i18n/myv.json9
-rw-r--r--www/wiki/extensions/Maps/i18n/nah.json6
-rw-r--r--www/wiki/extensions/Maps/i18n/nb.json228
-rw-r--r--www/wiki/extensions/Maps/i18n/nds.json24
-rw-r--r--www/wiki/extensions/Maps/i18n/nl-informal.json12
-rw-r--r--www/wiki/extensions/Maps/i18n/nl.json206
-rw-r--r--www/wiki/extensions/Maps/i18n/nn.json20
-rw-r--r--www/wiki/extensions/Maps/i18n/oc.json74
-rw-r--r--www/wiki/extensions/Maps/i18n/or.json10
-rw-r--r--www/wiki/extensions/Maps/i18n/pa.json72
-rw-r--r--www/wiki/extensions/Maps/i18n/pdc.json11
-rw-r--r--www/wiki/extensions/Maps/i18n/pfl.json53
-rw-r--r--www/wiki/extensions/Maps/i18n/pl.json170
-rw-r--r--www/wiki/extensions/Maps/i18n/pms.json174
-rw-r--r--www/wiki/extensions/Maps/i18n/ps.json26
-rw-r--r--www/wiki/extensions/Maps/i18n/pt-br.json231
-rw-r--r--www/wiki/extensions/Maps/i18n/pt.json233
-rw-r--r--www/wiki/extensions/Maps/i18n/qqq.json235
-rw-r--r--www/wiki/extensions/Maps/i18n/ro.json96
-rw-r--r--www/wiki/extensions/Maps/i18n/roa-tara.json65
-rw-r--r--www/wiki/extensions/Maps/i18n/ru.json251
-rw-r--r--www/wiki/extensions/Maps/i18n/rue.json13
-rw-r--r--www/wiki/extensions/Maps/i18n/sco.json9
-rw-r--r--www/wiki/extensions/Maps/i18n/si.json109
-rw-r--r--www/wiki/extensions/Maps/i18n/sk.json19
-rw-r--r--www/wiki/extensions/Maps/i18n/sl.json28
-rw-r--r--www/wiki/extensions/Maps/i18n/sq.json8
-rw-r--r--www/wiki/extensions/Maps/i18n/sr-ec.json28
-rw-r--r--www/wiki/extensions/Maps/i18n/sr-el.json26
-rw-r--r--www/wiki/extensions/Maps/i18n/sv.json232
-rw-r--r--www/wiki/extensions/Maps/i18n/sw.json8
-rw-r--r--www/wiki/extensions/Maps/i18n/ta.json63
-rw-r--r--www/wiki/extensions/Maps/i18n/te.json25
-rw-r--r--www/wiki/extensions/Maps/i18n/th.json9
-rw-r--r--www/wiki/extensions/Maps/i18n/tl.json158
-rw-r--r--www/wiki/extensions/Maps/i18n/tr.json84
-rw-r--r--www/wiki/extensions/Maps/i18n/tzm.json8
-rw-r--r--www/wiki/extensions/Maps/i18n/uk.json232
-rw-r--r--www/wiki/extensions/Maps/i18n/ur.json44
-rw-r--r--www/wiki/extensions/Maps/i18n/vep.json9
-rw-r--r--www/wiki/extensions/Maps/i18n/vi.json107
-rw-r--r--www/wiki/extensions/Maps/i18n/vo.json9
-rw-r--r--www/wiki/extensions/Maps/i18n/wa.json8
-rw-r--r--www/wiki/extensions/Maps/i18n/yi.json11
-rw-r--r--www/wiki/extensions/Maps/i18n/zh-hans.json235
-rw-r--r--www/wiki/extensions/Maps/i18n/zh-hant.json235
-rw-r--r--www/wiki/extensions/Maps/phpunit.xml.dist29
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/ext.maps.googlemaps3.js25
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/ext.sm.googlemaps3ajax.js48
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ProjectedOverlay.js139
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/README5
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ZipFile.complete.js2172
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/geoxml3.js1855
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/README6
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/googleearth-compiled.js20
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/markerclusterer.js1637
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/markerwithlabel.css13
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/markerwithlabel.js568
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/img/blue-dot.pngbin0 -> 1274 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/GoogleMaps/jquery.googlemap.js900
-rw-r--r--www/wiki/extensions/Maps/resources/editor/css/jquery.miniColors.css70
-rw-r--r--www/wiki/extensions/Maps/resources/editor/css/mapeditor.css79
-rw-r--r--www/wiki/extensions/Maps/resources/editor/images/circle.gifbin0 -> 78 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/editor/images/gradient.pngbin0 -> 6548 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/editor/images/line.gifbin0 -> 1104 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/editor/images/rainbow.pngbin0 -> 1665 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/editor/images/trigger.pngbin0 -> 538 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/editor/js/README3
-rw-r--r--www/wiki/extensions/Maps/resources/editor/js/jquery.miniColors.js567
-rw-r--r--www/wiki/extensions/Maps/resources/editor/js/mapeditor.iefixes.js28
-rw-r--r--www/wiki/extensions/Maps/resources/editor/js/mapeditor.js919
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/cluster/m1.pngbin0 -> 3288 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/cluster/m2.pngbin0 -> 3707 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/cluster/m3.pngbin0 -> 5605 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/cluster/m4.pngbin0 -> 6515 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/cluster/m5.pngbin0 -> 7249 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/ext.maps.leaflet.js4
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/ext.sm.leafletajax.js44
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/jquery.leaflet.js434
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet-providers/leaflet-providers.js774
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.editable/Leaflet.Editable.js1945
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.editor.js15
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/.jshintrc12
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.css4
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.js164
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/LICENSE22
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/README.md68
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/bower.json30
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen-2x.pngbin0 -> 228 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen.pngbin0 -> 153 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/index.html48
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/package.json25
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.markercluster/MarkerCluster.css14
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet.markercluster/leaflet.markercluster.js3
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet/images/layers-2x.pngbin0 -> 1259 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet/images/layers.pngbin0 -> 696 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon-2x.pngbin0 -> 4230 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon.pngbin0 -> 1870 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-shadow.pngbin0 -> 618 bytes
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet/leaflet.css635
-rw-r--r--www/wiki/extensions/Maps/resources/leaflet/leaflet/leaflet.js5
-rw-r--r--www/wiki/extensions/Maps/resources/maps.common.js4
-rw-r--r--www/wiki/extensions/Maps/resources/maps.services.js86
-rw-r--r--www/wiki/extensions/Maps/resources/sm.common.js76
-rw-r--r--www/wiki/extensions/Maps/src/DataAccess/CachingGeocoder.php46
-rw-r--r--www/wiki/extensions/Maps/src/DataAccess/JsonFileParser.php79
-rw-r--r--www/wiki/extensions/Maps/src/DataAccess/MapsFileFetcher.php26
-rw-r--r--www/wiki/extensions/Maps/src/DataAccess/MediaWikiFileUrlFinder.php31
-rw-r--r--www/wiki/extensions/Maps/src/DataAccess/PageContentFetcher.php40
-rw-r--r--www/wiki/extensions/Maps/src/Elements/BaseElement.php57
-rw-r--r--www/wiki/extensions/Maps/src/Elements/BaseFillableElement.php45
-rw-r--r--www/wiki/extensions/Maps/src/Elements/BaseStrokableElement.php63
-rw-r--r--www/wiki/extensions/Maps/src/Elements/Circle.php62
-rw-r--r--www/wiki/extensions/Maps/src/Elements/ImageOverlay.php36
-rw-r--r--www/wiki/extensions/Maps/src/Elements/Line.php69
-rw-r--r--www/wiki/extensions/Maps/src/Elements/Location.php157
-rw-r--r--www/wiki/extensions/Maps/src/Elements/Polygon.php44
-rw-r--r--www/wiki/extensions/Maps/src/Elements/Rectangle.php80
-rw-r--r--www/wiki/extensions/Maps/src/Elements/WmsOverlay.php71
-rw-r--r--www/wiki/extensions/Maps/src/FileUrlFinder.php18
-rw-r--r--www/wiki/extensions/Maps/src/GeoFunctions.php99
-rw-r--r--www/wiki/extensions/Maps/src/GoogleMapsService.php314
-rw-r--r--www/wiki/extensions/Maps/src/LeafletService.php184
-rw-r--r--www/wiki/extensions/Maps/src/MappingService.php33
-rw-r--r--www/wiki/extensions/Maps/src/MappingServices.php82
-rw-r--r--www/wiki/extensions/Maps/src/MapsFactory.php168
-rw-r--r--www/wiki/extensions/Maps/src/MapsFunctions.php218
-rw-r--r--www/wiki/extensions/Maps/src/MapsSetup.php206
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/Content/GeoJsonContent.php49
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/Content/GeoJsonContentHandler.php15
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/MapsHooks.php68
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/CoordinatesFunction.php102
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DisplayMapFunction.php171
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DisplayMapRenderer.php182
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DistanceFunction.php100
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/FindDestinationFunction.php120
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/GeoDistanceFunction.php117
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/GeocodeFunction.php113
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/MapsDocFunction.php200
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/SemanticMapsHooks.php115
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/Specials/MapEditorHTML.php221
-rw-r--r--www/wiki/extensions/Maps/src/MediaWiki/Specials/SpecialMapEditor.php69
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/CoordinateFormatter.php36
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/ElementJsonSerializer.php35
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/KmlFormatter.php78
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/MapHtmlBuilder.php37
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/MapsDistanceParser.php133
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/ParameterExtractor.php47
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/WikitextParser.php30
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/WikitextParsers/CircleParser.php86
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/WikitextParsers/DistanceParser.php38
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/WikitextParsers/ImageOverlayParser.php83
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/WikitextParsers/LineParser.php163
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/WikitextParsers/LocationParser.php147
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/WikitextParsers/PolygonParser.php41
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/WikitextParsers/RectangleParser.php89
-rw-r--r--www/wiki/extensions/Maps/src/Presentation/WikitextParsers/WmsOverlayParser.php49
-rw-r--r--www/wiki/extensions/Maps/src/SemanticMW/DataValues/CoordinateValue.php266
-rw-r--r--www/wiki/extensions/Maps/src/SemanticMW/ResultPrinters/KmlPrinter.php146
-rw-r--r--www/wiki/extensions/Maps/src/SemanticMW/ResultPrinters/MapPrinter.php403
-rw-r--r--www/wiki/extensions/Maps/src/SemanticMW/ResultPrinters/QueryHandler.php511
-rw-r--r--www/wiki/extensions/Maps/src/SemanticMW/ValueDescriptions/AreaDescription.php146
-rw-r--r--www/wiki/extensions/Maps/src/SemanticMW/ValueDescriptions/CoordinateDescription.php74
-rw-r--r--www/wiki/extensions/Maps/src/SemanticMaps.php74
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/DataAccess/MediaWikiFileUrlFinderTest.php33
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/InitializationTest.php20
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/MapsDistanceParserTest.php177
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/MapsMapperTest.php42
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/CoordinatesTest.php125
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/DistanceTest.php91
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/FinddestinationTest.php94
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/GeocodeTest.php64
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/GeodistanceTest.php81
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/MapsDocTest.php54
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/ParserHookTest.php133
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/Parser/CoordinatesTest.php96
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/Parser/DisplayMapTest.php220
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/Semantic/ValueDescriptions/AreaDescriptionTest.php81
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/parsers/CircleParserTest.php43
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/parsers/DistanceParserTest.php66
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/parsers/JsonFileParserTest.php120
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/parsers/LineParserTest.php77
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/parsers/LocationParserTest.php119
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/parsers/RectlangleParserTest.php44
-rw-r--r--www/wiki/extensions/Maps/tests/Integration/parsers/WmsOverlayParserTest.php54
-rw-r--r--www/wiki/extensions/Maps/tests/System/SemanticMW/MapQueryTest.php93
-rw-r--r--www/wiki/extensions/Maps/tests/Unit/Elements/BaseElementTest.php85
-rw-r--r--www/wiki/extensions/Maps/tests/Unit/Elements/CircleTest.php70
-rw-r--r--www/wiki/extensions/Maps/tests/Unit/Elements/ImageOverlayTest.php52
-rw-r--r--www/wiki/extensions/Maps/tests/Unit/Elements/LineTest.php74
-rw-r--r--www/wiki/extensions/Maps/tests/Unit/Elements/LocationTest.php36
-rw-r--r--www/wiki/extensions/Maps/tests/Unit/Elements/PolygonTest.php68
-rw-r--r--www/wiki/extensions/Maps/tests/Unit/Elements/RectangleTest.php73
-rw-r--r--www/wiki/extensions/Maps/tests/Unit/Presentation/KmlFormatterTest.php88
-rw-r--r--www/wiki/extensions/Maps/tests/Unit/Presentation/ParameterExtractorTest.php80
-rw-r--r--www/wiki/extensions/Maps/tests/Unit/Semantic/DataValues/CoordinateValueTest.php113
-rw-r--r--www/wiki/extensions/Maps/tests/Util/PageCreator.php24
-rw-r--r--www/wiki/extensions/Maps/tests/Util/TestFactory.php21
-rw-r--r--www/wiki/extensions/Maps/tests/bootstrap.php33
285 files changed, 36015 insertions, 0 deletions
diff --git a/www/wiki/extensions/Maps/.gitignore b/www/wiki/extensions/Maps/.gitignore
new file mode 100644
index 00000000..5fe6d62c
--- /dev/null
+++ b/www/wiki/extensions/Maps/.gitignore
@@ -0,0 +1,13 @@
+*~
+*.kate-swp
+
+!.*
+.idea/
+
+vendor/
+extensions/
+
+composer.phar
+composer.lock
+
+phpunit.phar \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/.scrutinizer.yml b/www/wiki/extensions/Maps/.scrutinizer.yml
new file mode 100644
index 00000000..19751cec
--- /dev/null
+++ b/www/wiki/extensions/Maps/.scrutinizer.yml
@@ -0,0 +1,12 @@
+inherit: true
+
+tools:
+ external_code_coverage: true
+ php_code_sniffer: true
+ php_cpd: true
+ php_cs_fixer: true
+ php_loc: true
+ php_mess_detector: true
+ php_pdepend: true
+ php_analyzer: true
+ sensiolabs_security_checker: true
diff --git a/www/wiki/extensions/Maps/.travis.install.sh b/www/wiki/extensions/Maps/.travis.install.sh
new file mode 100644
index 00000000..8d155391
--- /dev/null
+++ b/www/wiki/extensions/Maps/.travis.install.sh
@@ -0,0 +1,67 @@
+#! /bin/bash
+
+set -x
+
+originalDirectory=$(pwd)
+
+cd ..
+
+wget https://github.com/wikimedia/mediawiki-core/archive/$MW.tar.gz
+tar -zxf $MW.tar.gz
+mv mediawiki-$MW phase3
+
+cd phase3
+
+composer install --prefer-source
+
+if [ "$DB" == "postgres" ]
+then
+ psql -c 'create database its_a_mw;' -U postgres
+ php maintenance/install.php --dbtype $DBTYPE --dbuser postgres --dbname its_a_mw --pass nyan TravisWiki admin --scriptpath /TravisWiki
+else
+ mysql -e 'create database its_a_mw;'
+ php maintenance/install.php --dbtype $DBTYPE --dbuser root --dbname its_a_mw --dbpath $(pwd) --pass nyan TravisWiki admin --scriptpath /TravisWiki
+fi
+
+cd extensions
+cp -r $originalDirectory Maps
+cd Maps
+composer install --prefer-source
+cd ..
+cd ..
+
+if [ ! -z $SMW ]
+then
+ composer require "mediawiki/semantic-media-wiki=$SMW" --prefer-source
+fi
+
+cat <<EOT >> composer.local.json
+{
+ "extra": {
+ "merge-plugin": {
+ "merge-dev": true,
+ "include": [
+ "extensions/*/composer.json"
+ ]
+ }
+ }
+}
+EOT
+
+composer install --prefer-source
+
+echo 'wfLoadExtension( "Maps" );' >> LocalSettings.php
+
+if [ ! -z $SMW ]
+then
+ echo 'wfLoadExtension( "SemanticMediaWiki" );' >> LocalSettings.php
+fi
+
+echo 'error_reporting(E_ALL| E_STRICT);' >> LocalSettings.php
+echo 'ini_set("display_errors", 1);' >> LocalSettings.php
+echo '$wgShowExceptionDetails = true;' >> LocalSettings.php
+echo '$wgShowDBErrorBacktrace = true;' >> LocalSettings.php
+echo '$wgDevelopmentWarnings = true;' >> LocalSettings.php
+echo "putenv( 'MW_INSTALL_PATH=$(pwd)' );" >> LocalSettings.php
+
+php maintenance/update.php --quick
diff --git a/www/wiki/extensions/Maps/.travis.yml b/www/wiki/extensions/Maps/.travis.yml
new file mode 100644
index 00000000..49373ca8
--- /dev/null
+++ b/www/wiki/extensions/Maps/.travis.yml
@@ -0,0 +1,34 @@
+language: php
+
+services:
+ - mysql
+
+sudo: false
+
+matrix:
+ fast_finish: true
+ include:
+ - env: DBTYPE=mysql; MW=1.31.3
+ php: 7.1
+ - env: DBTYPE=mysql; MW=master; SMW=dev-master
+ php: 7.3
+ - env: DBTYPE=sqlite; MW=1.31.3; SMW=3.0.0
+ php: 7.1
+ - env: DBTYPE=sqlite; MW=master; TYPE=coverage
+ php: 7.2
+
+install:
+ - travis_retry composer self-update
+ - bash .travis.install.sh
+
+script: ../phase3/tests/phpunit/phpunit.php -c ../phase3/extensions/Maps/phpunit.xml.dist
+
+after_success:
+ - if [[ "$TYPE" != "coverage" ]]; then exit 0; fi
+ - ../phase3/tests/phpunit/phpunit.php -c ../phase3/extensions/Maps/phpunit.xml.dist --coverage-clover coverage.clover
+ - wget https://scrutinizer-ci.com/ocular.phar
+ - php ocular.phar code-coverage:upload --format=php-clover coverage.clover
+
+cache:
+ directories:
+ - $HOME/.composer/cache
diff --git a/www/wiki/extensions/Maps/COPYING b/www/wiki/extensions/Maps/COPYING
new file mode 100644
index 00000000..ebba08a4
--- /dev/null
+++ b/www/wiki/extensions/Maps/COPYING
@@ -0,0 +1,347 @@
+The license text below "----" applies to all files within this distribution, other
+than those that are in a directory which contains files named "LICENSE" or
+"COPYING", or a subdirectory thereof. For those files, the license text contained in
+said file overrides any license information contained in directories of smaller depth.
+Alternative licenses are typically used for software that is provided by external
+parties, and merely packaged with this software for convenience.
+----
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/www/wiki/extensions/Maps/DefaultSettings.php b/www/wiki/extensions/Maps/DefaultSettings.php
new file mode 100644
index 00000000..a3834788
--- /dev/null
+++ b/www/wiki/extensions/Maps/DefaultSettings.php
@@ -0,0 +1,365 @@
+<?php
+
+// This file lists all settings you can use to configure the Maps extension and their default values.
+// Do not modify this file to change settings. See https://www.semantic-mediawiki.org/wiki/Maps/Configuration
+// Do not include this file in LocalSettings. Instead include Maps_Settings.php.
+
+return [
+ // Mapping services that will be available in the wiki.
+ // These can be used in #display_map with service=leaflet or in #ask with format=leaflet
+ 'egMapsAvailableServices' => [
+ 'leaflet',
+ 'googlemaps3'
+ ],
+
+ // The mapping service that will be used when no service is specified by the user.
+ 'egMapsDefaultService' => 'leaflet',
+
+ // Allows disabling the extension even when it is installed.
+ // CAUTION: this setting is intended for wiki farms. On single wiki installations,
+ // the recommended way to disable maps is to uninstall it via Composer. Disabling
+ // Maps via this setting undermines package management safety: extensions that depend
+ // on Maps will likely either break or disable themselves.
+ 'egMapsDisableExtension' => false,
+
+ // Allows disabling the Semantic MediaWiki integration.
+ 'egMapsDisableSmwIntegration' => false,
+
+
+
+
+
+ /**
+ * GENERAL MAP CONFIGURATION
+ */
+
+ // Integer or string. The default width and height of a map. These values will
+ // only be used when the user does not provide them.
+ 'egMapsMapWidth' => 'auto',
+ 'egMapsMapHeight' => 350,
+
+ // Strings. The default content for all pop-ups. This value will only be used
+ // when the user does not provide one.
+ 'egMapsDefaultTitle' => '',
+ 'egMapsDefaultLabel' => '',
+
+ 'egMapsResizableByDefault' => false,
+ 'egMapsRezoomForKML' => false,
+
+ // Boolean. Sets if pages with maps should be put in special category
+ 'egMapsEnableCategory' => false,
+
+ // Integer. Determines the TTL of cached GeoJson.
+ // Default value: 0 (no caching).
+ 'egMapsGeoJsonCacheTtl' => 0,
+
+
+
+
+
+ /**
+ * SEMANTIC MEDIAWIKI INTEGRATION
+ */
+
+ // Boolean. The default value for the showtitle parameter. Will hide the title in the marker pop-ups when set to false.
+ // This value will only be used when the user does not provide one.
+ 'smgQPShowTitle' => true,
+
+ // Boolean. The default value for the hidenamespace parameter. Will hide the namespace in the marker pop-ups when set to true.
+ // This value will only be used when the user does not provide one.
+ 'smgQPHideNamespace' => false,
+
+ // String or false. Allows you to define the content and it's layout of marker pop-ups via a template.
+ // This value will only be used when the user does not provide one.
+ 'smgQPTemplate' => false,
+
+
+
+
+
+ /**
+ * COORDINATE CONFIGURATION
+ */
+
+ // The coordinate notations that should be available.
+ 'egMapsAvailableCoordNotations' => [
+ 'float',
+ 'dms',
+ 'dm',
+ 'dd'
+ ],
+
+ // The default output format of coordinates.
+ // Possible values: float, dms, dm, dd
+ 'egMapsCoordinateNotation' => 'dms',
+
+ // Boolean. Indicates if coordinates should be outputted in directional notation by default.
+ // Recommended to be true for dms and false for float.
+ 'egMapsCoordinateDirectional' => true,
+
+ // The default output format of coordinates when displayed by Semantic MediaWiki.
+ // Possible values: float, dms, dm, dd
+ 'smgQPCoodFormat' => 'dms',
+
+ // Boolean. Indicates if coordinates should be outputted in directional notation by default when
+ // displayed by Semantic MediaWiki.
+ 'smgQPCoodDirectional' => true,
+
+ // Boolean. Sets if direction labels should be translated to their equivalent in the wiki language or not.
+ 'egMapsInternatDirectionLabels' => true,
+
+ // Boolean. When false, the #coordinates parser function will not be enabled.
+ // This is useful for people using the GeoData extension and want to use its #coordinates function instead.
+ 'egMapsEnableCoordinateFunction' => true,
+
+
+
+
+
+ /**
+ * GEOCODING CONFIGURATION
+ */
+
+ // Sets which service should be used to turn addresses into coordinates
+ // Available services: geonames, google, nominatim
+ // The geonames service requires you to specify a geonames user (see below),
+ // if you set this setting to geonames but do not specify the user, Maps will
+ // fall back to using the google service.
+ 'egMapsDefaultGeoService' => 'nominatim',
+
+ // String. GeoNames API user/application name.
+ // Obtain an account here: http://www.geonames.org/login
+ // Do not forget to activate your account for API usage!
+ 'egMapsGeoNamesUser' => '',
+
+ // Boolean. Sets if geocoded addresses should be stored in a cache.
+ 'egMapsEnableGeoCache' => true,
+
+ // Integer. If egMapsEnableGeoCache is true, determines the TTL of cached geocoded addresses.
+ // Default value: 1 day.
+ 'egMapsGeoCacheTtl' => 24 * 3600,
+
+
+
+
+
+ /**
+ * LEAFLET CONFIGURATION
+ */
+
+ // Integer. The default zoom of a map. This value will only be used when the
+ // user does not provide one.
+ 'egMapsLeafletZoom' => 14,
+
+ // String. The default layer for Leaflet. This value will only be
+ // used when the user does not provide one.
+ 'egMapsLeafletLayer' => 'OpenStreetMap',
+ 'egMapsLeafletLayers' => [ 'OpenStreetMap' ],
+
+ 'egMapsLeafletOverlayLayers' => [],
+
+ // The definitions for the layers that should be available for the user.
+ 'egMapsLeafletAvailableLayers' => [
+ 'OpenStreetMap' => true,
+ 'OpenStreetMap.DE' => true,
+ 'OpenStreetMap.BlackAndWhite' => true,
+ 'OpenStreetMap.HOT' => true,
+ 'OpenTopoMap' => true,
+ 'Thunderforest.OpenCycleMap' => true,
+ 'Thunderforest.Transport' => true,
+ 'Thunderforest.TransportDark' => true,
+ 'Thunderforest.SpinalMap' => true,
+ 'Thunderforest.Landscape' => true,
+ 'Thunderforest.Outdoors' => true,
+ 'Thunderforest.Pioneer' => true,
+ 'OpenMapSurfer.Roads' => true,
+ 'OpenMapSurfer.Grayscale' => true,
+ 'Hydda.Full' => true,
+ 'Hydda.Base' => true,
+ //'MapBox' => false, // todo: implement setting api key
+ 'Stamen.Toner' => true,
+ 'Stamen.TonerBackground' => true,
+ 'Stamen.TonerHybrid' => true,
+ 'Stamen.TonerLines' => true,
+ 'Stamen.TonerLabels' => true,
+ 'Stamen.TonerLite' => true,
+ 'Stamen.Watercolor' => true,
+ 'Stamen.Terrain' => true,
+ 'Stamen.TerrainBackground' => true,
+ 'Stamen.TopOSMRelief' => true,
+ 'Stamen.TopOSMFeatures' => true,
+ 'Esri.WorldStreetMap' => true,
+ 'Esri.DeLorme' => true,
+ 'Esri.WorldTopoMap' => true,
+ 'Esri.WorldImagery' => true,
+ 'Esri.WorldTerrain' => true,
+ 'Esri.WorldShadedRelief' => true,
+ 'Esri.WorldPhysical' => true,
+ 'Esri.OceanBasemap' => true,
+ 'Esri.NatGeoWorldMap' => true,
+ 'Esri.WorldGrayCanvas' => true,
+ 'MapQuestOpen' => true,
+ //'HERE' => false, // todo: implement setting api key
+ 'FreeMapSK' => true,
+ 'MtbMap' => true,
+ 'CartoDB.Positron' => true,
+ 'CartoDB.PositronNoLabels' => true,
+ 'CartoDB.PositronOnlyLabels' => true,
+ 'CartoDB.DarkMatter' => true,
+ 'CartoDB.DarkMatterNoLabels' => true,
+ 'CartoDB.DarkMatterOnlyLabels' => true,
+ 'HikeBike.HikeBike' => true,
+ 'HikeBike.HillShading' => true,
+ 'BasemapAT.basemap' => true,
+ 'BasemapAT.grau' => true,
+ 'BasemapAT.overlay' => true,
+ 'BasemapAT.highdpi' => true,
+ 'BasemapAT.orthofoto' => true,
+ 'NASAGIBS.ModisTerraTrueColorCR' => true,
+ 'NASAGIBS.ModisTerraBands367CR' => true,
+ 'NASAGIBS.ViirsEarthAtNight2012' => true,
+ 'NLS' => true,
+ 'GeoportailFrance' => true,
+ 'GeoportailFrance.parcels' => true,
+ 'GeoportailFrance.ignMaps' => true,
+ 'GeoportailFrance.orthos' => true
+ ],
+
+ 'egMapsLeafletAvailableOverlayLayers' => [
+ 'OpenMapSurfer.AdminBounds' => true,
+ 'OpenSeaMap' => true,
+ 'OpenWeatherMap.Clouds' => true,
+ 'OpenWeatherMap.CloudsClassic' => true,
+ 'OpenWeatherMap.Precipitation' => true,
+ 'OpenWeatherMap.PrecipitationClassic' => true,
+ 'OpenWeatherMap.Rain' => true,
+ 'OpenWeatherMap.RainClassic' => true,
+ 'OpenWeatherMap.Pressure' => true,
+ 'OpenWeatherMap.PressureContour' => true,
+ 'OpenWeatherMap.Wind' => true,
+ 'OpenWeatherMap.Temperature' => true,
+ 'OpenWeatherMap.Snow' => true,
+ 'Hydda.RoadsAndLabels' => true,
+ 'NASAGIBS.ModisTerraLSTDay' => true,
+ 'NASAGIBS.ModisTerraSnowCover' => true,
+ 'NASAGIBS.ModisTerraAOD' => true,
+ 'NASAGIBS.ModisTerraChlorophyll' => true
+ ],
+
+ 'egMapsLeafletLayersApiKeys' => [
+ 'MapBox' => '',
+ 'MapQuestOpen' => '',
+ 'Thunderforest' => '',
+ 'GeoportailFrance' => ''
+ ],
+
+ 'egMapsLeafletLayerDependencies' => [
+ 'MapQuestOpen' => 'https://open.mapquestapi.com/sdk/leaflet/v2.2/mq-map.js?key=',
+ ],
+
+
+
+
+
+ /**
+ * GOOGLE MAPS CONFIGURATION
+ */
+
+ // String. Google Maps v3 API Key
+ 'egMapsGMaps3ApiKey' => '',
+
+ // String. Google Maps v3 API version number
+ 'egMapsGMaps3ApiVersion' => '',
+
+ // Integer. The default zoom of a map. This value will only be used when the
+ // user does not provide one.
+ 'egMapsGMaps3Zoom' => 14,
+
+ // Array of String. The Google Maps v3 default map types. This value will only
+ // be used when the user does not provide one.
+ 'egMapsGMaps3Types' => [
+ 'roadmap',
+ 'satellite',
+ 'hybrid',
+ 'terrain'
+ ],
+
+ // String. The default map type. This value will only be used when the user
+ // does not provide one.
+ 'egMapsGMaps3Type' => 'roadmap',
+
+ // Array. List of controls to display onto maps by default.
+ 'egMapsGMaps3Controls' => [
+ 'pan',
+ 'zoom',
+ 'type',
+ 'scale',
+ 'streetview',
+ 'rotate'
+ ],
+
+ // String. The default style for the type control.
+ // horizontal, vertical or default
+ 'egMapsGMaps3DefTypeStyle' => 'default',
+
+ // String. The default style for the zoom control.
+ // small, large or default
+ 'egMapsGMaps3DefZoomStyle' => 'default',
+
+ // Boolean. Open the info windows on load by default?
+ 'egMapsGMaps3AutoInfoWindows' => false,
+
+ // Array. Layers to load by default.
+ // traffic, bicycling and transit
+ 'egMapsGMaps3Layers' => [],
+
+ // Show points of interest or not.
+ 'egMapsShowPOI' => true,
+
+ // String. Set the language when rendering Google Maps.
+ 'egMapsGMaps3Language' => '',
+
+
+
+
+
+ /**
+ * DISTANCE CONFIGURATION
+ */
+
+ // Array. A list of units (keys) and how many meters they represent (value).
+ // No spaces! If the unit consists out of multiple words, just write them together.
+ 'egMapsDistanceUnits' => [
+ 'm' => 1,
+ 'meter' => 1,
+ 'meters' => 1,
+ 'km' => 1000,
+ 'kilometers' => 1000,
+ 'kilometres' => 1000,
+ 'mi' => 1609.344,
+ 'mile' => 1609.344,
+ 'miles' => 1609.344,
+ 'nm' => 1852,
+ 'nauticalmile' => 1852,
+ 'nauticalmiles' => 1852,
+ ],
+
+ // String. The default unit for distances.
+ 'egMapsDistanceUnit' => 'm',
+
+ // Integer. The default amount of fractal digits in a distance.
+ 'egMapsDistanceDecimals' => 2,
+
+
+
+
+
+ /**
+ * DEBUGGING
+ */
+
+ // When true, debugging messages will be logged using mw.log(). Do not use on production wikis.
+ 'egMapsDebugJS' => false,
+
+ 'egMapsGlobalJSVars' => [],
+]; \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/INSTALL.md b/www/wiki/extensions/Maps/INSTALL.md
new file mode 100644
index 00000000..20a4b3ad
--- /dev/null
+++ b/www/wiki/extensions/Maps/INSTALL.md
@@ -0,0 +1,267 @@
+# Maps installation
+
+These are the installation and configuration instructions for the [Maps extension](README.md).
+
+## Download and installation
+
+See the [installation instructions](https://www.semantic-mediawiki.org/wiki/Maps/Installation)
+
+## Configuration
+
+See the [configuration documentation](https://www.semantic-mediawiki.org/wiki/Maps/Configuration)
+
+## Platform compatibility and release status
+
+The PHP and MediaWiki version ranges listed are those in which Maps is known to work. It might also
+work with more recent versions of PHP and MediaWiki, though this is not guaranteed. Increases of
+minimum requirements are indicated in bold. For a detailed list of changes, see the [release notes](RELEASE-NOTES.md).
+
+<table>
+ <tr>
+ <th>Maps</th>
+ <th>PHP</th>
+ <th>MediaWiki</th>
+ <th>Semantic<br>MediaWiki</th>
+ <th>Release status</th>
+ </tr>
+ <tr>
+ <th>7.4.x</th>
+ <td>7.1 - 7.4+</td>
+ <td>1.31 - 1.33+</td>
+ <td>3.0+</td>
+ <td>Planned Q3 2019</td>
+ </tr>
+ <tr>
+ <th>7.3.x</th>
+ <td>7.1 - 7.4</td>
+ <td>1.31 - 1.33</td>
+ <td>3.0 - 3.1</td>
+ <td><strong>Stable release</strong></td>
+ </tr>
+ <tr>
+ <th>7.2.x</th>
+ <td>7.1 - 7.3</td>
+ <td>1.31 - 1.32</td>
+ <td>3.0.x</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
+ <th>7.1.x</th>
+ <td>7.1 - 7.3</td>
+ <td>1.31 - 1.32</td>
+ <td>3.0.x</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
+ <th>7.0.x</th>
+ <td>7.1 - 7.3</td>
+ <td>1.31 - 1.32</td>
+ <td><strong>3.0.x</strong></td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
+ <th>6.3.x</th>
+ <td>7.1 - 7.2</td>
+ <td>1.31 - 1.32</td>
+ <td>2.4 - 3.0</td>
+ <td>Security fixes only</td>
+ </tr>
+ <tr>
+ <th>6.2.x</th>
+ <td>7.1 - 7.2</td>
+ <td>1.31 - 1.32</td>
+ <td>2.4 - 3.0</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
+ <th>6.1.x</th>
+ <td>7.1 - 7.2</td>
+ <td>1.31 - 1.32</td>
+ <td>2.4 - 3.0</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
+ <th>6.0.x</th>
+ <td><strong>7.1</strong> - 7.2</td>
+ <td><strong>1.31</strong> - 1.32</td>
+ <td><strong>2.4</strong> - 3.0</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
+ <th>5.6.x</th>
+ <td>7.0 - 7.2</td>
+ <td>1.27 - 1.31</td>
+ <td>2.1 - 3.0</td>
+ <td>Security fixes only</td>
+ </tr>
+ <tr>
+ <th>5.5.x</th>
+ <td>7.0 - 7.2</td>
+ <td>1.27 - 1.31</td>
+ <td>2.1 - 2.5</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
+ <th>5.4.x</th>
+ <td>7.0 - 7.2</td>
+ <td>1.27 - 1.31</td>
+ <td>2.1 - 2.5</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
+ <th>5.3.x</th>
+ <td>7.0 - 7.2</td>
+ <td>1.27 - 1.30</td>
+ <td>2.1 - 2.5</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
+ <th>5.2.x</th>
+ <td>7.0 - 7.2</td>
+ <td>1.27 - 1.30</td>
+ <td>2.1 - 2.5</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
+ <th>5.1.x</th>
+ <td><strong>7.0</strong> - 7.2</td>
+ <td>1.27 - 1.30</td>
+ <td>2.1 - 2.5</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
+ <th>5.0.x</th>
+ <td>5.6 - 7.1</td>
+ <td>1.27 - 1.30</td>
+ <td>2.1 - 2.5</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
+ <th>4.4.x</th>
+ <td>5.6 - 7.1</td>
+ <td>1.27 - 1.29</td>
+ <td>2.1 - 2.5</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
+ <th>4.3.x</th>
+ <td><strong>5.6</strong> - 7.1</td>
+ <td><strong>1.27</strong> - 1.29</td>
+ <td>2.1 - 2.5</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
+ <th>4.2.x</th>
+ <td>5.5 - 7.1</td>
+ <td>1.23 - 1.29</td>
+ <td>2.1 - 2.5</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
+ <th>4.1.x</th>
+ <td>5.5 - 7.1</td>
+ <td>1.23 - 1.28</td>
+ <td>2.1 - 2.5</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+ <tr>
+ <th>4.0.x</th>
+ <td>5.5 - 7.0</td>
+ <td>1.23 - 1.28</td>
+ <td>2.1 - 2.4</td>
+ <td>Obsolete release, no support</td>
+ </tr>
+</table>
+
+Older obsolete versions:
+
+<table>
+ <tr>
+ <th>Maps</th>
+ <th>PHP</th>
+ <th>MediaWiki</th>
+ <th>Composer</th>
+ <th>Validator</th>
+ </tr>
+ <tr>
+ <th>3.8.x</th>
+ <td>5.5 - 7.0</td>
+ <td>1.23 - 1.27</td>
+ <td>Required</td>
+ <td>Handled by Composer</td>
+ </tr>
+ <tr>
+ <th>3.7.x</th>
+ <td>5.5 - 7.0</td>
+ <td>1.23 - 1.27</td>
+ <td>Required</td>
+ <td>Handled by Composer</td>
+ </tr>
+ <tr>
+ <th>3.6.x</th>
+ <td><strong>5.5</strong> - 7.0</td>
+ <td><strong>1.23</strong> - 1.27</td>
+ <td>Required</td>
+ <td>Handled by Composer</td>
+ </tr>
+ <tr>
+ <th>3.5.x</th>
+ <td>5.3.2 - 7.0</td>
+ <td>1.18 - 1.27</td>
+ <td>Required</td>
+ <td>Handled by Composer</td>
+ </tr>
+ <tr>
+ <th>3.4.x</th>
+ <td>5.3.2 - 7.0</td>
+ <td>1.18 - 1.27</td>
+ <td>Required</td>
+ <td>Handled by Composer</td>
+ </tr>
+ <tr>
+ <th>3.3.x</th>
+ <td>5.3.2 - 5.6.x</td>
+ <td>1.18 - 1.25</td>
+ <td>Required</td>
+ <td>Handled by Composer</td>
+ </tr>
+ <tr>
+ <th>3.2.x</th>
+ <td>5.3.2 - 5.6.x</td>
+ <td>1.18 - 1.24</td>
+ <td>Required</td>
+ <td>Handled by Composer</td>
+ </tr>
+ <tr>
+ <th>3.1.x</th>
+ <td>5.3.2 - 5.6.x</td>
+ <td>1.18 - 1.24</td>
+ <td>Required</td>
+ <td>Handled by Composer</td>
+ </tr>
+ <tr>
+ <th>3.0.x</th>
+ <td>5.3.2 - 5.6.x</td>
+ <td>1.18 - 1.23</td>
+ <td>Required</td>
+ <td>Handled by Composer</td>
+ </tr>
+ <tr>
+ <th>2.0.x</th>
+ <td><strong>5.3.2</strong> - 5.5.x</td>
+ <td><strong>1.18</strong> - 1.23</td>
+ <td>Not supported</td>
+ <td>0.5.1</td>
+ </tr>
+ <tr>
+ <th>1.0.5</th>
+ <td>5.2.0 - 5.3.x</td>
+ <td>1.17 - 1.19</td>
+ <td>Not supported</td>
+ <td>0.4.13 or 0.4.14</td>
+ </tr>
+</table>
+
+### Database support
+
+All current versions of Maps have full support for all databases that can be used with MediaWiki.
diff --git a/www/wiki/extensions/Maps/ISSUE_TEMPLATE.md b/www/wiki/extensions/Maps/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..d5e5b0e0
--- /dev/null
+++ b/www/wiki/extensions/Maps/ISSUE_TEMPLATE.md
@@ -0,0 +1,15 @@
+### Setup
+
+- Maps version:
+- MW version:
+- PHP version:
+- SMW version (if applicable):
+
+### Issue
+
+Detailed description of the issue and a [stack trace](https://www.semantic-mediawiki.org/wiki/Help:Identifying_bugs) if applicable:
+
+```
+```
+
+Steps to reproduce the observation (recommendation is to use the [sandbox](https://sandbox.semantic-mediawiki.org)):
diff --git a/www/wiki/extensions/Maps/Maps.php b/www/wiki/extensions/Maps/Maps.php
new file mode 100644
index 00000000..4212ed92
--- /dev/null
+++ b/www/wiki/extensions/Maps/Maps.php
@@ -0,0 +1,14 @@
+<?php
+
+/**
+ * Initialization file for the Maps extension.
+ *
+ * @links https://github.com/JeroenDeDauw/Maps/blob/master/README.md#maps Documentation
+ * @links https://github.com/JeroenDeDauw/Maps/issues Support
+ * @links https://github.com/JeroenDeDauw/Maps Source code
+ *
+ * @license https://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+
+wfLoadExtension( 'Maps' ); \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/MapsRegistration.php b/www/wiki/extensions/Maps/MapsRegistration.php
new file mode 100644
index 00000000..68d0ea05
--- /dev/null
+++ b/www/wiki/extensions/Maps/MapsRegistration.php
@@ -0,0 +1,57 @@
+<?php
+
+use Maps\MapsSetup;
+
+class MapsRegistration {
+
+ public static function onRegistration( array $credits ) {
+ if ( defined( 'Maps_VERSION' ) ) {
+ // Do not initialize more than once.
+ return true;
+ }
+
+ if ( !defined( 'Maps_SETTINGS_LOADED' ) ) {
+ require_once __DIR__ . '/Maps_Settings.php';
+ }
+
+ if ( is_readable( __DIR__ . '/vendor/autoload.php' ) ) {
+ include_once( __DIR__ . '/vendor/autoload.php' );
+ }
+
+ define( 'Maps_VERSION', $credits['version'] );
+ define( 'SM_VERSION', Maps_VERSION );
+
+ if ( !(bool)'Defining PHP constants in JSON is a bad idea and breaks tools' ) {
+ define( 'NS_GEO_JSON', 420 );
+ define( 'NS_GEO_JSON_TALK', 421 );
+ }
+
+ $GLOBALS['wgExtensionFunctions'][] = function() {
+ if ( $GLOBALS['egMapsDisableExtension'] ) {
+ return true;
+ }
+
+ // Only initialize the extension when all dependencies are present.
+ if ( !defined( 'Validator_VERSION' ) ) {
+ throw new Exception( 'You need to have Validator installed in order to use Maps' );
+ }
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.27c', '<' ) ) {
+ throw new Exception(
+ 'This version of Maps requires MediaWiki 1.27 or above; use Maps 4.2.x for older versions.'
+ . ' More information at https://github.com/JeroenDeDauw/Maps/blob/master/INSTALL.md'
+ );
+ }
+
+ ( new MapsSetup( $GLOBALS ) )->setup();
+
+ return true;
+ };
+
+ return true;
+ }
+
+}
+
+
+
diff --git a/www/wiki/extensions/Maps/Maps_Settings.php b/www/wiki/extensions/Maps/Maps_Settings.php
new file mode 100755
index 00000000..0d56a302
--- /dev/null
+++ b/www/wiki/extensions/Maps/Maps_Settings.php
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * For instructions on how to configure Maps, see
+ * https://www.semantic-mediawiki.org/wiki/Maps/Configuration
+ *
+ * For a list of all available settings and their default values,
+ * see DefaultSettings.php in this directory.
+ */
+
+foreach ( include __DIR__ . '/DefaultSettings.php' as $key => $value ) {
+ $GLOBALS[$key] = $value;
+}
+
+if ( !defined( 'Maps_SETTINGS_LOADED' ) ) {
+ define( 'Maps_SETTINGS_LOADED', true );
+}
diff --git a/www/wiki/extensions/Maps/README.md b/www/wiki/extensions/Maps/README.md
new file mode 100644
index 00000000..230f3183
--- /dev/null
+++ b/www/wiki/extensions/Maps/README.md
@@ -0,0 +1,105 @@
+# Maps
+
+Maps is a [MediaWiki](https://www.mediawiki.org) extension to work with and visualize geographical
+information.
+
+Features:
+
+* Powerful [`#display_map`](https://www.semantic-mediawiki.org/wiki/Extension:Maps/Displaying_maps) parser hook for embedding highly customizable dynamic maps into wiki pages.
+* Support for multiple mapping services: [Leaflet](http://leafletjs.com/), Google Maps and [OpenStreetMap](www.openstreetmap.org/).
+* Integration with [Semantic MediaWiki](https://www.semantic-mediawiki.org) via a [coordinate datatype](https://www.semantic-mediawiki.org/wiki/Help:Type_Geographic_coordinate)
+ * Query your stored coordinates and visualize them on dynamic maps, as tables or as lists
+ * Export your coordinates as KML or RDF
+ * Combine coordinates with other structured data stored in your wiki
+* Geocoding via several supported services with the [`#geocode`](https://www.semantic-mediawiki.org/wiki/Maps/Geocoding) parser function.
+* Coordinate formatting and format conversion via the [`#coordinates`](https://www.semantic-mediawiki.org/wiki/Maps/Coordinates) parser function.
+* Geospatial operations
+ * Calculating the distance between two points with [`#geodistance`](https://www.semantic-mediawiki.org/wiki/Maps/Geodistance)
+ * Finding a destination given a starting point, bearing and distance with [`#finddestination`](https://www.semantic-mediawiki.org/wiki/Maps/Finddestination)
+* Distance formatting and format conversion via the [`#distance`](https://www.semantic-mediawiki.org/wiki/Maps/Distance) parser function.
+* Visual map editor ([Special:MapEditor](https://www.semantic-mediawiki.org/wiki/Special:MapEditor)) to edit [`#display_map`](https://www.semantic-mediawiki.org/wiki/Extension:Maps/Displaying_maps) wikitext (requires Google Maps).
+
+Maps has been maintained since 2009 and is installed on 1000+ wikis. [Professional.Wiki](https://professional.wiki/) provides professional support.
+
+## User manual
+
+### For administrators
+
+* [Installation](https://www.semantic-mediawiki.org/wiki/Maps/Installation)
+* [Configuration](https://www.semantic-mediawiki.org/wiki/Maps/Configuration)
+* [Release notes](RELEASE-NOTES.md) - detailed list of changes per release
+* [Platform compatibility](INSTALL.md#platform-compatibility-and-release-status) - overview of PHP and MediaWiki support per release
+
+### For wiki users
+
+* [Usage instructions](https://www.semantic-mediawiki.org/wiki/Extension:Maps)
+* [Usage examples](https://www.semantic-mediawiki.org/wiki/Category:Maps_examples)
+* [Semantic usage examples](https://www.semantic-mediawiki.org/wiki/Semantic_Maps_examples)
+
+### Getting support
+
+* Professional support and custom development: **[Professional.Wiki](https://professional.wiki/)**
+* Ask a question on [the mailing list](https://www.semantic-mediawiki.org/wiki/Mailing_list)
+* File an issue on [our issue tracker](https://github.com/JeroenDeDauw/Maps/issues)
+
+## Project status
+
+* Latest version &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [![Latest Stable Version](https://poser.pugx.org/mediawiki/maps/version.png)](https://packagist.org/packages/mediawiki/maps)
+* Downloads on [Packagist](https://packagist.org/packages/mediawiki/maps) [![Download count](https://poser.pugx.org/mediawiki/maps/d/total.png)](https://packagist.org/packages/mediawiki/maps)
+* TravisCI &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [![Build Status](https://secure.travis-ci.org/JeroenDeDauw/Maps.png?branch=master)](http://travis-ci.org/JeroenDeDauw/Maps)
+* Code quality &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/JeroenDeDauw/Maps/badges/quality-score.png?s=3881a27e63cb64e7511d766bfec2e2db5d39bec3)](https://scrutinizer-ci.com/g/JeroenDeDauw/Maps/)
+* [Open bugs and feature requests](https://github.com/JeroenDeDauw/Maps/issues)
+* [Maps on OpenHub](https://www.ohloh.net/p/maps/)
+* [Blog posts about Maps](https://www.entropywins.wtf/blog/tag/maps/)
+
+## Contributing
+
+* [File an issue](https://github.com/JeroenDeDauw/Maps/issues)
+* [Submit a pull request](https://github.com/JeroenDeDauw/Maps/pulls) ([tasks for newcomers](https://github.com/JeroenDeDauw/Maps/issues?q=is%3Aissue+is%3Aopen+label%3Anewcomer))
+
+### Project structure
+
+The `src/` contains the PHP code and follows PSR-4 autoloading.
+
+* `src/DataAccess` - implementations of services that use the network, read from disk or persistence services
+* `src/Elements` - Value Objects representing geographical elements (should be devoid of MediaWiki binding)
+* `src/MediaWiki` - MediaWiki hook points, including API modules, special pages and MediaWiki hooks
+* `src/Presentation` - presentation layer code (all code dealing with HTML etc should be here)
+* `src/SemanticMW` - Semantic MediaWiki hook points, including result printers and value descriptions
+
+JavaScript, CSS and other web resources go into `resources/`.
+
+Tests for PHP go into `tests/` where they are grouped by test type (ie unit, integration). Within those test type
+directories the tests should mirror the directory structure in `src/`.
+
+### Running the tests
+
+As setup, run `composer install` inside of the Maps root directory.
+
+You can run the MediaWiki independent tests by executing phpunit in the root directory of maps:
+
+ phpunit
+
+This is possible without having a MediaWiki installation or webserver. A clone of the Maps code suffices.
+
+If you do not have PHPUnit installed, you can download the .phar into the root directory and execute it there:
+
+ wget -O phpunit.phar https://phar.phpunit.de/phpunit-7.phar
+ php phpunit.phar
+
+To run the tests with MediaWiki, change into `tests/phpunit` of your MediaWiki installation and run
+
+ php phpunit.php --wiki wiki -c ../../extensions/Maps/phpunit.xml.dist
+
+Where you either update `wiki` to match your wikis name, or drop the parameter. The above command
+works without modification if you are using the [MediaWiki Vagrant](https://www.mediawiki.org/wiki/MediaWiki-Vagrant).
+
+Beware that due to technical debt, some tests access the network.
+
+## Links
+
+* [Maps on OpenHub](https://www.openhub.net/p/maps/)
+* [Maps on Packagist](https://packagist.org/packages/mediawiki/maps)
+* [Maps on WikiApiary](https://wikiapiary.com/wiki/Extension:Maps)
+* [Maps on MediaWiki.org](https://www.mediawiki.org/wiki/Extension:Maps)
+* [TravisCI build status](https://travis-ci.org/JeroenDeDauw/Maps)
diff --git a/www/wiki/extensions/Maps/RELEASE-NOTES.md b/www/wiki/extensions/Maps/RELEASE-NOTES.md
new file mode 100644
index 00000000..118272e2
--- /dev/null
+++ b/www/wiki/extensions/Maps/RELEASE-NOTES.md
@@ -0,0 +1,1302 @@
+These are the release notes for the [Maps extension](README.md). For an overview of the
+different releases and which versions of PHP and MediaWiki they support, see the
+[platform compatibility tables](INSTALL.md#platform-compatibility-and-release-status).
+
+
+## Maps 7.4.0
+
+Released on August 9th, 2019.
+
+* Fixed default map height bug occurring with recent versions of an used library
+* Removed `egMapsSizeRestrictions` setting, unused since Maps 3.0.0
+
+## Maps 7.3.3
+
+Released on August 2nd, 2019.
+
+* Fixed error in height parameter description
+
+## Maps 7.3.2
+
+Released on July 25th, 2019.
+
+* Removed broken geocode API module
+
+## Maps 7.3.1
+
+Released on July 20th, 2019.
+
+* Fixed compatibility issue with SMW 3.1+ (thanks @mwjames!)
+
+## Maps 7.3.0
+
+Released on May 27th, 2019.
+
+* Fixed loading of certain Leaflet maps due to JavaScript error
+* Fixed `ajaxquery` result format parameter
+* Removed long broken `pagelinktext` option from KML result format
+
+## Maps 7.2.0
+
+Released on March 5th, 2019.
+
+* Fixed Google Maps KML path issue
+
+## Maps 7.1.0
+
+Released on January 16th, 2019.
+
+* Added `egMapsEnableCoordinateFunction` setting (for people using the GeoData extension)
+* Fixed fatal error on Special:MapEditor (6.1.0 regression) (by @paladox)
+
+## Maps 7.0.0
+
+Released on December 16th, 2018.
+
+* Breaking change: removed OpenLayers service
+* Breaking change: removed `egMapsDefaultServices['qp']` setting (just use `egMapsDefaultService`)
+* Breaking change: removed Google `fusiontables` parameter (Google is retiring this service)
+* Added transit layer support for Google Maps (`layers=transit`) (by @acnetj)
+* Added `egMapsGeoJsonCacheTtl` setting and optional caching for GeoJSON files
+* Fixed display of Maps category on Special:SpecialPages
+* Removed unused `tilt` parameter for Google Maps, including the `egMapsGMaps3DefaultTilt` setting
+
+## Maps 6.3.0
+
+Released on November 29th, 2018.
+
+* The `copycoords` parameter (shows coordinates on right click of marker) now works for Leaflet
+
+## Maps 6.2.2
+
+Released on November 27th, 2018.
+
+* Fixed image loading issues, most notably Leaflet markers (6.1.0 regression)
+
+## Maps 6.2.1
+
+Released on November 26th, 2018.
+
+* Fixed Google Maps 'kml' parameter. It now again works with just the file name rather than the full path
+* Fixed `lines` parameter for Ask queries (it is no longer ignored)
+* Fixed `polygons` parameter for Ask queries (it is no longer ignored)
+* Fixed `circles` parameter for Ask queries (it is no longer ignored)
+* Fixed `rectangles` parameter for Ask queries (it is no longer ignored)
+* Fixed optional list parameters (including `locations` for Ask queries) to ignore invalid values
+
+## Maps 6.2.0
+
+Released on November 23rd, 2018.
+
+* Fixed markers with no text having empty popups (was likely only happening with recent MediaWiki versions)
+* Fixed `link=all` in Ask queries: values are now linked where appropriate
+* Fixed `link=none` and `link=subject` in Ask queries: properties are no longer linked
+* Fixed `headers=hide` in Ask queries: the values now show
+* Fixed KML result format (6.1.0 regression)
+
+## Maps 6.1.0
+
+Released on November 21st, 2018.
+
+* The `rotate` control for Google Maps is now enabled by default (changeable via `$egMapsGMaps3Controls`) (by @acnetj)
+* The `layers` parameter for Google Maps now works again (5.2.0 regression)
+* Files can again be referenced without capitalizing the first letter (3.0.0 regression)
+* Removed broken SMW `_gpo` data type
+
+## Maps 6.0.4
+
+Released on October 20th, 2018.
+
+* Fixed localizaion loading issue (by paladox)
+
+## Maps 6.0.3
+
+Released on October 4th, 2018.
+
+* Fixed double display of markers when using Leaflet (6.0.0 regression)
+
+## Maps 6.0.2
+
+Released on October 1st, 2018.
+
+* Coordinates formatted as Decimal Degrees or Float are now rounded sensibly
+* The `smgQPCoodDirectional` setting is no longer ignored
+
+## Maps 6.0.1
+
+Released on September 5th, 2018.
+
+* Fixed loading of default settings (6.0.0 regression)
+
+## Maps 6.0.0
+
+Released on September 4th, 2018.
+
+* Dropped support for PHP older than 7.1
+* Dropped support for MediaWiki older than 1.31
+* Dropped support for Semantic MediaWiki older than 2.4
+* Added API key support for Leaflet layers via the `egMapsLeafletLayersApiKeys` setting (by Gilles Cébélieu)
+* Updated Leaflet library from version 1.3.1 to version 1.3.4
+
+### GeoJson support
+
+* Added GeoJson namespace. Pages in this namespace can only contain GeoJson
+* The `#display_map` parameter `geojson` now accepts page names of pages in the GeoJson namespace
+
+### Breaking changes
+
+* Maps is no longer automatically loaded when it is installed with Composer. You now need to call `wfLoadExtension( 'Maps' )`
+ (preferred) or include the entry point (`require_once __DIR__ . '/extensions/Maps/Maps.php';`) in `LocalSettings.php`.
+ You will also need to include the default settings before your modify the Maps settings
+ `require_once __DIR__ . '/extensions/Maps/Maps_Settings.php';`
+* Removed unused `egMapsNamespaceIndex` setting
+
+## Maps 5.6.0
+
+Released on July 16th, 2018.
+
+* Added `geojson` parameter to `#display_map`, available only for Leaflet
+* Fixed `#display_map` parameter `imageoverlays`: title, text and link are no longer ignored
+
+## Maps 5.5.5
+
+Released on July 9th, 2018.
+
+* Fixed `#display_map` parameter `polygons` when using Leaflet
+* Fixed regression introduced in 5.5.3 that broke marker icons in some cases when using `visitedicon`
+
+## Maps 5.5.4
+
+Released on July 8th, 2018.
+
+* Fixed popups for lines, polygons, circles and rectangles when using Leaflet
+* Fixed display of popups with no content for Google Maps
+* Fixed fatal error when geocoding fails for addresses in circles and rectangles
+
+## Maps 5.5.3
+
+Released on July 7th, 2018.
+
+* Fixed `#display_map` parameters `icon` and `visitedicon` when using a namespace prefix (ie. File:FileName.png)
+* Fixed `icon` and `visited icon` modifiers of `#display_map` parameter `locations` when using a namespace prefix
+* Fixed `#display_map` parameter `circles` when using Leaflet
+
+## Maps 5.5.2
+
+Released on July 5th, 2018.
+
+* Fixed the `visited icon` modifier of the `#display_map` parameter `locations` (it is no longer ignored)
+
+## Maps 5.5.1
+
+Released on July 4th, 2018.
+
+* Fixed regression introduced in 5.5.0 causing the `#display_map` parameter `service` to often be ignored
+* Fixed fatal error when using `#display_map` parameter `circles`
+* Fixed fatal error when using `#display_map` parameter `rectangles`
+* Fixed `#display_map` parameter `rectangles` fill color modifier (it is no longer ignored)
+* Fixed `#display_map` parameter `rectangles` fill opacity modifier (it is no longer ignored)
+
+## Maps 5.5.0
+
+Released on July 3rd, 2018.
+
+* Added Geoportail (France) layers for Leaflet (by Gilles Cébélieu)
+* Updated Leaflet library from version 1.1.0 to version 1.3.1
+* Updated Leaflet plugins to their latest versions
+* Removed redundant `$egMapsDefaultServices['display_map']` setting
+
+## Maps 5.4.0
+
+Released on June 7th, 2018.
+
+* Improved geocoding service fallback order (by Karsten Hoffmeyer)
+* Improved compatibility with the latest MediaWiki development version (by Timo Tijhof)
+
+## Maps 5.3.0
+
+Released on April 28th, 2018.
+
+* Added `scrollwheelzoom` parameter for GoogleMaps (by hidrarga)
+* Fixed installation issue caused by using a development version of the geocoding library
+
+## Maps 5.2.0
+
+Released on January 30th, 2018.
+
+* Added support for installation of Maps in non standard directories (using `wgExtensionDirectory`) (by Tobias Oetterer)
+* Added `egMapsGeoCacheTtl` setting (by Máté Szabó)
+* Added `scrollwheelzoom` parameter for Leaflet that allows disabling scroll wheel zoom (by hidrarga)
+
+## Maps 5.1.0
+
+Released on November 17th, 2017.
+
+* Dropped support for PHP older than 7.0
+* Improved layer support for Leaflet (by Peter Grassberger)
+ * Leaflet maps now show a layer control when there are multiple layers
+ * The `layer` parameter now accepts multiple values and has been aliased to `layers`
+ * Added `egMapsLeafletLayers` setting
+ * Deprecated `egMapsLeafletLayer` setting in favour of the new `egMapsLeafletLayers`
+* Fixed Leaflet attribution link (by Bernhard Krabina)
+
+## Maps 5.0.2
+
+Released on October 19th, 2017.
+
+* Custom marker icons on Leaflet maps are now scaled correctly (by hidrarga)
+
+## Maps 5.0.1
+
+Released on October 18th, 2017.
+
+Special one-off PHP 7.x optimized release. (requires PHP 7.x)
+
+## Maps 5.0.0
+
+Released on October 16th, 2017.
+
+* Added persistent geocoding cache (by hidrarga)
+* Fixed rendering of wikitext in popups of the map result format when using the `template` parameter (by hidrarga)
+* Fixed random loading failure of Leaflet maps (by Peter Grassberger)
+* Geocoders now respect MediaWiki's network settings such as `wgHTTPProxy`
+* Image overlays used in `#display_map` now support geocoding for their locations
+
+### Breaking changes
+
+* Removed `geoservice` parameters from the `#display_map` parser function
+* Removed `geoservice` and `allowcoordinates` parameters from the `#geocode` parser function
+* Removed `mappingservice` and `geoservice` parameters from the `#geodistance` parser function
+* Removed `mappingservice`, `geoservice` and `allowcoordinates` params from the `#finddestination` parser function
+* Removed `geoservice` parameter from the SMW result formats
+* Removed `service` parameter from the `geocode` API module
+* Removed `egMapsUserGeoOverrides` setting
+* Removed `egMapsAvailableGeoServices` setting
+* Removed `egMapsAllowCoordsGeocoding` setting
+* Removed support for the GeocoderUS geocoding service as it appears to have shut down
+
+## Maps 4.4.0
+
+Released on September 14th, 2017.
+
+* Added layer support for Leaflet (by Peter Grassberger)
+* Added static map support for Leaflet (`static=true`) (by hidrarga)
+* Fixed custom marker icon bug when using Leaflet (by hidrarga)
+
+## Maps 4.3.0
+
+Released on June 10th, 2017.
+
+* Dropped support for MediaWiki older than 1.27
+* Dropped support for PHP older than 5.6
+* Fixed compatibility conflict with the GitHub MediaWiki extension
+
+## Maps 4.2.1
+
+Released on May 20th, 2017.
+
+* Fixed issue occurring when using the `template` parameter in the Google Maps result format more than once on a page
+
+## Maps 4.2.0
+
+Released on May 15th, 2017.
+
+* Fixed bug in Nomatim geocoder that caused page loading to fail when Nomatim is down
+* Fixed bug in Nomatim geocoder that caused page loading to fail when Nomatim returned an invalid response
+* Updated Leaflet from 1.0.0-rc to 1.0.3
+
+## Maps 4.1.0
+
+Released on April 14th, 2017.
+
+* Fixed rendering of area query values (they now work properly in SMW "further result" links)
+* Fixed type warning in `Maps\SemanticMW\ResultPrinters\SMMapPrinter::getMapHTML`
+* Added missing geographical polygon type i18n messages
+
+## Maps 4.0.5
+
+Released on March 5th, 2017.
+
+* Fixed i18n issue in the `mapsdoc` parser hook
+
+## Maps 4.0.4
+
+Released on January 9th, 2017.
+
+* Fixed encoding of special characters in the Google geocoder (by somescout)
+* Improved PHP 7 compatibility (by Andre Klapper)
+
+## Maps 4.0.3
+
+Released on December 6th, 2016.
+
+* Fixed regression introduced in 4.0.2 that caused the JavaScript to not be loaded in some cases
+* The `display_map` parser hook now correctly uses its `geoservice` parameter
+* The `center` parameter for the map result format now takes into account the `geoservice` parameter
+
+## Maps 4.0.2
+
+Released on December 4th, 2016.
+
+* Fixed fatal error caused by double loading of initialization code on some platforms
+
+## Maps 4.0.1
+
+Released on November 19th, 2016.
+
+* The `geocode` parser hook now correctly uses its `geoservice` and `allowcoordinates` parameters
+
+## Maps 4.0
+
+Released on November 16th, 2016. Also see the [Maps 4.0 blog post](https://www.entropywins.wtf/blog/2016/11/09/maps-4-0-0-rc1-released/)
+
+### Highlight: Integrated Semantic MediaWiki support
+
+Merged in most of the features of the Semantic Maps extension. These are enabled automatically when SMW is installed.
+
+* Added a [coordinate datatype](https://www.semantic-mediawiki.org/wiki/Help:Type_Geographic_coordinate)
+* Added a [result format](https://www.semantic-mediawiki.org/wiki/Help:Result_formats) for each mapping service
+* Added a KML result format
+* Added [distance query](https://www.semantic-mediawiki.org/wiki/Semantic_Maps_examples/Distance_query) support
+
+Semantic Maps is discontinued as the features will now be maintained in Maps. The Semantic Maps form input
+has been moved into the [Page Forms](https://www.mediawiki.org/wiki/Extension:Page_Forms) extension.
+
+### Breaking changes
+
+* The default mapping service was changed from Google Maps to Leaflet (can be changed via the `egMapsDefaultService` setting)
+* The Maps tracking category is now disabled by default (can be enabled using the `egMapsEnableCategory` setting)
+
+### Other changes
+
+* Added `egMapsDisableExtension` setting that allows disabling the extension even when it is installed
+* The `egGoogleJsApiKey` setting from Maps 2.x will now be used as Google API key when `egMapsGMaps3ApiKey` is not set
+* Various missing messages where added
+
+## Maps 3.8.2
+
+Released on September 22nd, 2016.
+
+* Fixed incorrect centering of OpenLayers maps (by Peter Grassberger)
+
+## Maps 3.8.1
+
+Released on September 7th, 2016.
+
+* Fixed bug that caused clustering to always be enabled for Leaflet (by Peter Grassberger)
+
+## Maps 3.8
+
+Released on August 24rd, 2016.
+
+Due to changes to Google Maps, an API key now needs to be set. See the
+[installation configuration instructions](https://github.com/JeroenDeDauw/Maps/blob/master/INSTALL.md#configuration).
+
+* Added Google Maps API key `egMapsGMaps3ApiKey` setting (by Peter Grassberger)
+* Added Google Maps API version number `egMapsGMaps3ApiVersion` setting (by Peter Grassberger)
+* Added [Leaflet marker clustering](https://www.semantic-mediawiki.org/wiki/Maps_examples/Leaflet_marker_clustering) (by Peter Grassberger)
+ * `markercluster`: Enables clustering, multiple markers are merged into one marker.
+ * `clustermaxzoom`: The maximum zoom level where clusters may exist.
+ * `clusterzoomonclick`: Whether clicking on a cluster zooms into it.
+ * `clustermaxradius`: The maximum radius that a cluster will cover.
+ * `clusterspiderfy`: At the lowest zoom level markers are separated so you can see them all.
+* Added [Leaflet fullscreen control](https://www.semantic-mediawiki.org/wiki/Maps_examples/Leaflet_fullscreen_control) (by Peter Grassberger)
+* Added [OSM Nominatim Geocoder](https://www.semantic-mediawiki.org/wiki/Maps_examples/Geocode) (by Peter Grassberger)
+* Upgraded Leaflet library to its latest version (1.0.0-r3) (by Peter Grassberger)
+* Made removal of marker clusters more robust (by Peter Grassberger)
+* Unified system messages for several services (by Karsten Hoffmeyer)
+
+## Maps 3.7
+
+Released on June 21st, 2016.
+
+* Added [rotate control support](https://www.semantic-mediawiki.org/wiki/Maps_examples/Google_Maps_with_rotate_control) for Google Maps (by Peter Grassberger)
+* Changed coordinate display on OpenLayers maps from long-lat to lat-long (by Peter Grassberger)
+* Upgraded google marker cluster library to its latest version (2.1.2) (by Peter Grassberger)
+* Upgraded Leaflet library to its latest version (0.7.7) (by Peter Grassberger)
+* Added missing system messages (by Karsten Hoffmeyer)
+* Internal code enhancements (by Peter Grassberger)
+* Removed broken custom map layer functionality. You no longer need to run update.php for full installation.
+
+## Maps 3.6
+
+Released on May 26th, 2016.
+
+* Dropped support for MediaWiki older than 1.23
+* Dropped support for PHP older than 5.5
+* Added cluster properties for Google Maps (by Peter Grassberger)
+ * `clustergridsize`: The grid size of a cluster in pixels
+ * `clustermaxzoom`: The maximum zoom level that a marker can be part of a cluster
+ * `clusterzoomonclick`: Whether the default behaviour of clicking on a cluster is to zoom into it.
+ * `clusteraveragecenter`: Whether the center of each cluster should be the average of all markers in the cluster.
+ * `clusterminsize`: The minimum number of markers required to form a cluster.
+* Fixed missing marker cluster images for Google Maps (by Peter Grassberger)
+* Fixed duplicate markers in OpenLayers maps (by Peter Grassberger)
+* Fixed URL support in the icon parameter (by Peter Grassberger)
+* Various minor MediaWiki compatibility enhancements (by Karsten Hoffmeyer, Siebrand Mazeland and FlorianSW)
+
+## Maps 3.5
+
+Released on April 2nd, 2016.
+
+* Added `egMapsGMaps3Language` setting (by James Hong Kong and Karsten Hoffmeyer)
+* Added `osm-mapquest` layer for OpenLayers (by Bernhard Krabina)
+* Added license lable to display on "Special:Version" (by Karsten Hoffmeyer)
+* Improved Mobile Frontend support (by James Hong Kong)
+* Added missing Leaflet system messages (by Karsten Hoffmeyer)
+
+## Maps 3.4.1
+
+Released on January 30th, 2016.
+
+* Fixed Open Street Map HTTPS support issues (by Karsten Hoffmeyer)
+* Migrated remaining wfMsg* to wfMessage (by Florian Schmidt)
+* Migrated wfRunHooks to Hooks::run (by Adam Shorland)
+
+## Maps 3.4
+
+Released on July 25th, 2015.
+
+* Added KML support for OpenLayers via a new `kml` parameter (by akionux)
+* Fixed Google Maps HTTPS support issues (by Karsten Hoffmeyer)
+
+## Maps 3.3
+
+Released on June 29th, 2015.
+
+* Added `$egMapsEnableCategory` setting (by Bernhard Krabina)
+* Fixed OpenLayers specific path issue (by Simon Heimler)
+
+## Maps 3.2.4
+
+Released on June 21st, 2015.
+
+* Map reside is now triggered when going fullscreen (by Kjetil Volden)
+* Improved styling of the fullscreen button (by Kjetil Volden)
+* Removed no longer working osmarender layer (by Karsten Hoffmeyer)
+* Fixed resource paths for some installation configurations
+
+## Maps 3.2.3
+
+Released on March 23rd, 2015.
+
+* Protocol relative URLs are now used, avoiding HTTPS related problems
+* Selecting OpenLayers markers now works on touch devices
+
+## Maps 3.2.2
+
+Released on January 19th, 2015.
+
+* Fixed fatal error in the KML formatter
+
+## Maps 3.2.1
+
+Released on January 13th, 2015.
+
+* Fixed `geocode` right
+* Fixed coordinate precision issue after breaking changes in DataValues Geo
+
+## Maps 3.2
+
+Released on September 12th, 2014.
+
+* Enhanced compatibility with MediaWiki 1.24
+* Improved the translations
+* Switched to using DataValue Geo 1.x
+
+## Maps 3.1
+
+Released on June 30th, 2014.
+
+* Re added Google Earth support
+* Removed support for the deprecated Google JavaScript API
+* Updated the translations to use the new MediaWiki JSON format
+* Re added support for fill color and fill opacity parameters for circles
+* Re added image overlay support for Google Maps
+
+## Maps 3.0.1
+
+Released on March 27th, 2014.
+
+* Fixed bug that prevented non-px units (%, ex, em) from being used in the width and height parameters.
+* Translation updates
+
+## Maps 3.0
+
+Released on January 18th, 2014.
+
+In this version a big part of the PHP codebase has been rewritten to decrease technical debt and
+thus facilitate maintenance, new feature deployment and debugging. Many tests have been added and a
+lot of bugs have been found and fixed. As an experimental feature, allowing the use of custom image
+layers with OpenLayers has been reintroduced.
+
+#### Compatibility changes
+
+* The extension now needs to be installed with Composer.
+* Changed minimum Validator version from 0.5 to 1.0.
+
+#### New features
+
+* Added leaflet service (by Pavel Astakhov)
+* Added Geocoder.us geoservice support (Ike Hecht)
+* Experimental: Usage of custom image layers defined in "Layer:" namespaced wiki pages.
+ NOTE: This feature has been part of Maps in an old 0.7.x version but got broken shortly after.
+ 3.0 reintroduces the feature in a similar way but old layer definitions are probably not
+ fully compatible for the sake of some advanced features of this rewrite.
+ NOTE: Requires running MediaWiki's maintenance/update.php for database schema updates.
+
+#### Bug fixes
+
+* Fixed autoinfowindows functionality.
+* Fixed various bugs in geocoordinate parsing and formatting.
+
+#### Breaking internal changes
+
+* Moved classes into Maps namespace
+* Removed all Criteria classes
+* Removed all Manipulation classes
+* Removed MapsCoordinateParser
+* Geocoding interfaces changed
+* MapsLocation interface changed
+* Custom image layers related classes (previously broken feature) changed
+
+#### Infrastructure
+
+* Maps is now hosted on GitHub at https://github.com/JeroenDeDauw/Maps
+* Maps now has its tests run on TravisCI at https://travis-ci.org/JeroenDeDauw/Maps
+* Maps code quality is now tracked by ScrutinizerCI at https://scrutinizer-ci.com/g/JeroenDeDauw/Maps/
+* Maps is now available on Packagist at https://packagist.org/packages/mediawiki/maps
+
+## Maps 2.0
+
+Released on October 5, 2012.
+
+#### Compatibility changes
+
+* Changed minimum PHP version from 5.2 to 5.3.
+* Changed minimum MediaWiki version from 1.17 to 1.18.
+* Changed minimum Validator version from 0.4 to 0.5.
+* Removed support for the deprecated Google Maps v2 API.
+* Removed support for the now unsupported Yahoo! Maps API and associated geocoding service.
+* Temporary disabled OSM service (you can still use OSM with the OpenLayers service).
+
+#### New features
+
+* Added support for defining an inline label to markers to GoogleMaps.
+* Added support for marker clustering to Google Maps.
+* Added support for grouping locations.
+* Added support for defining image overlays (ground overlays) in Google Maps.
+* Added support for defining lines, polygons, rectangles and circles using wikitext for Google Maps and OpenLayers.
+* Added a graphical map editing tool that allows exporting to and importing from simple wikitext (Google Maps only).
+* Added "copycoords" parameter to Google Maps and OpenLayers that allows copying coordinates after right clicking a location on a map.
+* Added "minzoom" and "maxzoom" parameters to #display_map.
+* Added support for using the Google JS API key with Google Maps (for increased map display and geocoding call limits).
+* Added support for searching markers (searchmarkers=all/title) in Google Maps and OpenLayers
+* Added support for creating static maps in OpenLayers and GoogleMaps (static=on)
+* Added positional parameter to show polygons only on hover.
+* Added an optional link parameter as an alternative to popup bubble with text and title
+* Added an optional visitedicon parameter (both global and marker parameter), that will change the icon of a marker on click.
+
+#### Other improvements
+
+* Merged display_map and display_point(s) into a single parser function: display_map (display_points is now an alias).
+* Updates parameter definitions from Validator 0.4.x to Validator 0.5.x.
+* Improved script loading.
+* Added various unit tests that caught some bugs and will now prevent regressions.
+
+#### Bug fixes
+
+* Fixed JavaScript error on some special pages due to incorrect order of map initialization.
+* Fixed partially broken kml functionality.
+
+## Maps 1.0.5
+
+Released on Novebmer 30, 2011.
+
+* Fixed display of attribution control for OpenLayers.
+* Fixed to big precision of geographic coordinates in decimal minutes format (bug 32407).
+
+## Maps 1.0.4
+
+Released on October 15, 2011.
+
+* Updated OpenLayers from 2.10 to 2.11.
+* Fixed bug in adding additional markers for Google Maps v3 (mainly affecting the Semantic Maps form input).
+
+## Maps 1.0.3
+
+Released on September 14, 2011.
+
+* Added API module for geocoding.
+* Added 'geocoding' right.
+* Added kmlrezoom parameter for Google Maps v3 and general $egMapsRezoomForKML setting.
+* Fixed Google Maps v3 JavaScript issue occurring on MediaWiki 1.17.
+
+## Maps 1.0.2
+
+Released on August 24, 2011.
+
+* Fixed Google Maps v3 JavaScript issue occurring when using Google Earth on unsupported systems.
+* Fixed internationalization of distances (bug 30467).
+
+## Maps 1.0.1
+
+Released on August 17, 2011.
+
+* Added language parameter to the mapsdoc hook.
+* Use of Validator 0.4.10s setMessage method instead of setDescription for better i18n.
+* Fixed zoom and types parameters for Google Maps v3.
+* Minor improvement to script loading.
+* Added support for Google Earth in Google Maps v3.
+* Added tilt parameter for Google Earth in Google Maps v3.
+
+## Maps 1.0
+
+Released on July 19, 2011.
+
+This version branched from Maps 0.7.x at version 0.7.3.
+
+#### New features
+
+* Added full Google Maps v3 support and set it as the default mapping service.
+* Added new geocoder making use of the new GeoNames API.
+* Added support for the auto-documentation features for parser hooks introduced in Validator 0.4.3.
+* Added resizeable parameter to all mapping services except OSM.
+
+#### Removed features
+
+* Removed compatibility with pre MediaWiki 1.17.
+* Removed overlays parameter for Google Maps v2.
+* Removed the previously deprecated "display map", "display point" and "display points" parser hooks.
+Use their underscored equivalents, ie "display_map".
+
+#### Internal improvements
+
+* Usage of the Resource Loader for all scripts and stylesheets.
+* Rewrote all the map JavaScript to jQuery plugins.
+* Rewrote the way parameters are translated to JavaScript. Now one big PHP object is json_encoded.
+* Improved KML formatter.
+* Use of Google Maps geocoding service v3 instead of v2.
+* Completed coordinate and distance parser/formatter unit tests and made them compliant with the
+MediaWiki unit testing support.
+
+#### Bug fixes
+
+* Fixed geocoding service overriding based on mapping service (merged in from Maps 0.7.5).
+* Fixed fatal error occurring when running maintenance/refreshLinks.php.
+* Fixed DMS coordinate parsing issue (bug 29419).
+* Fixed coordinate normalization issue (bug 29421).
+
+#### Other tweaks
+
+* Improved default width of maps (merged in from Maps 0.7.5).
+
+## Maps 0.7.3
+
+Released on November 30, 2010.
+
+* Some internal improvements and translation updates.
+* Fixed issue occurring when Maps is the only extension adding custom namespaces.
+
+## Maps 0.7.2
+
+Released on October 28, 2010.
+
+#### New features
+
+* Added experimental support for KML layer definitions.
+
+#### Internal improvements
+
+* Extended the layer handling to support different types of layers, each of which can be supported by one or more mapping services.
+
+#### Bug fixes
+
+* Fixed incompatibility with MW 1.15.x.
+* Fixed incorrect parsing of certain DM and DMS coordinates.
+* Fixed small layout issue with pop-ups in Google Maps.
+* Fixed incorrect error on non-existing pages in the Layer namespace.
+
+## Maps 0.7.1
+
+Released on October 20, 2010.
+
+#### New features
+
+* Image layers for OpenLayers maps, defined via pages in the Layer namespace.
+
+#### Bug fixes
+
+* Support for images without namespace prefix in the display points parser hook.
+* Fixed layer oder for OpenLayers maps.
+
+#### Internal improvements
+
+* Rewrote OpenLayers layer handling.
+
+## Maps 0.7
+
+Released on October 15, 2010.
+
+#### New features
+
+* Tag support for these parser hooks (which previously only had parser function support):
+ * Coordinates
+ * Distance
+ * Finddestination
+ * Geocode
+ * Geodistance
+* Thumbs and photos parameters for the OSM service.
+
+#### Bug fixes
+
+* Fixed compatibility with the MW 1.17 resource loader.
+* Fixed i18n issue with the overlays control for Google Maps v2 maps.
+* Fixed default zoom level for Yahoo! Maps maps.
+* Increased the maximum decimals for DMS coordinates from 2 to 20.
+
+#### Removed features
+
+* #geocodelong and #geocodelat parser functions - you can obtain their functionality using #geocode.
+
+#### Internal improvements
+
+* Rewrote the geocoding functionality. It's now an integral part of the extension that can not be just pulled out,
+while the reverse is true for individual geocoders. Geocoder interaction now uses the same model as mapping
+service interaction.
+* Use of Validator 0.4, allowing for more robust and consistent error reporting.
+* Rewrote the parser hooks to use the ParserHook class provided by Validator.
+* Restructured the directory structure of the extension to better match it's architecture.
+* Use of OpenLayers 2.10 instead of 2.9.
+
+## Maps 0.6.6
+
+Released on August 26, 2010.
+
+#### New features
+
+* Support for geocoding over proxies.
+* Added $egMapsInternatDirectionLabels settings, allowing users to disable internationalization of direction labels.
+
+#### Refactoring
+
+* Added MapsMappingServices, which serves as factory for MapsPappingService objects and does away
+with all the globals previously needed for this.
+* Removed the http/curl request code from the geocoder classes - now using Http:get() instead.
+
+#### Bug fixes
+
+* Fixed issue that caused pop-up contents to render incorrectly when it contained wiki markup.
+* Fixed coordinate parsing bug (direction labels did not get recognized) that was introduced in 0.6.4.
+* Fixed spacing issues with several parser functions.
+
+## Maps 0.6.5
+
+Released on July 27, 2010.
+
+#### Refactoring
+
+* Added unit tests for the coordinates parser.
+* Created iMappingFeature interface, from which iMapParserFunctions inherits.
+* Moved map id creation to the mapping service class for all features.
+* Moved marker JavaScript creation for display_points to the mapping service class for all features.
+* Moved default zoom level access method to the mapping service class for all features.
+* Improved the way marker data is turned into JavaScript variables.
+* Improved coordinate recognition regexes.
+
+#### Bug fixes
+
+* Fixed several small coordinate parsing and formatting issues.
+* Fixed a few small distance parsing issues.
+
+## Maps 0.6.4
+
+Released on July 8, 2010.
+
+#### New features
+
+* Added new OSM service based on iframe inclusion of toolserver page that renders OpenStreetMap tiles with Wikipedia overlays.
+* Added internationalization to the OpenLayers service.
+* Added support for including KML files for Google Maps v2.
+* Added 'searchbar' control for Google Maps v2.
+
+#### Refactoring
+
+* Moved more functionality over from feature classes to service classes to prevent crazy code-flow and code duplication.
+
+#### Bug fixes
+
+* Fixed bug in the OpenLayers service causing it to display badly in Chrome.
+* Fixed issue with with and height validation for % values, also causing backward compatibility problems with pre 0.6 setting definitions.
+* Fixed several small bugs in the coordinate parser.
+
+## Maps 0.6.3
+
+Released on June 20, 2010.
+
+#### Refactoring
+
+* Mayor refactoring of the mapping service handling, to make the code flow less messy and be able to do mapping service related things in a more consistent fashion.
+* Upgrade to OpenLayers 2.9.1.
+
+#### Bug fixes
+
+* Fixed severe bug in the coordinate parsing that removed the degree symbol from passes values, resulting in rendering most of them invalid. Presumably present since 0.6.2.
+
+## Maps 0.6.2
+
+Released on June 7, 2010.
+
+#### New features
+
+* Added #distance parser function parse distances using any of the supported units and outputting them in any of these.
+* Made supported distance units configurable and added setting for the default distance unit.
+* Added 'decimals' and 'unit' parameters to #geosiatnce.
+* Default parameter handling improvements (via Validator 0.3.2).
+
+#### Bug fixes
+
+* Re-added parameter name and value insensitivity (via Validator 0.3.2).
+
+## Maps 0.6.1
+
+Released on June 4, 2010.
+
+#### Bug fixes
+
+* Fixed bug that caused geocoding requests to fail when using display_points
+* Fixed bug that had broken the geoservice parameter for display_points and display_map.
+* Fixed bug that made OSM layers in the OpenLayers service fail.
+* Fixed issue that made custom markers on Google Maps not show up on initial page load and centred them wrongly.
+
+## Maps 0.6
+
+Released on May 31, 2010.
+
+#### New features
+
+* Added support for width and height in px, ex, em and %, instead of only px, allowing for maps that
+adjust their size to the screen width and other content.
+* Added full support for both directional and non-directional coordinate notations in DMS, DD, DM
+and float notation.
+* Added #coordinates parser function which allows rewformatting of coordinates to all supported notations.
+* Rewrote the #geocode parser function to work with named parameters and added support for smart
+geocoding. Now takes in all supported coordinate notations, and is able to output in all of them as well.
+* Added #geodistance function (based on the one in MathFunctions) with smart geocoding support.
+* Added #finddestination function with smart geocoding support.
+
+#### Refactoring
+
+* Rewrote the handling of the display_map and display_point(s) parser functions, esp the way the
+service parameter is getting determined and acted upon.
+* Removed the MapsMapFeature class to make the base classes for the features more independent and flexible.
+* Restructured the directory structure to make what the services and features are more clear.
+* Rewrote map divs and added loading message for each map.
+* Rewrote individual map JS to be added to the page header.
+* Mayor clean up of the coordinate handling, to allow for coordinate formatting and to facilitate
+better integration by the GeoCoords data type in Semantic Maps. All this code is now located in MapsCoordinateParser.
+* Use native MW hook system for mapping services and features if possible.
+* Updated the magic words to mw >=1.16 style, and retained backward compatibility.
+* Updated the OpenLayers version from 2.8 to 2.9.
+* Rewrote the parameter definitions to work with Validator 0.3.
+* Rewrote the resource inclusion html to make the code cleaner and more secure.
+
+#### Bug fixes
+
+* Changed parsing of parameters so that '=' signs in values don't cause themselves and
+proceeding characters to be omitted.
+* Add mapping to the language codes that are send to the Google Maps API to null the naming
+differences between MW and the API.
+* Added automatic icon image sizing for Google Maps and Yahoo! Maps markers.
+* Fixed conflict with prototype library that caused compatibility problems with the Halo extension.
+
+## Maps 0.5.5
+
+Released on March 20, 2010.
+
+#### Refactoring
+
+* Stylized the code to conform to MediaWiki's spacing conventions.
+
+#### Bug fixes
+
+* Fixed issue with scrollbar in pop-ups on Google Maps maps.
+* Fixed Google Maps key issue with OpenLayers that arose from the new OpenLayers layer definition system.
+* Fixed JS issue with Google Maps default overlays.
+
+## Maps 0.5.4
+
+Released on March 1, 2010.
+
+#### New features
+
+* Added the ability to define the layers (and their dependencies) that can be added by users to an OpenLayers map.
+* Added the ability to define "layer groups" for OpenLayers layers.
+
+#### Refactoring
+
+* Moved the OpenLayers layer definition validation and selection from JS to PHP.
+
+#### Bug fixes
+
+* Fixed bug causing the default zoom for each mapping service to be off.
+* Fixed potential xss vectors.
+* Fixed minor JS error that was present for all maps except OSM.
+
+## Maps 0.5.3
+
+Released on February 1, 2010.
+
+#### New features
+
+* Added Google Maps v3 support for display_map.
+
+#### Refactoring
+
+* Added service defaulting for features using a hook themselves.
+
+#### Bug fixes
+
+* Fixed JavaScript bug causing all OSM maps to fail.
+
+## Maps 0.5.2
+
+Released on January 20, 2010.
+
+#### New features
+
+* Added icon parameter to display_point(s), allowing you to set the icon for all markers that do not
+have a specific icon assigned.
+
+#### Refactoring
+
+* Usage of Validator 0.2 features for the static map specific parameters.
+
+#### Bug fixes
+
+* Fixed escaping issue causing wikitext in the title and label parameters not to be displayed correctly.
+* Fixed file path for location specific icons.
+
+## Maps 0.5.1
+
+Released on December 25, 2009.
+
+#### New features
+
+* Integrated further with Validator by holding into account the error level for coordinate validation
+in the display_ parser functions.
+
+* Added activatable= parameter to the static map support.
+
+#### Refactoring
+
+* Cleaned up the static map code for OSM display_map.
+* Modified the parameter definitions to work with Validator 0.2
+* Removed redundant (because of Validator 0.2) utility function calls from the mapping classes.
+* Removed redundant (because of Validator 0.2) utility functions from the mapping service files.
+
+#### Bug fixes
+
+* Fixed issue with the hook system that caused code to get executed when it shouldn't.
+
+## Maps 0.5
+
+Released on December 17, 2009.
+
+#### New features
+
+* Added strict parameter validation.
+* Added smart 'autopanzoom' like control for Google Maps and Yahoo! Maps.
+* Added internationalization to the OSM service, and an extra parameter to define per-map languages.
+* Static map support, similar and based upon SlippyMap.
+
+#### Refactoring
+
+* Rewrite the parameter handling to be more centralized and modular.
+** Make it possible to override the info of parameters for mapping services, including
+their aliases, default values and criteria.
+** Make it possible to add and override parameters in each segment of Maps, instead of only
+the mapping services.
+
+* Cleaned up and centralized parser function code.
+* Refactored the marker specific data handling code in every display point class up to
+a central location.
+* Removed backward compatibility (to 0.2.x and earlier) of the earth parameter.
+* Removed support for Google Map API map type names for Google Maps.
+* Added code to unload any services from the service hook that are not present in the list of
+allowed services. This ensures they don't get initialized, and makes any check to see if the
+service is one of the allowed ones further on unneeded.
+* Added checks for extension dependencies that need to be present for Maps to be initialized.
+
+#### Bug fixes
+
+* Fixed bug causing markers not to show up when a specific description was provided.
+
+#### Documenting
+
+* Created screencast demonstrating display_map usage.
+* Creates screencast demonstrating display_point usage.
+* Updated the developer documentation about hooking into and extending Maps to be useful
+for the current version.
+
+## Maps 0.4.2
+
+Released on November 15, 2009.
+
+Changes in 0.4.2 discussed on the authors blog:
+
+* [Maps and Semantic Maps 0.4.2 released](https://www.entropywins.wtf/blog/2009/11/16/maps-and-semantic-maps-0-4-2/)
+* [New in Maps 0.4.2](https://www.entropywins.wtf/blog/2009/11/12/new-in-maps-0-4-2/)
+
+#### New features
+
+* Added overlays to Google Maps. This includes both an 'overlay' control, and a new parameter
+to choose the available and default loaded overlays.
+* Added specific handling for the coordinates= and addresses= parameters for both display_map
+and display_point(s). You can now specify you do not want anything that's not a coordinate on
+your map (so no geocoding) with the coordinates= parameter, or let Maps know everything is
+an address with the addresses= parameter, causing everything to be geocoded. Also modified
+the error messages for wrong addresses and coordinates to fit this new behavior.
+
+#### Refactoring
+
+* Added the version of Maps to the JS files call, to prevent issues when functions or calls
+are changed in new versions.
+* Changed the JavaScript map parameters for Google Maps from individual parameters to a group.
+
+#### Bug fixes
+
+* Fixed inclusion path to the OSM JS file. This bug prevented any OSM maps from showing up.
+* Fixed display_map and the centre parameter of display_point(s). Both are unusable by a bug
+introduced in 0.4.1.
+* Fixed bug causing to many decimal digits in some coordinate notations, making them unrecognisable
+for Maps.
+* Fixed bug causing a form of DD notation not to get recognized.
+
+## Maps 0.4.1
+
+Released on November 10, 2009.
+
+#### Bug fixes
+
+* Fixed problems with the ° sign, caused by wrong file encodings, resulting into problems with
+the DMS notation.
+* Fixed flaw in DMS to float translation, resulting into a map being displayed when the values
+where not separated by a comma.
+
+## Maps 0.4
+
+Released on November 3, 2009.
+
+Changes in 0.4 discussed on the authors blog:
+
+* [Finally! Maps and Semantic Maps 0.4!](https://www.entropywins.wtf/blog/2009/11/03/finally-maps-and-semantic-maps-0-4/)
+
+#### New features
+
+* Added display_map parser function, to display maps without any markers.
+* Added parsing of marker-specific title and label values.
+* Added geocoding support for the centre parameter. This is based on automatic detection of
+non-coordinates to see if geocoding is required, similar to the modified behavior of display_point(s).
+* Added minimum and maximum map size restrictions, like done in SlippyMap.
+* Added OSM mapping service, which uses OL, but only allows OSM layers and is optimized for OSM.
+* Added smart 'autopanzoom' control to OL and OSM services. It will determine for itself if a
+panzoom, panzoombar, or no control should be displayed, depending on the maps height.
+* Added support for DM and DD coordinate notations.
+
+#### Refactoring
+
+* Created a hook system for the parser functions, allowing the adding or removing of additional
+parser function support.
+* Removed redundant absolute script path variable. This absolute value caused problems for some installations.
+* Changed the geocoding functionality into a true feature hook element, enabling easy removal.
+* Created service hook for the geocoding feature, loose from the mapping services hook.
+* Changed display_point(s) and display_address(es) to display_point(s), with auto detect
+functionality to see if the provided value are coordinates or addresses. display_address and
+display_addresses have been retained for backward compatibility, but will be removed from the docs.
+Backward compatibility will be removed at some point, so the use of these functions is discouraged.
+
+#### Bug fixes
+
+* Fixed issue with the default parameter for the display_address(es) parser functions.
+* Fixed major bug in the initialization method causing hook code to get executed at a probably
+wrong moment. This bug can be the cause of some weird problems that surfaced since 0.3.3.
+* Fixed issue with size of pop-ups in Google Maps. They did not stretch far enough vertically
+for large contents.
+
+## Maps 0.3.4
+
+Released on September 12, 2009.
+
+Changes in 0.3.4 discussed on the authors blog:
+
+* [Maps and Semantic Maps 0.3.4 released](https://www.entropywins.wtf/blog/2009/09/12/maps-and-semantic-maps-0-3-4-released/)
+
+#### New features
+
+* Created hook system for features, which now also allows you to specify which features
+should be enabled and which not.
+
+#### Refactoring
+
+* Added old style geocoding request again for people who do not have cURL enabled, plus a
+more consistent fall-back mechanism.
+* Added internationalization for the mapping service names.
+* Added internationalized list notations.
+* Restructured the parser function handling code to work with the new feature hook system.
+* Improved structure of geocoding classes.
+* Moved Semantic Maps JavaScript code from the Maps JS files to new SM JS files.
+* Fixed tiny performance issues all over the code.
+
+#### Bug fixes
+
+* Fixed issue with empty parameters (par=value||par2=value2) that caused the default parameter
+(coordinate(s)/address(es)) to be overridden if it occurred after the default one was set.
+* Fixed wrong error message when you provide a coordinate(s)/address(es) parameter without
+any value (ie |coordinates=|)
+
+## Maps 0.3.3
+
+Released on August 25, 2009.
+
+Changes in 0.3.3 discussed on the authors blog:
+
+* [Maps and Semantic Maps 0.3.3](https://www.entropywins.wtf/blog/2009/08/25/maps-and-semantic-maps-0-3-3/)
+
+#### New features
+
+*Added [Geonames](https://www.geonames.org) geocoding support. This is an open source geocoding
+service, that does not require a licence. It has been made the default geocoding service.
+* Added wiki-text rendering to the values of the title and label parameters, allowing users
+to pass along links, images, and more.
+
+#### Refactoring
+
+* Refactored some common functionality of the geocoder classes up to MapsBaseGeocoder.
+* Minor issue - the OpenLayers default zoom should be closer, when displaying one
+point
+
+#### Bug fixes
+
+* Fixed small bug in MapsMapper::inParamAliases that caused the determination of the
+geoservice to fail in some cases, and set it to the default.
+
+## Maps 0.3.2
+
+Released on August 18, 2009.
+
+Release for consistency. Only changes to Semantic Maps where made in 0.3.2.
+
+## Maps 0.3.1
+
+Released on August 18, 2009.
+
+#### New features
+
+* Users can now define a default service for each feature - parser functions, query printers and form inputs.
+
+#### Refactoring
+
+* Added check to see if the classes array is present in a mapping service info array.
+* Added check to see if a mapping service has handling for parser functions. In 0.3,
+Maps assumed it had, preventing the adding of mapping services that only have a form input or/and query printer.
+* The getValidService function now holds into account that not every service has support for
+both parser functions, query printers and form inputs.
+
+#### Bug fixes
+
+* Added path to extension directory to non local class item in a service's info array,
+since adding the path is impossible in the declaration.
+
+## Maps 0.3
+
+Released on August 14, 2009.
+
+Changes in 0.3 discussed on the authors blog:
+
+* [Final changes for Maps and SM 0.3](https://www.entropywins.wtf/blog/2009/08/13/final-changes-for-maps-and-sm-0-3/)
+* [New features in Maps and SM 0.3](https://www.entropywins.wtf/blog/2009/08/07/new-features-in-maps-and-sm-0-3/)
+* [Structural changes for Maps and SM 0.3](https://www.entropywins.wtf/blog/2009/08/05/structural-changes-for-maps-and-sm-0-3/)
+
+#### New features
+
+* Multi location parser functions. Two completely new parser functions have been added that
+allow the displaying of multiple points on a map.
+* Configurable map type controls. Users can now configure the map type controls of Google
+maps and Yahoo! maps maps. They can set the available map types, and the order they want
+them to be displayed in the map type control.
+* Property names now have aliases. This means you can add several alternative ways to name
+the same parameter, for instance, you can make so that ‘auto zoom’ and ‘auto-zoom’ will do
+excellently the same as the main parameter ‘autozoom’. This is particularly handy for
+parameters such as ‘centre’ (British spelling) and ‘center’ (American spelling).
+* Added Google Maps moon, Mars and sky support.
+* Controls on both Yahoo! Maps and Google Maps map can now be configured by the user with
+the controls parameter. Yahoo! Maps maps already have this option for a limited set of
+controls since version 0.2, but the amount of available controls has now been expanded
+to what the Yahoo! Maps API offers. For Google Maps the change is significantly larger,
+since a lot of new controls can now be added. These included an overview map, a scale
+line, a drop down menu for map types, an automated reverse geocoding location determiner
+and more.
+* Added the ability to specify separate title, label and icon values for each marker
+in the display_points and display_addresses parser functions.
+* Added user friendly notices for when geocoding of an address fails.
+* A whole list of OpenLayers base layers have been added. These include the satellite,
+street and hybrid views for Yahoo! Maps and Bing Maps, but also finally the OpenStreetMap layers.
+
+#### Refactoring
+
+* Created hook system for the mapping services. All hard-coded references to mapping
+services in the core code have been removed. A service is now added by one multi dimensional
+array in Maps.php (note that this can also be done in the initialization file of another
+extension!), which holds the name of the parser functions class and it’s location, the
+aliases for the service name (feature added in 0.2), and their allowed specific parameters
+and their aliases. This architecture allows other people to create their own mapping
+extension using the Maps (and Semantic Maps) ‘API’.
+* Created a class that bundles common functionality from MapsBaseMap and SMFormInput.
+* Rewrote parts of the geocoder base class.
+* Added separated handling for default parameter for each mapping service.
+* Changed the requests in the geocoder classes to CURL requests to avoid security issues.
+* Moved common, parser function specific, functions and variables from MapsMapper to a new MapsParserFunctions class.
+* Moved common code within the mapping services out of the parser function class to a new utility classes.
+
+#### Bug fixes
+
+* Fixed issue preventing the extension description from showing up in 0.2.1 & 0.2.2.
+* Fixed bug that caused Bing maps (for open layers) to not work.
+
+## Maps 0.2
+
+Released on July 29, 2009.
+
+#### New features
+
+* Added Backward compatibility by using the $wgGoogleMapsKey when this one is set and $egGoogleMapsKey isn't.
+* Added hook for [[Extension:Admin_Links|Admin Links]].
+* Added a true aliasing system for service names.
+* Created a centre parameter, that will allow you to set a custom map centre (different from the
+place where the marker will be put).
+* Added pop-ups for the markers with title and label parameters to determine the pop-up contents.
+* Changed the OpenLayers control handling. Make it accept all (36) OL controls by using eval()
+instead of a switch statement in the JavaScript.
+* Added the 'physical' button in the map type control of Google Maps maps when this map type is set.
+* Added Yahoo! geocoder support (for parser functions).
+
+#### Refactoring
+
+* Refactored MapsBaseMap and all it's child classes. This will vastly increase code
+centralization and decrease redundant logic and definitions.
+* Did a major rewrite of the Google Maps and Yahoo! Maps code. The parser function
+classes now only print a call to a JS function with all needed parameters, which then
+does all the logic and creates the map.
+
+#### Bug fixes
+
+* Fixed issue causing aliases for service names getting turned into the default
+service since they are not in the allowed services list.
+* Removed redundant parts of the OpenLayers library.
+
+## Maps 0.1
+
+Released on July 20, 2009.
+
+* Initial release, featuring Google Maps (+ Google Earth), Yahoo! Maps and OpenLayers mapping services.
diff --git a/www/wiki/extensions/Maps/composer.json b/www/wiki/extensions/Maps/composer.json
new file mode 100644
index 00000000..ae9b63ed
--- /dev/null
+++ b/www/wiki/extensions/Maps/composer.json
@@ -0,0 +1,74 @@
+{
+ "name": "mediawiki/maps",
+ "type": "mediawiki-extension",
+ "description": "Adds various mapping features to MediaWiki",
+ "keywords": [
+ "MediaWiki",
+ "Semantic MediaWiki",
+ "Maps",
+ "Semantic Maps",
+ "Google Maps",
+ "OpenLayers",
+ "OSM",
+ "Leaflet",
+ "Geocode",
+ "Geocoding",
+ "OpenStreetMap"
+ ],
+ "homepage": "https://github.com/JeroenDeDauw/Maps",
+ "license": "GPL-2.0-or-later",
+ "authors": [
+ {
+ "name": "Jeroen De Dauw",
+ "email": "jeroendedauw@gmail.com",
+ "homepage": "https://www.entropywins.wtf",
+ "role": "Creator and lead developer"
+ }
+ ],
+ "support": {
+ "email": "semediawiki-user@lists.sourceforge.net",
+ "irc": "irc://irc.freenode.net/mediawiki",
+ "source": "https://github.com/JeroenDeDauw/Maps"
+ },
+ "require": {
+ "php": ">=7.1",
+ "composer/installers": "^1.0.1",
+ "mediawiki/validator": "~2.2",
+ "mediawiki/parser-hooks": "~1.5",
+ "param-processor/param-processor": "^1.4.2",
+ "data-values/geo": "~4.0|~3.0",
+ "jeroen/file-fetcher": "~6.0|~5.0",
+ "jeroen/file-fetcher-cache": "~1.0",
+ "jeroen/simple-cache": "~2.0",
+ "jeroen/simple-geocoder": "~1.2"
+ },
+ "require-dev": {
+ "data-values/common": "~0.4.1"
+ },
+ "autoload": {
+ "psr-4": {
+ "Maps\\": "src/",
+ "Maps\\Tests\\": "tests/"
+ },
+ "classmap": [
+ "MapsRegistration.php"
+ ]
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "7.x-dev"
+ }
+ },
+ "replace": {
+ "mediawiki/semantic-maps": "*"
+ },
+ "suggest": {
+ "mediawiki/semantic-media-wiki": "Add, edit, aggregate and visualize structured coordinate data stored with Semantic MediaWiki"
+ },
+ "config": {
+ "process-timeout": 0
+ },
+ "scripts":{
+ "ci": "php ../../tests/phpunit/phpunit.php -c phpunit.xml.dist"
+ }
+}
diff --git a/www/wiki/extensions/Maps/extension.json b/www/wiki/extensions/Maps/extension.json
new file mode 100644
index 00000000..f0bcddc8
--- /dev/null
+++ b/www/wiki/extensions/Maps/extension.json
@@ -0,0 +1,280 @@
+{
+ "name": "Maps",
+ "version": "7.4.0",
+
+ "author": [
+ "[https://www.entropywins.wtf/mediawiki Jeroen De Dauw]",
+ "..."
+ ],
+ "url": "https://github.com/JeroenDeDauw/Maps/blob/master/README.md#maps",
+ "descriptionmsg": "maps-desc",
+ "license-name": "GPL-2.0-or-later",
+ "type": "parserhook",
+
+ "requires": {
+ "MediaWiki": ">= 1.31.0"
+ },
+
+ "MessagesDirs": {
+ "Maps.class": [
+ "i18n"
+ ]
+ },
+ "ExtensionMessagesFiles": {
+ "MapsAlias": "i18n/aliases/Aliases.php",
+ "MapsMagic": "i18n/magic/MagicWords.php"
+ },
+
+ "callback": "MapsRegistration::onRegistration",
+ "ContentHandlers": {
+ "GeoJson": "Maps\\MediaWiki\\Content\\GeoJsonContentHandler"
+ },
+ "namespaces": [
+ {
+ "id": 420,
+ "constant": "NS_GEO_JSON",
+ "name": "GeoJson",
+ "subpages": false,
+ "content": true,
+ "defaultcontentmodel": "GeoJson"
+ },
+ {
+ "id": 421,
+ "constant": "NS_GEO_JSON_TALK",
+ "name": "GeoJson_talk",
+ "subpages": true,
+ "content": false,
+ "defaultcontentmodel": "wikitext"
+ }
+ ],
+
+ "ResourceFileModulePaths": {
+ "localBasePath": "resources",
+ "remoteExtPath": "Maps/resources"
+ },
+
+ "ResourceModules": {
+ "ext.maps.common": {
+ "scripts": [
+ "maps.common.js"
+ ],
+ "messages": [
+ "maps-load-failed"
+ ]
+ },
+
+ "ext.maps.services": {
+ "dependencies": [
+ "ext.maps.common"
+ ],
+ "scripts": [
+ "maps.services.js"
+ ]
+ },
+
+ "ext.sm.common": {
+ "dependencies": [
+ "ext.maps.common",
+ "ext.maps.services"
+ ],
+ "scripts": [
+ "sm.common.js"
+ ]
+ },
+
+ "mapeditor": {
+ "dependencies": [
+ "ext.maps.common",
+ "jquery.ui.autocomplete",
+ "jquery.ui.slider",
+ "jquery.ui.dialog"
+ ],
+ "scripts": [
+ "editor/js/jquery.miniColors.js",
+ "editor/js/mapeditor.iefixes.js",
+ "editor/js/mapeditor.js"
+ ],
+ "styles": [
+ "editor/css/jquery.miniColors.css",
+ "editor/css/mapeditor.css"
+ ],
+ "messages": [
+ "mapeditor-parser-error",
+ "mapeditor-none-text",
+ "mapeditor-done-button",
+ "mapeditor-remove-button",
+ "mapeditor-import-button",
+ "mapeditor-export-button",
+ "mapeditor-import-button2",
+ "mapeditor-select-button",
+ "mapeditor-mapparam-button",
+ "mapeditor-clear-button",
+ "mapeditor-imageoverlay-button"
+ ]
+ },
+
+ "ext.maps.resizable": {
+ "dependencies": [
+ "jquery.ui.resizable"
+ ]
+ },
+
+ "ext.maps.leaflet.base": {
+ "scripts": [
+ "leaflet/leaflet/leaflet.js"
+ ],
+ "styles": [
+ "leaflet/leaflet/leaflet.css"
+ ]
+ },
+
+ "ext.maps.leaflet": {
+ "dependencies": [
+ "ext.maps.common",
+ "ext.maps.services",
+ "ext.maps.leaflet.base"
+ ],
+ "scripts": [
+ "leaflet/jquery.leaflet.js",
+ "leaflet/ext.maps.leaflet.js"
+ ],
+ "messages": [
+ "maps-markers",
+ "maps-copycoords-prompt",
+ "maps-searchmarkers-text"
+ ]
+ },
+
+ "ext.maps.leaflet.fullscreen": {
+ "dependencies": [
+ "ext.maps.leaflet.base"
+ ],
+ "scripts": [
+ "leaflet/leaflet.fullscreen/Control.FullScreen.js"
+ ],
+ "styles": [
+ "leaflet/leaflet.fullscreen/Control.FullScreen.css"
+ ]
+ },
+
+ "ext.maps.leaflet.markercluster": {
+ "dependencies": [
+ "ext.maps.leaflet.base"
+ ],
+ "scripts": [
+ "leaflet/leaflet.markercluster/leaflet.markercluster.js"
+ ],
+ "styles": [
+ "leaflet/leaflet.markercluster/MarkerCluster.css"
+ ]
+ },
+
+ "ext.maps.leaflet.providers": {
+ "dependencies": [
+ "ext.maps.leaflet.base"
+ ],
+ "scripts": [
+ "leaflet/leaflet-providers/leaflet-providers.js"
+ ]
+ },
+
+ "ext.maps.leaflet.editable": {
+ "dependencies": [
+ "ext.maps.leaflet.base"
+ ],
+ "scripts": [
+ "leaflet/leaflet.editable/Leaflet.Editable.js"
+ ]
+ },
+
+ "ext.maps.leaflet.editor": {
+ "dependencies": [
+ "ext.maps.leaflet"
+ ],
+ "scripts": [
+ "leaflet/leaflet.editor.js"
+ ]
+ },
+
+ "ext.maps.leaflet.leafletajax": {
+ "dependencies": [
+ "ext.maps.leaflet",
+ "ext.sm.common"
+ ],
+ "scripts": [
+ "leaflet/ext.sm.leafletajax.js"
+ ]
+ },
+
+ "ext.maps.googlemaps3": {
+ "dependencies": [
+ "ext.maps.common"
+ ],
+ "scripts": [
+ "GoogleMaps/jquery.googlemap.js",
+ "GoogleMaps/ext.maps.googlemaps3.js"
+ ],
+ "messages": [
+ "maps-googlemaps3-incompatbrowser",
+ "maps-copycoords-prompt",
+ "maps-searchmarkers-text",
+ "maps-fullscreen-button",
+ "maps-fullscreen-button-tooltip"
+ ]
+ },
+
+ "ext.maps.gm3.markercluster": {
+ "dependencies": [
+ "ext.maps.googlemaps3"
+ ],
+ "scripts": [
+ "GoogleMaps/gm3-util-library/markerclusterer.js"
+ ]
+ },
+
+ "ext.maps.gm3.markerwithlabel": {
+ "dependencies": [
+ "ext.maps.googlemaps3"
+ ],
+ "scripts": [
+ "GoogleMaps/gm3-util-library/markerwithlabel.js"
+ ],
+ "styles": [
+ "GoogleMaps/gm3-util-library/markerwithlabel.css"
+ ]
+ },
+
+ "ext.maps.gm3.geoxml": {
+ "dependencies": [
+ "ext.maps.googlemaps3"
+ ],
+ "scripts": [
+ "GoogleMaps/geoxml3/geoxml3.js",
+ "GoogleMaps/geoxml3/ZipFile.complete.js",
+ "GoogleMaps/geoxml3/ProjectedOverlay.js"
+ ]
+ },
+
+ "ext.maps.gm3.earth": {
+ "dependencies": [
+ "ext.maps.googlemaps3"
+ ],
+ "scripts": [
+ "GoogleMaps/gm3-util-library/googleearth-compiled.js"
+ ]
+ },
+
+ "ext.sm.googlemaps3ajax": {
+ "dependencies": [
+ "ext.maps.googlemaps3",
+ "ext.sm.common"
+ ],
+ "scripts": [
+ "GoogleMaps/ext.sm.googlemaps3ajax.js"
+ ]
+ }
+ },
+
+ "load_composer_autoloader": true,
+ "manifest_version": 2
+}
diff --git a/www/wiki/extensions/Maps/i18n/af.json b/www/wiki/extensions/Maps/i18n/af.json
new file mode 100644
index 00000000..22b88fad
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/af.json
@@ -0,0 +1,27 @@
+{
+ "@metadata": {
+ "authors": [
+ "Naudefj"
+ ]
+ },
+ "maps_map": "Kaart",
+ "maps-abb-north": "N",
+ "maps-abb-east": "O",
+ "maps-abb-south": "S",
+ "maps-abb-west": "W",
+ "maps-latitude": "Breedte:",
+ "maps-longitude": "Lengte:",
+ "maps_coordinates_missing": "Geen koördinate is vir die kaart verskaf nie.",
+ "maps_unrecognized_coords": "Die volgende koördinate is nie herken nie: $1.",
+ "maps_unrecognized_coords_for": "Die volgende {{PLURAL:$2|koördinaat|koördinate}} is nie herken nie en is uit die kaart weggelaat:\n$1.",
+ "maps_map_cannot_be_displayed": "Die kaart kan nie vertoon word nie.",
+ "maps_click_to_activate": "Kliek om die kaart te aktiveer",
+ "maps_centred_on": "Kaart gesentreer op $1, $2.",
+ "specialpages-group-maps": "Kaarte",
+ "mapeditor-none-text": "Geen",
+ "mapeditor-link-title-switcher-link-text": "Skakel",
+ "mapeditor-form-field-title": "Titel",
+ "mapeditor-form-field-text": "Teks",
+ "mapeditor-form-field-icon": "Ikoon",
+ "mapeditor-form-field-group": "Groep"
+}
diff --git a/www/wiki/extensions/Maps/i18n/aliases/Aliases.php b/www/wiki/extensions/Maps/i18n/aliases/Aliases.php
new file mode 100644
index 00000000..0c89dec3
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/aliases/Aliases.php
@@ -0,0 +1,129 @@
+<?php
+$specialPageAliases = [];
+
+/** English
+ * @author netbrain
+ */
+// @codingStandardsIgnoreFile
+
+$specialPageAliases = [];
+
+/** English (English) */
+$specialPageAliases['en'] = [
+ 'MapEditor' => [ 'MapEditor' ],
+];
+
+/** Arabic (العربية) */
+$specialPageAliases['ar'] = [
+ 'MapEditor' => [ 'محرر_الخريطة' ],
+];
+
+/** Egyptian Arabic (مصرى) */
+$specialPageAliases['arz'] = [
+ 'MapEditor' => [ 'محرر_الخريطه' ],
+];
+
+/** Assamese (অসমীয়া) */
+$specialPageAliases['as'] = [
+ 'MapEditor' => [ 'মানচিত্ৰ_সম্পাদক' ],
+];
+
+/** Western Balochi (بلوچی رخشانی) */
+$specialPageAliases['bgn'] = [
+ 'MapEditor' => [ 'نخشه_ئی_ایڈیٹگر' ],
+];
+
+/** German (Deutsch) */
+$specialPageAliases['de'] = [
+ 'MapEditor' => [ 'Karteneditor' ],
+];
+
+/** Zazaki (Zazaki) */
+$specialPageAliases['diq'] = [
+ 'MapEditor' => [ 'VırneréXerita' ],
+];
+
+/** Greek (Ελληνικά) */
+$specialPageAliases['el'] = [
+ 'MapEditor' => [ 'ΕπεξεργαστήςΧαρτών' ],
+];
+
+/** Persian (فارسی) */
+$specialPageAliases['fa'] = [
+ 'MapEditor' => [ 'ویرایشگر_نقشه' ],
+];
+
+/** Galician (galego) */
+$specialPageAliases['gl'] = [
+ 'MapEditor' => [ 'Editor_do_mapa' ],
+];
+
+/** Hebrew (עברית) */
+$specialPageAliases['he'] = [
+ 'MapEditor' => [ 'עורך_מפה' ],
+];
+
+/** Upper Sorbian (hornjoserbsce) */
+$specialPageAliases['hsb'] = [
+ 'MapEditor' => [ 'Kartowy_editor' ],
+];
+
+/** Italian (italiano) */
+$specialPageAliases['it'] = [
+ 'MapEditor' => [ 'EditorMappa' ],
+];
+
+/** Japanese (日本語) */
+$specialPageAliases['ja'] = [
+ 'MapEditor' => [ '地図編集', 'マップ編集' ],
+];
+
+/** Korean (한국어) */
+$specialPageAliases['ko'] = [
+ 'MapEditor' => [ '지도편집기' ],
+];
+
+/** Luxembourgish (Lëtzebuergesch) */
+$specialPageAliases['lb'] = [
+ 'MapEditor' => [ 'Kaartenediteur' ],
+];
+
+/** Macedonian (македонски) */
+$specialPageAliases['mk'] = [
+ 'MapEditor' => [ 'УредникНаКарти' ],
+];
+
+/** Malayalam (മലയാളം) */
+$specialPageAliases['ml'] = [
+ 'MapEditor' => [ 'ഭൂപടതിരുത്തലുപാധി' ],
+];
+
+/** Dutch (Nederlands) */
+$specialPageAliases['nl'] = [
+ 'MapEditor' => [ 'Kaarteditor' ],
+];
+
+/** Sicilian (sicilianu) */
+$specialPageAliases['scn'] = [
+ 'MapEditor' => [ 'EditorMappa' ],
+];
+
+/** Turkish (Türkçe) */
+$specialPageAliases['tr'] = [
+ 'MapEditor' => [ 'HaritaDüzenleyici', 'HaritaEditörü' ],
+];
+
+/** Vietnamese (Tiếng Việt) */
+$specialPageAliases['vi'] = [
+ 'MapEditor' => [ 'Sửa_đổi_bản_đồ' ],
+];
+
+/** Simplified Chinese (中文(简体)‎) */
+$specialPageAliases['zh-hans'] = [
+ 'MapEditor' => [ '地图编辑器' ],
+];
+
+/** Traditional Chinese (中文(繁體)‎) */
+$specialPageAliases['zh-hant'] = [
+ 'MapEditor' => [ '地圖編輯器' ],
+]; \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/i18n/aln.json b/www/wiki/extensions/Maps/i18n/aln.json
new file mode 100644
index 00000000..3a3ab54c
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/aln.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Mdupont"
+ ]
+ },
+ "maps-desc": "Ofron mundësinë për të shfaqur koordinimin e të dhënave në harta, dhe adresat geocode ([http://mapping.referata.com/wiki/Maps_examples demo]). Hartës shërbimet në dispozicion: $1",
+ "maps_map": "Hartë",
+ "maps-loading-map": "Loading Harta ...",
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "W",
+ "maps-latitude": "Latitude:",
+ "maps-longitude": "Gjatësi:",
+ "maps-invalid-coordinates": "Vlera $1 nuk është njohur si një grup të vlefshme të kordinatave.",
+ "maps_coordinates_missing": "Nuk ka koordinon parashikuara në hartë.",
+ "maps_geocoding_failed": "Më poshtë {{PLURAL:$2|Adresa|adresat}} nuk mund të geocoded: $1."
+}
diff --git a/www/wiki/extensions/Maps/i18n/ar.json b/www/wiki/extensions/Maps/i18n/ar.json
new file mode 100644
index 00000000..3dcd2223
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ar.json
@@ -0,0 +1,227 @@
+{
+ "@metadata": {
+ "authors": [
+ "Imksa",
+ "Meno25",
+ "Macofe",
+ "ديفيد"
+ ]
+ },
+ "maps-desc": "يسمح بتضمين خرائط ديناميكية إلى صفحات الويكي، كود العناوين وعمليات جغرافية أخرى",
+ "right-geocode": "الترميز الجغرافي",
+ "action-geocode": "تفعيل الترميز الجغرافي في هذا الويكي",
+ "maps_map": "خريطة",
+ "maps-tracking-category": "الصفحات التي تحتوي على خريطة مقدمة من تمديد الخرائط",
+ "maps-loading-map": "تحميل الخريطة...",
+ "maps-load-failed": "لا يمكن تحميل الخريطة!",
+ "maps-markers": "علامات",
+ "maps-copycoords-prompt": "CTRL+C، ENTER",
+ "maps-searchmarkers-text": "علامات التصفية",
+ "maps-others": "أخرون",
+ "maps-kml-parsing-failed": "أخفق تحليل ملف KML واحد أو أكثر; عادة ما يحدث هذا بسبب فشل أو تلف استرجاع XML.",
+ "maps-ns-layer": "طبقة",
+ "maps-ns-layer-talk": "نقاش الطبقة",
+ "maps-layer-property": "خاصية",
+ "maps-layer-value": "القيمة",
+ "maps-layer-errors": "أخطاء",
+ "maps-layerdef-invalid": "{{PLURAL:$1|تحديد}} غير صالح",
+ "maps-layerdef-invalid-fatal": "تعريف غير صالح قاتل",
+ "maps-layerdef-wrong-namespace": "تعريفات الطبقة صالحة فقط في نطاق \"$1\"",
+ "maps-layerdef-equal-layer-name": "يجب أن تكون أسماء الطبقات فريدة داخل صفحة الطبقة نفسها، يتم بالفعل استخدام \"$1\" من قبل طبقة أخرى.",
+ "maps-layerpage-usage": "الصفحات التي تحتوي على خرائط باستخدام طبقة \"$1\"",
+ "maps-layerpage-nousage": "لا توجد صفحات تستخدم هذه الطبقة في الوقت الحالي.",
+ "maps-error-invalid-layertype": "لا توجد طبقات من نوع \" $1 \". فقط {{PLURAL:$3| هذا النوع هو | هذه الأنواع هي}} المدعومة : $2",
+ "maps-error-no-layertype": "تحتاج إلى تحديد نوع طبقة. {{PLURAL:$2| هذا النوع فقط | هذه الأنواع هي}} المدعومة : $1",
+ "validation-error-invalid-layer": "المعلمة $1 يجب أن تكون طبقة صالحة.",
+ "validation-error-invalid-layers": "المعلمة $1 يجب أن تكون طبقة واحدة أو أكثر صالحة.",
+ "validation-error-no-non-numeric": "يجب أن يكون الوسيط \"$1\" سلسلة غير رقمية.",
+ "validation-error-no-non-numerics": "يجب أن يكون الوسيط \"$1\" واحدة أو أكثر من السلاسل غير الرقمية.",
+ "maps-layer-of-type": "طبقة من نوع $1",
+ "maps-layer-of-type-and-name": "طبقة \"$2\" من نوع \"$1\"",
+ "maps-layer-type-supported-by": "نوع الطبقة هذا يمكن أن {{PLURAL:$2| يمكن استخدامها فقط مع $1 خدمة الخرائط | يمكن استخدامها مع خدمات الخرائط : $1 }}.",
+ "maps-coordinates-description": "ربط محلل بإحداثيات الشكل ، من وإلى أي من الأشكال المدعومة.",
+ "maps-displaymap-description": "عرض الخرائط الجغرافية دون أي علامات ويكي معرفة عليها.",
+ "maps-distance-description": "تحويل المسافة باستخدام وحدة معتمدة معينة إلى ما يعادلها باستخدام وحدة أخرى.",
+ "maps-finddestination-description": "العثور على جهة معينة لنقطة البداية (التي يمكن أن تكون بأية صيغة مدعومة)، تحمل الأولي والمسافة.",
+ "maps-geocode-description": "تمكن الترميز الجغرافي للعناوين، وبعبارة أخرى، تحويل المواقع القابلة للقراءة البشرية إلى مجموعات من الإحداثيات، وهناك دعم لعدة خدمات للترميز الجغرافي، لا ينبغي الخلط بينها وبين خدمات رسم الخرائط.",
+ "maps-geodistance-description": "احسب المسافة الجغرافية بين نقطتين، من وإلى أي من الصيغ المدعومة.",
+ "maps-mapsdoc-description": "عرض جدول بالوسائط لخدمة تعيين محددة مع قيمها وأوصافها الافتراضية.",
+ "maps-layerdefinition-description": "يصف طبقة مخصصة والتي يمكن عرضها مع وظائف خريطة أخرى.",
+ "maps-mapsdoc-par-service": "خدمة رسم الخرائط لعرض توثيق الوسيط.",
+ "maps-mapsdoc-par-language": "اللغة التي يتم بها عرض التوثايق، إذا لم تتوفر مثل هذه الترجمة، سيتم استخدام الإنجليزية بدلا من ذلك.",
+ "maps-coordinates-par-location": "الإحداثيات التي تريد تنسيقها.",
+ "maps-coordinates-par-format": "الصيغة الهدف للإحداثيات.",
+ "maps-coordinates-par-directional": "يشير إلى ما إذا كان ينبغي إخراج الإحداثيات اتجاهيا أم لا.",
+ "maps-par-scrollwheelzoom": "يشير إلى ما إذا كان يجب تمكين تمرير الفأرة أم لا.",
+ "maps-distance-par-distance": "المسافة للتحويل إلى ما يعادلها بوحدة محددة.",
+ "maps-distance-par-decimals": "الحد الأقصى لعدد الأرقام الكسرية التي سيتم استخدامها في القيمة الناتجة.",
+ "maps-distance-par-unit": "وحدة لإخراج المسافة بها.",
+ "maps-finddestination-par-location": "الموقع الأولي.",
+ "maps-finddestination-par-bearing": "التحمل الأولي.",
+ "maps-finddestination-par-distance": "مسافة للسفر.",
+ "maps-finddestination-par-format": "الصيغة التي سيتم بها إخراج الوجهة.",
+ "maps-finddestination-par-directional": "يشير إلى ما إذا كانت صيغة الوجهة يجب أن تكون اتجاهية أم لا.",
+ "maps-geocode-par-location": "العنوان الذي تريد ترميزه جغرافيا.",
+ "maps-geocode-par-format": "صيغة الإحداثيات الناتجة.",
+ "maps-geocode-par-directional": "يشير إلى ما إذا كان ينبغي إخراج الإحداثيات اتجاهيا أم لا.",
+ "maps-geodistance-par-location1": "النقطة الأولى في المجموعة لحساب المسافة.",
+ "maps-geodistance-par-location2": "النقطة الثانية في المجموعة لحساب المسافة.",
+ "maps-geodistance-par-unit": "وحدة لإخراج المسافة بها.",
+ "maps-geodistance-par-decimals": "الحد الأقصى لعدد الأرقام الكسرية التي سيتم استخدامها في القيمة الناتجة.",
+ "maps-displaymap-par-mappingservice": "السماح بتعيين خدمة التعرسم الخرائط التي سيتم استخدامها لإنشاء الخريطة.",
+ "maps-displaymap-par-coordinates": "موقع واحد أو أكثر لعرضه على الخريطة، سيتم الإشارة إليه بعلامة.",
+ "maps-displaymap-par-visitedicon": "اسم ملف صورة لاستخدامها لأيقونات علامة بعد النقر على العلامات الأصلية",
+ "maps-displaymap-par-zoom": "السماح بتحديد مستوى تكبير للخريطة،\nعندما لا يتم التوفير وإيجاد علامات متعددة على الخريطة، سيتم اتخاذ أفضل تكبير مناسب، وليس الافتراضي الشكلي.",
+ "maps-displaymap-par-centre": "يسمح تعيين إحداثيات مركز الخريطة لـdisplay_point(s)،\nيقبل كل من العناوين والإحداثيات،\nعندما لا يتم توفير هذه الخاصية، ستركز الخريطة نفسها على العلامة المقدمة، أو بين العلامات المقدمة.",
+ "maps-displaymap-par-title": "يسماح بتعيين النص الذي سيتم عرضه في النوافذ المنبثقة من جميع العلامات التي ليس لها عنوان محدد،\nعند استخدامها جنبا إلى جنب مع التسمية، سيكون العنوان غامقا وتحته خط.",
+ "maps-displaymap-par-label": "يسمح بتعيين النص الذي سيتم عرضه في النوافذ المنبثقة من جميع العلامات التي لا تحتوي على تسمية محددة.",
+ "maps-displaymap-par-icon": "يسمح بتعيين الأيقونة المستخدمة لجميع العلامات.",
+ "maps-displaymap-par-circles": "دوائر لعرضها",
+ "maps-displaymap-par-copycoords": "عرض مربع حوار عند النقر على موقع يمكن نسخ إحداثياته ​​منه",
+ "maps-displaymap-par-lines": "خطوط لعرضها",
+ "maps-displaymap-par-maxzoom": "الحد الأقصى لمستوى التكبير",
+ "maps-displaymap-par-minzoom": "الحد الأدنى لمستوى التكبير",
+ "maps-displaymap-par-polygons": "مضلعات لعرضها",
+ "maps-displaymap-par-rectangles": "مستطيلات للعرض",
+ "maps-displaymap-par-static": "جعل الخريطة ثابتة",
+ "maps-displaymap-par-wmsoverlay": "استخدم تراكب خدمة خرائط الويب",
+ "maps-displaymap-par-geojson": "مسار ملف أو اسم الصفحة التي تحتوي على بيانات GeoJSON",
+ "maps-fullscreen-button": "تبديل ملء الشاشة",
+ "maps-fullscreen-button-tooltip": "عرض الخريطة على أنها ملء الشاشة أو مضمنة.",
+ "validation-error-invalid-location": "يجب أن يكون الوسيط \"$1\" موقعا صالحا.",
+ "validation-error-invalid-locations": "يجب أن يكون الوسيط \"$1\" واحدا أو أكثر من المواقع الصالحة.",
+ "validation-error-invalid-width": "يجب أن يكون الوسيط \"$1\" عرضا صالحا.",
+ "validation-error-invalid-height": "يجب أن يكون الوسيط \"$1\" ارتفاعا صالحا.",
+ "validation-error-invalid-distance": "يجب أن يكون الوسيط \"$1\" مسافة صالحة.",
+ "validation-error-invalid-distances": "يجب أن يكون الوسيط \"$1\" مسافة صالحة واحدة أو أكثر.",
+ "validation-error-invalid-image": "يجب أن يكون الوسيط \"$1\" صورة صالحة.",
+ "validation-error-invalid-images": "يجب أن يكون الوسيط \"$1\" صورة صالحة واحدة أو أكثر.",
+ "validation-error-invalid-goverlay": "يجب أن يكون الوسيط \"$1\" تراكبا صالحا.",
+ "validation-error-invalid-goverlays": "يجب أن يكون الوسيط \"$1\" واحدا أو أكثر من التراكبات الصالحة.",
+ "maps-abb-north": "شم",
+ "maps-abb-east": "شر",
+ "maps-abb-south": "ج",
+ "maps-abb-west": "غ",
+ "maps-latitude": "دائرة العرض:",
+ "maps-longitude": "خط الطول:",
+ "maps-invalid-coordinates": "لم يتم التعرف على القيمة $1 كمجموعة صالحة للإحداثيات.",
+ "maps_coordinates_missing": "لا إحداثيات موفرة للخريطة.",
+ "maps_geocoding_failed": "{{PLURAL:$2|العنوان التالي|العناوين التالية}} لم يمكن تكويدها جغرافيا: $1.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|العنوان التالي|العناوين التالية}} لم يمكن تكويدها جغرافيا و {{PLURAL:$2|تمت|تمت}} إزالتها من الخريطة:\n$1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|الإحداثي التالي|الإحداثيات التالية}} لم يتم التعرف عليها: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|الإحداثي التالي|الإحداثيات التالية}} لم يتم التعرف عليها و {{PLURAL:$2|تمت|تمت}} إزالتها من الخريطة:\n$1",
+ "maps_map_cannot_be_displayed": "الخريطة لا يمكن عرضها.",
+ "maps-geocoder-not-available": "ميزة الترميز الجغرافي للخرائط غير متوفرة; لا يمكن وضع رمز جغرافي لموقعك.",
+ "maps_googlemaps3": "خرائط جوجل v3",
+ "maps_leaflet": "الطبقة",
+ "maps-leaflet-par-defzoom": "يسمح بتعيين مستوى التكبير الافتراضي للخريطة.",
+ "maps-leaflet-par-layers": "الطبقات التي ستكون متاحة في محدد الطبقة، سيتم عرض الطبقة الأولى عند تحميل الخريطة.",
+ "maps-leaflet-par-overlaylayers": "طبقات التراكب التي سيتم عرضها عند تحميل الخريطة.",
+ "maps-leaflet-par-maxclusterradius": "الحد الأقصى لنصف القطر الذي سيغطيه العنقود من العلامة المركزية (بالبكسل).",
+ "maps-leaflet-par-clusterspiderfy": "عند النقر فوق عنقود في مستوى التكبير السفلي سنكبره حتى تتمكن من رؤية كل علاماته.",
+ "maps_click_to_activate": "اضغط لتنشيط الخريطة",
+ "maps_centred_on": "الخريطة مركزها في $1، $2.",
+ "maps-par-mappingservice": "يسمح بتعيين خدمة رسم الخرائط التي سيتم استخدامها لإنشاء الخريطة.",
+ "maps-par-resizable": "يجعل الخريطة قابلة للتغيير عن طريق سحبا في الزاوية السفلية اليمنى.",
+ "maps-par-searchmarkers": "يسمح بالبحث عن علامات محددة عبر حقل مضمن في الخريطة.",
+ "maps-par-zoom": "مستوى التكبير للخريطة، بالنسبة للخرائط التي تحتوي على علامات، سيؤدي هذا إلى الوضع الافتراضي إلى أقصى مستوى من التكبير الذي لا يزال يظهر جميع العلامات.",
+ "maps-par-width": "يسمح بتعيين عرض الخريطة، سيتم افتراض وحدات البكسل الافتراضية على أنها وحدة، ولكن يمكنك تحديد إحدى هذه الوحدات بشكل صريح: px، ex، em.",
+ "maps-par-height": "يسمح بتعيين ارتفاع الخريطة، سيتم افتراض وحدات البكسل الافتراضية على أنها وحدة، ولكن يمكنك تحديد إحدى هذه الوحدات بشكل صريح: px، ex، emK %.",
+ "maps-par-centre": "الموقع الذي يجب أن تركز عليه الخريطة",
+ "maps-par-enable-fullscreen": "تمكين زر ملء الشاشة",
+ "maps-par-kml": "ملفات KML لتحميلها على الخريطة.",
+ "maps-par-markercluster": "يسمح بدمج عدة علامات قريبة في علامة واحدة",
+ "maps-googlemaps3-incompatbrowser": "متصفحك غير متوافق مع الإصدار الثالث من خرائط جوجل.",
+ "maps-googlemaps3-par-imageoverlays": "يسمح بإضافة صورة ليتم عرضها على الموقع المحدد على الخريطة.",
+ "maps-googlemaps3-par-type": "نوع خريطة ليتم عرضه في البداية.",
+ "maps-googlemaps3-par-types": "أنواع الخريطة التي ستكون متاحة عن طريق التحكم في النوع.",
+ "maps-googlemaps3-par-layers": "طبقات خاصة للتحميل على الخريطة.",
+ "maps-googlemaps3-par-controls": "ضوابط للوضع على الخريطة.",
+ "maps-googlemaps3-par-zoomstyle": "أسلوب التحكم في التكبير.",
+ "maps-googlemaps3-par-typestyle": "أسلوب التحكم في النوع.",
+ "maps-googlemaps3-par-autoinfowindows": "فتح جميع نوافذ المعلومات تلقائيا بعد تحميل الصفحة.",
+ "maps-googlemaps3-par-gkml": "ملفات KML تستضيفها جوجل لتحميلها على الخريطة.",
+ "maps-googlemaps3-par-kmlrezoom": "إعادة تكبير الخريطة بعد تحميل طبقات KML.",
+ "maps-googlemaps3-par-poi": "عرض نقاط الاهتمام.",
+ "maps-googlemaps3-par-clustergridsize": "حجم شبكة العنقود بالبكسل.",
+ "maps-par-clustermaxzoom": "الحد الأقصى لمستوى التكبير حيث قد يوجد عنقود.",
+ "maps-par-clusterzoomonclick": "ما إذا كان السلوك الافتراضي للنقر على عنقود هو تكبيره.",
+ "maps-par-maxclusterradius": "الحد الأقصى لنصف القطر الذي سيغطيه العنقود.",
+ "maps-googlemaps3-par-clusteraveragecenter": "ما إذا كان مركز كل عنقود يجب أن يكون متوسط ​​جميع العلامات في العنقود.",
+ "maps-googlemaps3-par-clusterminsize": "الحد الأدنى لعدد العلامات لتكون في عنقود قبل إخفاء العلامات وإظهار العد.",
+ "mapeditor": "محرر الخرائط",
+ "specialpages-group-maps": "الخرائط",
+ "mapeditor-parser-error": "حدث خطأ أثناء تحليل البيانات الوصفية; يتم تجاهل إدخال المستخدم.",
+ "mapeditor-none-text": "لا شيء",
+ "mapeditor-done-button": "تم",
+ "mapeditor-remove-button": "إزالة",
+ "mapeditor-import-button2": "استيراد",
+ "mapeditor-export-button": "تصدير إلى رمز ويكي",
+ "mapeditor-import-button": "استيراد من رمز ويكي",
+ "mapeditor-select-button": "حدد هذا المضلع",
+ "mapeditor-mapparam-button": "تحرير وسائط الخريطة",
+ "mapeditor-clear-button": "مسح الخريطة",
+ "mapeditor-code-title": "رمز ويكي",
+ "mapeditor-import-title": "استيراد رمز ويكي",
+ "mapeditor-import-note": "تُرجَى ملاحظة أن المحلل يتوقع تنسيقا صارما جدا على رمز الويكي، يجب أن تتطابق الكود المدخل هنا مع الكود الذي تم إخراجه بواسطة وظيفة التصدير.",
+ "mapeditor-form-title": "عدل التفاصيل",
+ "mapeditor-link-title-switcher-popup-text": "انبثق مع النص",
+ "mapeditor-link-title-switcher-link-text": "وصلة",
+ "mapeditor-form-field-title": "العنوان",
+ "mapeditor-form-field-text": "النص",
+ "mapeditor-form-field-link": "الوصلة",
+ "mapeditor-form-field-icon": "الأيقونة",
+ "mapeditor-form-field-group": "المجموعة",
+ "mapeditor-form-field-inlinelabel": "التسمية المضمنة",
+ "mapeditor-form-field-strokecolor": "لون الخط",
+ "mapeditor-form-field-strokeopacity": "تعتيم الخط",
+ "mapeditor-form-field-strokeweight": "وزن الخط",
+ "mapeditor-form-field-fillcolor": "ملء اللون",
+ "mapeditor-form-field-fillopcaity": "ملء التعتيم",
+ "mapeditor-form-field-showonhover": "إظهار على التمرير فقط",
+ "mapeditor-mapparam-title": "تحرير وسائط الخريطة",
+ "mapeditor-mapparam-defoption": "-اختر وسيطا-",
+ "mapeditor-imageoverlay-button": "إضافة تراكب صورة",
+ "mapeditor-form-field-image": "الصورة",
+ "mapeditor-imageoverlay-title": "تفاصيل تراكب الصور",
+ "mapeditor-form-field-visitedicon": "أيقونة تمت زيارتها",
+ "semanticmaps-unrecognizeddistance": "القيمة $1 ليست مسافة صحيحة.",
+ "semanticmaps-kml-link": "عرض ملف KML",
+ "semanticmaps-kml": "كيه إم إل",
+ "semanticmaps-default-kml-pagelink": "عرض الصفحة $1",
+ "semanticmaps-latitude": "دائرة العرض: $1",
+ "semanticmaps-longitude": "خط الطول: $1",
+ "semanticmaps-altitude": "الارتفاع: $1",
+ "semanticmaps-forminput-locations": "المواقع",
+ "semanticmaps-par-staticlocations": "قائمة بالمواقع التي تريد إضافتها إلى الخريطة مع البيانات التي تم الاستعلام عنها، كما هو الحال مع display_points، يمكنك إضافة عنوان ووصف ورمز لكل موقع باستخدام التلدة \"~\" كفاصل.",
+ "semanticmaps-par-showtitle": "عرض عنوان في نافذة معلومات العلامة أو لا، غالبا ما يكون تعطيل هذا الأمر مفيدا عند استخدام قالب لتنسيق محتوى نافذة المعلومات.",
+ "semanticmaps-par-hidenamespace": "عرض عنوان النطاق في نافذة معلومات العلامة",
+ "semanticmaps-par-centre": "مركز الخريطة، عندما لا يتم توفيره، فإن الخريطة تلقائيا تختار المركز الأمثل لعرض كافة العلامات على الخريطة.",
+ "semanticmaps-par-template": "قالب لاستخدامه لتنسيق محتويات نافذة المعلومات.",
+ "semanticmaps-par-geocodecontrol": "عرض تحكم الترميز الجغرافي.",
+ "semanticmaps-par-activeicon": "أيقونة ليتم عرضها بدلا من علامة افتراضية، عندما تكون الصفحة النشطة تساوي نتيجة الاستعلام",
+ "semanticmaps-par-pagelabel": "عند تعيينها لـ\"نعم\"، ستكون لكل علامات \"التسمية المضمنة\" مع وصلة إلى الصفحة التي تحتوي على إحداثيات للعلامة",
+ "semanticmaps-par-ajaxcoordproperty": "اسم خاصية الإحداثيات التي يتم استخدامها لبناء استعلام أجاكس.",
+ "semanticmaps-par-ajaxquery": "الاستعلام الثاني الذي يتم إرساله عبر أجاكس لجلب إحداثيات إضافية.",
+ "semanticmaps-par-userparam": "قيمة تم تمريرها في كل استدعاء قالب، إذا تم استخدام قالب",
+ "semanticmaps-kml-text": "النص المرتبط بكل صفحة، يتم تجاوزه بواسطة الخصائص الإضافية التي تم الاستعلام عنها إن وُجِدت.",
+ "semanticmaps-kml-title": "العنوان الافتراضي للنتائج",
+ "semanticmaps-kml-linkabsolute": "يجب أن تكون الروابط مطلقة (بدلا من النسبية)",
+ "semanticmaps-kml-pagelinktext": "النص الذي سيتم استخدامه للروابط إلى الصفحة، بحيث سيتم استبدال $1 بعنوان الصفحة",
+ "semanticmaps-shapes-improperformat": "تنسيق غير صحيح بقيمة $1; يُرجَى الاطلاع على توثيق التنسيق",
+ "semanticmaps-shapes-missingshape": "لم يتم العثور على أية أشكال مقابل $1; يُرجَى الاطلاع على توثيق الأشكال المتاحة",
+ "validator-type-mapscircle": "الدائرة الجغرافية",
+ "validator-type-mapscircle-list": "قائمة الدوائر",
+ "validator-type-mapsimageoverlay": "تراكب الصور",
+ "validator-type-mapsimageoverlay-list": "قائمة تراكبات الصور",
+ "validator-type-mapsline": "الخط الجغرافي",
+ "validator-type-mapsline-list": "قائمة السطور",
+ "validator-type-mapslocation": "الموقع الجغرافي",
+ "validator-type-mapslocation-list": "قائمة المواقع",
+ "validator-type-mapsrectangle": "المستطيل الجغرافي",
+ "validator-type-mapsrectangle-list": "قائمة المستطيلات",
+ "validator-type-mapspolygon": "المضلع الجغرافي",
+ "validator-type-mapspolygon-list": "قائمة المضلعات الجغرافية",
+ "validator-type-wmsoverlay": "تراكب خدمة خريطة الويب",
+ "validator-type-jsonfile": "نص"
+}
diff --git a/www/wiki/extensions/Maps/i18n/arc.json b/www/wiki/extensions/Maps/i18n/arc.json
new file mode 100644
index 00000000..05c734c5
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/arc.json
@@ -0,0 +1,15 @@
+{
+ "@metadata": {
+ "authors": [
+ "Basharh"
+ ]
+ },
+ "maps_map": "ܨܘܪܬ ܥܠܡܐ",
+ "maps-layer-property": "ܕܝܠܝܬܐ",
+ "maps-layer-value": "ܛܝܡܐ",
+ "maps-layer-errors": "ܦܘܕ̈ܐ",
+ "maps-abb-north": "ܓܪܒܝܐ",
+ "maps-abb-east": "ܡܕܢܚܐ",
+ "maps-abb-south": "ܬܝܡܢܐ",
+ "maps-abb-west": "ܡܥܪܒܐ"
+}
diff --git a/www/wiki/extensions/Maps/i18n/arz.json b/www/wiki/extensions/Maps/i18n/arz.json
new file mode 100644
index 00000000..aba64ff8
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/arz.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Meno25"
+ ]
+ },
+ "maps-abb-north": "شمال",
+ "maps-abb-east": "شرق",
+ "maps-abb-south": "جنوب",
+ "maps-abb-west": "غرب",
+ "maps-latitude": "دوائر العرض:",
+ "maps-longitude": "خطوط الطول:"
+}
diff --git a/www/wiki/extensions/Maps/i18n/ast.json b/www/wiki/extensions/Maps/i18n/ast.json
new file mode 100644
index 00000000..7f11fc02
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ast.json
@@ -0,0 +1,223 @@
+{
+ "@metadata": {
+ "authors": [
+ "Xuacu",
+ "Macofe"
+ ]
+ },
+ "maps-desc": "Permite inxertar mapes dinámicos nes páxines wiki, xeocodificación de direiciones y otres operaciones xeográfiques",
+ "right-geocode": "Xeocódigu",
+ "action-geocode": "xeocodificar nesta wiki",
+ "maps_map": "Mapa",
+ "maps-tracking-category": "Páxines con un mapa renderizáu pola estensión Maps",
+ "maps-loading-map": "Cargando'l mapa...",
+ "maps-load-failed": "¡Nun se pudo cargar el mapa!",
+ "maps-markers": "Marcadores",
+ "maps-copycoords-prompt": "CTRL+C, Intro",
+ "maps-searchmarkers-text": "Peñerar marcadores",
+ "maps-others": "otros",
+ "maps-kml-parsing-failed": "Falló l'analís d'un ficheru KML o más, de vezu debío a un fallu na recuperación o a un XML con mal formatu.",
+ "maps-ns-layer": "Capa",
+ "maps-ns-layer-talk": "Alderique de capa",
+ "maps-layer-property": "Propiedá",
+ "maps-layer-value": "Valor",
+ "maps-layer-errors": "Errores",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Definición inválida|Definiciones inválides}}",
+ "maps-layerdef-invalid-fatal": "Definición inválida fatal",
+ "maps-layerdef-wrong-namespace": "Les definiciones de capes namái son válides nes páxines del espaciu de nomes «$1»",
+ "maps-layerdef-equal-layer-name": "Los nomes de capa tienen de ser únicos dientro de la mesma páxina de capes. Otra capa yá ta usando «$1».",
+ "maps-layerpage-usage": "Páxines con mapes qu'usen la capa «$1»",
+ "maps-layerpage-nousage": "Nesti momentu denguna páxina ta usando esta capa.",
+ "maps-error-invalid-layertype": "Nun hai capes de tipu \"$1\". Sólo {{PLURAL:$3|tien sofitu esti tipu|tienen sofitu estos tipos}}: $2",
+ "maps-error-no-layertype": "Tienes d'especificar la triba de capa. {{PLURAL:$2|Sólo tien sofitu esta triba|Tienen sofitu estes tribes}}: $1",
+ "validation-error-invalid-layer": "El parámetru $1 tien de ser una capa válida.",
+ "validation-error-invalid-layers": "El parámetru $1 tien de ser una o más capes válides.",
+ "validation-error-no-non-numeric": "El parámetru «$1» tien de ser una cadena non-numbérica.",
+ "validation-error-no-non-numerics": "El parámetru «$1» tien de ser una o más cadenes non-numbériques.",
+ "maps-layer-of-type": "Capa de tipu $1",
+ "maps-layer-of-type-and-name": "Capa «$2» de tipu «$1»",
+ "maps-layer-type-supported-by": "Esta triba de capa pue emplegase {{PLURAL:$2|namái col serviciu de mapes $1|con estos servicios de mapes: $1}}.",
+ "maps-coordinates-description": "Asociador del analizador pa dar formatu a les coordenaes, dende y a cualesquiera de los formatos sofitaos.",
+ "maps-displaymap-description": "Amosar mapes xeográficos ensin dengún marcador definíu na wiki nellos.",
+ "maps-distance-description": "Convertir una distancia usando cierta unidá sofitada al equivalente utilizando otra unidá.",
+ "maps-finddestination-description": "Alcontrar un destín dende un puntu de partida (que pue tar en cualesquiera de los formatos compatibles), una orientación inicial y una distancia.",
+ "maps-geocode-description": "Activa la xeocodificación de direiciones; n'otres pallabres, tresformar llugares lleíbles por humanos en conxuntos de coordenaes. Hai sofitu pa dellos servicios de xeocodificación, que nun tienen de confundise con servicios de mapes.",
+ "maps-geodistance-description": "Calcular la distancia xeográfica ente dos puntos, dende y a cualesquiera de los formatos compatibles.",
+ "maps-mapsdoc-description": "Amosar una tabla colos parámetros d'un serviciu de mapes específicu, xunto colos valores y descripciones predeterminaos.",
+ "maps-layerdefinition-description": "Describe una capa personalizada que se pue amosar con otres funciones de Mapa.",
+ "maps-mapsdoc-par-service": "El serviciu de mapes del que se quier amosar la documentación de parámetros.",
+ "maps-mapsdoc-par-language": "La llingua na que presentar la documentación. Si esa traducción nun tuviera disponible, s'usará la versión n'inglés nel so llugar.",
+ "maps-coordinates-par-location": "Les coordenaes a les que quies dar formatu.",
+ "maps-coordinates-par-format": "El formatu destín de les coordenaes.",
+ "maps-coordinates-par-directional": "Indica si les coordenaes tienen de sacase con formatu direicional o non.",
+ "maps-par-scrollwheelzoom": "Indica si tien d'activase o non la ampliación cola rueda'l mur.",
+ "maps-distance-par-distance": "La distancia a convertir a la so equivalente nuna unidá especificada.",
+ "maps-distance-par-decimals": "El númberu máximu de díxitos fraccionarios a usar nel valor resultante.",
+ "maps-distance-par-unit": "La unidá na que sacar la distancia.",
+ "maps-finddestination-par-location": "El llugar d'aniciu.",
+ "maps-finddestination-par-bearing": "La orientación d'aniciu.",
+ "maps-finddestination-par-distance": "La distancia a percorrer.",
+ "maps-finddestination-par-format": "El formatu nel qu'amosar el destín.",
+ "maps-finddestination-par-directional": "Indica si'l formatu de destín tien de ser direicional o non.",
+ "maps-geocode-par-location": "La direición que quies xeocodificar.",
+ "maps-geocode-par-format": "El formatu pa les coordenaes resultantes.",
+ "maps-geocode-par-directional": "Indica si les coordenaes tienen de sacase con formatu direicional o non.",
+ "maps-geodistance-par-location1": "El primer puntu del conxuntu pa calcular la distancia.",
+ "maps-geodistance-par-location2": "El segundu puntu del conxuntu pa calcular la distancia.",
+ "maps-geodistance-par-unit": "La unidá na que sacar la distancia.",
+ "maps-geodistance-par-decimals": "El númberu máximu de díxitos fraccionarios a usar nel valor resultante.",
+ "maps-displaymap-par-mappingservice": "Permite configurar el serviciu de mapes que s'usará pa xenerar el mapa.",
+ "maps-displaymap-par-coordinates": "El llugar onde apaecerá'l centru'l mapa nel aniciu.",
+ "maps-displaymap-par-visitedicon": "El nome de ficheru d'una imaxe a usar pa los iconos de marcador dempués de facer clic nos marcadores orixinales",
+ "maps-displaymap-par-zoom": "Permite configurar el nivel de zoom do mapa.\nCuando nun se da y hai múltiples marcadores presentes nel mapa, se tomará'l zoom que meyor axuste, non el valor configurable predetermináu.",
+ "maps-displaymap-par-centre": "Permite configurar les coordenaes del centru do mapa pa display_point(s).\nAlmite tanto direiciones como coordenaes.\nCuando nun se da esta propiedá, el mapa centraráse nel marcador proporcionáu, o entre los marcadores proporcionaos.",
+ "maps-displaymap-par-title": "Permite configurar el testu qu'apaecerá nes ventanes emerxentes de tolos marcadores que nun tengan un títulu propiu.\nCuando s'usa xunto cola etiqueta, el títulu tará en negrina y sorrayáu.",
+ "maps-displaymap-par-label": "Permite configurar el testu qu'apaecerá nes ventanes emerxentes de tolos marcadores que nun tengan un títulu propiu.",
+ "maps-displaymap-par-icon": "Permite configurar l'iconu qu'usen tolos marcadores.",
+ "maps-displaymap-par-circles": "Círculos a amosar",
+ "maps-displaymap-par-copycoords": "Amosar un diálogu del que pueden copiase les coordenaes al facer clic nun llugar",
+ "maps-displaymap-par-lines": "Llinies a amosar",
+ "maps-displaymap-par-maxzoom": "El nivel máximu d'ampliación",
+ "maps-displaymap-par-minzoom": "El nivel mínimu d'ampliación",
+ "maps-displaymap-par-polygons": "Polígonos a amosar",
+ "maps-displaymap-par-rectangles": "Rectángulos a amosar",
+ "maps-displaymap-par-static": "Facer el mapa estáticu",
+ "maps-displaymap-par-wmsoverlay": "Usar una superposición WMS",
+ "maps-displaymap-par-geojson": "URL d'un ficheru o nome de la páxina que contien datos GeoJSON",
+ "maps-fullscreen-button": "Conmutar pantalla completa",
+ "maps-fullscreen-button-tooltip": "Ver el mapa como pantalla completa o incrustáu.",
+ "validation-error-invalid-location": "El parámetru $1 tien de ser un llugar válidu.",
+ "validation-error-invalid-locations": "El parámetru $1 tien de ser un o más llugares válidos.",
+ "validation-error-invalid-width": "El parámetru $1 tien de ser un anchor válidu.",
+ "validation-error-invalid-height": "El parámetru $1 tien de ser un altor válidu.",
+ "validation-error-invalid-distance": "El parámetru $1 tien de ser una distancia válida.",
+ "validation-error-invalid-distances": "El parámetru $1 tien de ser una o más distancies válides.",
+ "validation-error-invalid-image": "El parámetru $1 tien de ser una imaxe válida.",
+ "validation-error-invalid-images": "El parámetru $1 tien de ser una o más imaxes válides.",
+ "validation-error-invalid-goverlay": "El parámetru $1 tien de ser una superposición válida.",
+ "validation-error-invalid-goverlays": "El parámetru $1 tien de ser una o más superposiciones válides.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "O",
+ "maps-latitude": "Llatitú:",
+ "maps-longitude": "Llonxitú:",
+ "maps-invalid-coordinates": "El valor $1 nun se reconoció como un conxuntu de coordenaes válidu.",
+ "maps_coordinates_missing": "Nun se proporcionaron coordenaes pal mapa.",
+ "maps_geocoding_failed": "{{PLURAL:$2|La siguiente direición nun se pudo|Les siguientes direiciones nun se pudieron}} xeocodificar: $1.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|La siguiente direición nun se pudo|Les siguientes direiciones nun se pudieron}} xeocodificar y {{PLURAL:$2|s'omitió|s'omitieron}} nel mapa: $1",
+ "maps_unrecognized_coords": "Nun se {{PLURAL:$2|reconoció la siguiente coordenada|reconocieron les siguientes coordenaes}}: $1.",
+ "maps_unrecognized_coords_for": "Nun se {{PLURAL:$2|reconoció la siguiente coordenada|reconocieron les siguientes coordenaes}} y {{PLURAL:$2|s'omitió|s'omitieron}} nel mapa: $1",
+ "maps_map_cannot_be_displayed": "Nun puede amosase'l mapa.",
+ "maps-geocoder-not-available": "La carauterística de xeocodificación de mapes nun ta disponible. Nun se pue xeocodificar la to situación.",
+ "maps_leaflet": "Leaflet",
+ "maps-leaflet-par-defzoom": "Permite configurar el nivel predetermináu d'ampliación del mapa.",
+ "maps-leaflet-par-layers": "La capa que va apaecer cuando se cargue'l mapa.",
+ "maps-leaflet-par-overlaylayers": "Les capas sobrepuestes qu'apaecerán cuando se cargue'l mapa.",
+ "maps-leaflet-par-maxclusterradius": "El radiu máximu que cubrirá un grupu dende'l marcador central (en pixels).",
+ "maps-leaflet-par-clusterspiderfy": "Cuando faes click nun grupu al mínimu d'ampliación, espardémoslu pa que puedan vese los marcadores.",
+ "maps_click_to_activate": "Calca p'activar el mapa",
+ "maps_centred_on": "Mapa centráu en $1, $2.",
+ "maps-par-mappingservice": "Permite configurar el serviciu de mapes que s'usará pa xenerar el mapa.",
+ "maps-par-resizable": "Permite redimensionar el mapa arrastrando de la esquina inferior drecha.",
+ "maps-par-searchmarkers": "Permite buscar marcadores específicos con un campu incrustáu nel mapa.",
+ "maps-par-zoom": "Nivel de zoom del mapa. Para los mapes con marcadores el valor predetermináu sedrá el nivel máximu d'ampliación qu'amuese tolos marcadores.",
+ "maps-par-width": "Permite configurar l'anchor del mapa. De mou predetermináu s'asume el pixel como unidá, pero se pue conseñar esplícitamente una d'estes unidaes: px, ex, em, %.",
+ "maps-par-height": "Permite configurar l'altor del mapa. De mou predetermináu s'asume el pixel como unidá, pero se pue conseñar esplícitamente una d'estes unidaes: px, ex, em, %.",
+ "maps-par-centre": "El llugar nel que se tien de centrar el mapa",
+ "maps-par-enable-fullscreen": "Activar el botón de pantalla completa",
+ "maps-par-kml": "Ficheros KML a cargar nel mapa.",
+ "maps-par-markercluster": "Permite fusionar múltiples marcadores cercanos nun solu marcador",
+ "maps-googlemaps3-incompatbrowser": "El to navegador nun ye compatible con Google Maps v3.",
+ "maps-googlemaps3-par-imageoverlays": "Permite amestar una imaxe p'amosala nel llugar especificáu del mapa.",
+ "maps-googlemaps3-par-type": "El tipu de mapa a amosar inicialmente.",
+ "maps-googlemaps3-par-types": "Los tipos de mapa que tarán disponibles al traviés del control de tipu.",
+ "maps-googlemaps3-par-layers": "Capes especiales a cargar nel mapa.",
+ "maps-googlemaps3-par-controls": "Controles a poner nel mapa.",
+ "maps-googlemaps3-par-zoomstyle": "El estilu del control de zoom.",
+ "maps-googlemaps3-par-typestyle": "El estilo del control de tipu.",
+ "maps-googlemaps3-par-autoinfowindows": "Abrir automáticamente toles ventanes d'información dempués de que se cargue la páxina.",
+ "maps-googlemaps3-par-gkml": "Ficheros KML agospiaos por Google a cargar nel mapa.",
+ "maps-googlemaps3-par-kmlrezoom": "Axustar el nivel de zoom dempués de que carguen les capes KML.",
+ "maps-googlemaps3-par-poi": "Amosar puntos d'interés.",
+ "maps-googlemaps3-par-clustergridsize": "La midida de rexa d'un grupu en píxeles.",
+ "maps-par-clustermaxzoom": "El nivel máximu d'ampliación nel que pueden esistir grupos.",
+ "maps-par-clusterzoomonclick": "Si'l comportamientu predetermináu al facer click nun grupu ye amplialu.",
+ "maps-par-maxclusterradius": "El radiu máximu que cubrirá un cluster.",
+ "maps-googlemaps3-par-clusteraveragecenter": "Si'l centru de cada grupu tien de ser la media de tolos marcadores del grupu.",
+ "maps-googlemaps3-par-clusterminsize": "El númberu mínimu de marcadores que tán nun grupu primero que se despinten los marcadores y s'amuese un recuentu.",
+ "mapeditor": "Editor de mapes",
+ "specialpages-group-maps": "Mapes",
+ "mapeditor-parser-error": "Hebo un error al analizar los metadatos. Inorando la entrada d'usuariu.",
+ "mapeditor-none-text": "Dengún",
+ "mapeditor-done-button": "Fecho",
+ "mapeditor-remove-button": "Desaniciar",
+ "mapeditor-import-button2": "Importar",
+ "mapeditor-export-button": "Esportar a códigu wiki",
+ "mapeditor-import-button": "Importar dende códigu wiki",
+ "mapeditor-select-button": "Seleicionar esti polígonu",
+ "mapeditor-mapparam-button": "Editar los parámetros del mapa",
+ "mapeditor-clear-button": "Llimpiar el mapa",
+ "mapeditor-code-title": "Códigu wiki",
+ "mapeditor-import-title": "Importar códigu wiki",
+ "mapeditor-import-note": "Ten en cuenta que'l analizador espera un formatu de códigu wiki mui estrictu. El códigu escritu equí debe coincidir col códigu producíu pola funcionalidá d'esportación.",
+ "mapeditor-form-title": "Editar detalles",
+ "mapeditor-link-title-switcher-popup-text": "Ventana emerxente con testu",
+ "mapeditor-link-title-switcher-link-text": "Enllaz",
+ "mapeditor-form-field-title": "Títulu",
+ "mapeditor-form-field-text": "Testu",
+ "mapeditor-form-field-link": "Enllaz",
+ "mapeditor-form-field-icon": "Iconu",
+ "mapeditor-form-field-group": "Grupu",
+ "mapeditor-form-field-inlinelabel": "Etiqueta en llinia",
+ "mapeditor-form-field-strokecolor": "Color de trazu",
+ "mapeditor-form-field-strokeopacity": "Opacidá del trazu",
+ "mapeditor-form-field-strokeweight": "Grosor de trazu",
+ "mapeditor-form-field-fillcolor": "Color de rellenu",
+ "mapeditor-form-field-fillopcaity": "Opacidá del rellenu",
+ "mapeditor-form-field-showonhover": "Amosar sólo al pasar el mur por encima",
+ "mapeditor-mapparam-title": "Editar los parámetros del mapa",
+ "mapeditor-mapparam-defoption": "-Seleicionar parámetru-",
+ "mapeditor-imageoverlay-button": "Amestar superposición d'imaxe",
+ "mapeditor-form-field-image": "Imaxe",
+ "mapeditor-imageoverlay-title": "Detalles de superposición d'imaxe",
+ "mapeditor-form-field-visitedicon": "Iconu visitáu",
+ "semanticmaps-unrecognizeddistance": "El valor $1 nun ye una distancia válida.",
+ "semanticmaps-kml-link": "Ver el ficheru KML",
+ "semanticmaps-default-kml-pagelink": "Ver la páxina \"$1\"",
+ "semanticmaps-latitude": "Llatitú: $1",
+ "semanticmaps-longitude": "Llonxitú: $1",
+ "semanticmaps-altitude": "Altitú: $1",
+ "semanticmaps-forminput-locations": "Llugares",
+ "semanticmaps-par-staticlocations": "Llista de llugares p'amestar al mapa xunto colos datos consultaos. Como con display_points, pues amestar un títulu, una descripción y un iconu pa cada llugar usando'l signu \"~\" como separador.",
+ "semanticmaps-par-showtitle": "Amosar o non un títulu na ventana d'información del marcador. De vezu, desactivalo ye útil cuando s'utiliza una plantía pa dar formatu al conteníu de la ventana d'información.",
+ "semanticmaps-par-hidenamespace": "Amosar el títulu del espaciu de nomes na ventana d'información del marcador.",
+ "semanticmaps-par-centre": "El centru del mapa. Cuando nun se proporciona, el mapa escoyerá automáticamente'l meyor centru p'amosar tolos marcadores del mapa.",
+ "semanticmaps-par-template": "Una plantía que s'utiliza pa dar formatu al conteníu de la ventana d'información.",
+ "semanticmaps-par-geocodecontrol": "Amosar el control de xeocodificación.",
+ "semanticmaps-par-activeicon": "Iconu a amosar en llugar del marcador predetermináu, cuando la páxina activa ye igual al resultáu de la consulta",
+ "semanticmaps-par-pagelabel": "Cuando se pon a \"si\", tolos marcadores tendrán una \"inlineLabel\" con un enllaz a la páxina que contien les coordenaes del marcador",
+ "semanticmaps-par-ajaxcoordproperty": "Nome de la propiedá de coordenaes que s'usa pa construir la consulta ajax.",
+ "semanticmaps-par-ajaxquery": "Una segunda consulta que s'unvia con ajax para recuperar otres coordenaes más.",
+ "semanticmaps-par-userparam": "Un valor pasáu en cada llamada de plantía,si s'usa una plantía",
+ "semanticmaps-kml-text": "El testu asociáu con cada páxina. Sustituyíu poles otres propiedaes consultaes, si esisten.",
+ "semanticmaps-kml-title": "El títulu predetermináu pa los resultaos",
+ "semanticmaps-kml-linkabsolute": "Si los títulos tienen de ser absolutos (lo contrario de relativos)",
+ "semanticmaps-kml-pagelinktext": "El testu a usar pa los enllaces a la páxina, onde \"$1\" se sustituye pol títulu de la páxina",
+ "semanticmaps-shapes-improperformat": "Formatu incorreutu de $1. Por favor, consulta la documentación sobre formatos",
+ "semanticmaps-shapes-missingshape": "Nun s'alcontraron formes pa $1. Por favor, consulta la documentación de les formes disponibles",
+ "validator-type-mapscircle": "Círculu xeográficu",
+ "validator-type-mapscircle-list": "Llista de círculos",
+ "validator-type-mapsimageoverlay": "Superposición d'imaxen",
+ "validator-type-mapsimageoverlay-list": "Lista de superposiciones d'imaxen",
+ "validator-type-mapsline": "Llinia xeográfica",
+ "validator-type-mapsline-list": "Llista de llinies",
+ "validator-type-mapslocation": "Allugamientu xeográficu",
+ "validator-type-mapslocation-list": "Llista d'allugamientos",
+ "validator-type-mapsrectangle": "Rectángulu xeográficu",
+ "validator-type-mapsrectangle-list": "Llista de rectángulos",
+ "validator-type-mapspolygon": "Polígonu xeográficu",
+ "validator-type-mapspolygon-list": "Llista de polígonos xeográficos",
+ "validator-type-wmsoverlay": "Superposición de Serviciu de Mapes web",
+ "validator-type-jsonfile": "testu"
+}
diff --git a/www/wiki/extensions/Maps/i18n/az.json b/www/wiki/extensions/Maps/i18n/az.json
new file mode 100644
index 00000000..5c8778f2
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/az.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cekli829"
+ ]
+ },
+ "maps_map": "Xəritə",
+ "maps-loading-map": "Xəritə yüklənir...",
+ "maps-layer-errors": "Xətalar",
+ "maps-abb-north": "Şm",
+ "maps-abb-east": "C",
+ "maps-abb-west": "Q"
+}
diff --git a/www/wiki/extensions/Maps/i18n/ba.json b/www/wiki/extensions/Maps/i18n/ba.json
new file mode 100644
index 00000000..9d6bed5f
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ba.json
@@ -0,0 +1,15 @@
+{
+ "@metadata": {
+ "authors": [
+ "Haqmar",
+ "Вильданова Гюзель"
+ ]
+ },
+ "maps-leaflet-par-defzoom": "Карталағы күҙәтеүҙе артырыуҙың кимәлен көйләргә мөмкинлек бирә",
+ "mapeditor-form-title": "Үҙгәртеү мәғлүмәттәре",
+ "mapeditor-link-title-switcher-link-text": "Һылтанма",
+ "mapeditor-form-field-title": "Исем",
+ "mapeditor-form-field-text": "Текст",
+ "mapeditor-form-field-link": "Һылтанма",
+ "mapeditor-form-field-group": "Төркөм"
+}
diff --git a/www/wiki/extensions/Maps/i18n/bar.json b/www/wiki/extensions/Maps/i18n/bar.json
new file mode 100644
index 00000000..d56a8d85
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/bar.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Mucalexx"
+ ]
+ },
+ "maps-loading-map": "Korten werd gloon ..."
+}
diff --git a/www/wiki/extensions/Maps/i18n/be-tarask.json b/www/wiki/extensions/Maps/i18n/be-tarask.json
new file mode 100644
index 00000000..58b98b66
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/be-tarask.json
@@ -0,0 +1,153 @@
+{
+ "@metadata": {
+ "authors": [
+ "EugeneZelenko",
+ "Jim-by",
+ "Wizardist",
+ "Red Winged Duck",
+ "Macofe",
+ "Renessaince"
+ ]
+ },
+ "maps-desc": "Забясьпечвае магчымасьць убудаваньня дынамічных мапаў у вікі-старонкі, геаграфічнага кадаваньня адрасоў і іншыя геаграфічныя апэрацыі",
+ "right-geocode": "геаграфічнае кадаваньне",
+ "maps_map": "Мапа",
+ "maps-loading-map": "Загрузка мапы…",
+ "maps-load-failed": "Немагчыма загрузіць мапу!",
+ "maps-markers": "Пазнакі",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "Фільтраваць маркеры",
+ "maps-others": "іншыя",
+ "maps-ns-layer": "Слой",
+ "maps-ns-layer-talk": "Абмеркаваньне слоя",
+ "maps-layer-property": "Уласьцівасьць",
+ "maps-layer-value": "Значэньне",
+ "maps-layer-errors": "Памылкі",
+ "maps-error-invalid-layertype": "Няма слаёў тыпу «$1». Падтрымліваецца толькі {{PLURAL:$3|1=гэты тып|гэтыя тыпы}}: $2",
+ "maps-error-no-layertype": "Вам неабходна вызначыць тып слою. {{PLURAL:$2|1=Падтрымліваецца толькі гэты тып|Падтрымліваюцца толькі гэтыя тыпы}}: $1",
+ "validation-error-invalid-layer": "Парамэтар $1 мусіць быць слушным слоем.",
+ "validation-error-invalid-layers": "Парамэтар $1 мусіць быць адным ці болей слушнымі слаямі.",
+ "maps-layer-of-type": "Слой тыпу $1",
+ "maps-layer-type-supported-by": "Гэты тып слою можа быць выкарыстаны толькі з {{PLURAL:$2|1=сэрвісам мапаў $1|сэрвісамі мапаў: $1}}.",
+ "maps-coordinates-description": "Перахопнік парсэру для фарматаваньня каардынатаў, з і ў любыя фарматы, якія падтрымліваюцца.",
+ "maps-displaymap-description": "Паказвае геаграфічныя мапы без аніякіх вікі-пазнакаў на іх.",
+ "maps-distance-description": "Канвэртуе адлегласьць выкарыстоўваючы адзінкі, якія падтрымліваюцца, ў іх эквівалент, выкарыстоўваючы іншыя адзінкі.",
+ "maps-finddestination-description": "Знаходзіць пункт прызначэньня з пададзенага пачатковага пункту (можа быць у любым фармаце, якія падтрымліваюцца), пачатковага напрамку і адлегласьці.",
+ "maps-geocode-description": "Уключае геаграфічную кадыроўку адрасу, іншымі словамі, пераўтварае чытальнае для чалавека знаходжаньне ў набор каардынатаў. Тут ёсьць падтрымка для некалькіх сэрвісаў геаграфічнай кадыроўкі, якія не павінны быць блытаныя з сэрвісамі мапаў.",
+ "maps-geodistance-description": "Падлічвае геаграфічную адлегласьць паміж двума пунктамі, з і для любога з фарматаў, якія падтрымліваюцца.",
+ "maps-mapsdoc-description": "Паказвае табліцу з парамэтрамі для пазначанага сэрвісу стварэньня мапаў разам з яго значэньнямі па змоўчваньні і апісаньнямі.",
+ "maps-mapsdoc-par-service": "Сэрвіс стварэньня мапаў, для якога паказваць дакумэнтацыю пра парамэтры.",
+ "maps-mapsdoc-par-language": "Мова, на якой паказваць дакумэнтацыю. Калі перакладу на такую мову няма, замест яе будзе выкарыстоўвацца ангельская.",
+ "maps-coordinates-par-location": "Каардынаты, якія Вы жадаеце фарматаваць.",
+ "maps-coordinates-par-format": "Мэтавы фармат для каардынатаў.",
+ "maps-coordinates-par-directional": "Паказвае, ці павінны каардынаты быць выведзены непасрэдна ці не.",
+ "maps-distance-par-distance": "Адлегласьць для канвэртаваньня ў яе эквівалент у пададзеных адзінках.",
+ "maps-distance-par-decimals": "Максымальная колькасьць лічбаў пасьля дзесятковай коскі для выкарыстаньня ў выніковым значэньні.",
+ "maps-distance-par-unit": "Адзінкі, у якіх будзе выведзеная адлегласьць.",
+ "maps-finddestination-par-location": "Пачатковае месцазнаходжаньне.",
+ "maps-finddestination-par-bearing": "Пачатковы накірунак.",
+ "maps-finddestination-par-distance": "Адлегласьць для падарожжа.",
+ "maps-finddestination-par-format": "Фармат, у якім выводзіць пункт прызначэньня.",
+ "maps-finddestination-par-directional": "Паказвае, калі фармат пункту прызначэньня павінен быць накіраваны ці не.",
+ "maps-geocode-par-location": "Адрас, які Вы жадаеце геаграфічна кадыраваць.",
+ "maps-geocode-par-format": "Фармат для выніковых каардынатаў.",
+ "maps-geocode-par-directional": "Паказвае, ці павінны каардынаты быць выведзены непасрэдна ці не.",
+ "maps-geodistance-par-location1": "Першы пункт для вылічэньня адлегласьці.",
+ "maps-geodistance-par-location2": "Другі пункт для вылічэньня адлегласьці.",
+ "maps-geodistance-par-unit": "Адзінкі, у якіх будзе выведзеная адлегласьць.",
+ "maps-geodistance-par-decimals": "Максымальная колькасьць лічбаў пасьля дзесятковай коскі для выкарыстаньня ў выніковым значэньні.",
+ "maps-displaymap-par-mappingservice": "Дазваляе ўстанаўліваць сэрвіс мапаў, які будзе выкарыстоўвацца для стварэньня мапы.",
+ "maps-displaymap-par-coordinates": "Месцазнаходжаньне, на якім мапа будзе цэнтравацца.",
+ "maps-displaymap-par-zoom": "Дазваляе зьмяняць павелічэньне мапы.\nКалі гэта немагчыма, ці на мапе ёсьць некалькі маркераў, замест пазначанага ў наладах будзе выбранае найбольш слушнае павелічэньне.",
+ "maps-displaymap-par-centre": "Дазваляе ўстанаўліваць каардынаты цэнтру мапы для display_point(s).\nПрымаюцца адрасы і каардынаты.\nКалі гэтая ўласьцівасьць не пададзеная, мапа будзе цэнтравацца на пададзеным маркеры ці паміж пададзенымі маркерамі.",
+ "maps-displaymap-par-title": "Дазваляе ўстанаўліваць тэкст, які будзе паказаны ў разгортваемым тэксьце тых маркераў, якія ня маюць асобных назваў.\nКалі выкарыстоўваецца з меткамі, назва будзе паказаная тлустым тэкстам і падкрэсьленая.",
+ "maps-displaymap-par-label": "Дазваляе ўстанаўліваць тэкст, які будзе паказаны ў разгортваемым тэксьце тых маркераў, якія ня маюць асобных метак.",
+ "maps-displaymap-par-icon": "Дазваляе ўстанаўліваць выявы для ўсіх маркераў.",
+ "validation-error-invalid-location": "Парамэтар $1 мусіць быць слушным знаходжаньнем.",
+ "validation-error-invalid-locations": "Парамэтар $1 мусіць быць адным ці болей слушнымі знаходжаньнямі.",
+ "validation-error-invalid-width": "Парамэтар $1 мусіць быць слушнай шырынёй.",
+ "validation-error-invalid-height": "Парамэтар $1 мусіць быць слушнай вышынёй.",
+ "validation-error-invalid-distance": "Парамэтар $1 мусіць быць слушнай адлегласьцю.",
+ "validation-error-invalid-distances": "Парамэтар $1 мусіць быць адной ці болей слушнымі адлегласьцямі.",
+ "validation-error-invalid-image": "Парамэтар $1 мусіць быць слушнай выявай.",
+ "validation-error-invalid-images": "Парамэтар $1 мусіць быць адной ці болей слушнымі выявамі.",
+ "validation-error-invalid-goverlay": "Парамэтар $1 мусіць быць слушным слоем.",
+ "validation-error-invalid-goverlays": "Парамэтар $1 мусіць быць адным ці болей слушнымі слаямі.",
+ "maps-abb-north": "Пн.",
+ "maps-abb-east": "У.",
+ "maps-abb-south": "Пд.",
+ "maps-abb-west": "З.",
+ "maps-latitude": "Шырата:",
+ "maps-longitude": "Даўгата:",
+ "maps-invalid-coordinates": "Значэньне $1 зьяўляецца недапушчальным наборам каардынатаў.",
+ "maps_coordinates_missing": "Каардынаты для мапы не пазначаныя.",
+ "maps_geocoding_failed": "{{PLURAL:$2|1=Наступны адрас ня можа быць геакадаваны|Наступныя адрасы ня могуць быць геакадаваныя}}: $1.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|1=Наступны адрас ня можа быць геакадаваны і быў выдалены|Наступныя адрасы ня могуць быць геакадаваныя і былі выдаленыя}} з мапы:\n$1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|1=Наступная каардыната не была распазнаная|Наступныя каардынаты не былі распазнаныя}}: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|1=Наступная каардыната не была распазнаная|Наступныя каардынаты не былі распазнаныя}} і {{PLURAL:$2|1=яна не паказаная|яны не паказаныя}} на мапе:\n$1",
+ "maps_map_cannot_be_displayed": "Мапа ня можа быць паказаная.",
+ "maps-geocoder-not-available": "Магчымасьць геаграфічнага кадаваньня для мапаў недаступная. Вашае месцазнаходжаньне ня можа быць геаграфічна закадаванае.",
+ "maps_click_to_activate": "Націсьніце для актывацыі мапы",
+ "maps_centred_on": "Цэнтар мапы — $1, $2.",
+ "maps-par-resizable": "Дазваляе зьмяняць памеры мапы, перацягваючы яе ніжні правы кут.",
+ "maps-par-zoom": "Маштаб мапы. Для мапаў з пазначэньнямі маштаб будзе такім, пры якім яшчэ будуць паказвацца ўсе пазначэньні.",
+ "maps-par-width": "Дазваляе наладжваць шырыню мапы. Па змоўчваньні піксэлі выкарыстоўваюцца як адзінкі вымярэньня, але Вы можаце непасрэдна вызначыць адну з гэтых адзінак вымярэньня: px, ex, em, %.",
+ "maps-par-height": "Дазваляе наладжваць вышыню мапы. Па змоўчваньні піксэлі выкарыстоўваюцца як адзінкі вымярэньня, але Вы можаце непасрэдна вызначыць адну з гэтых адзінак вымярэньня: px, ex, em, %.",
+ "maps-par-kml": "KML-файлы для загрузкі ў мапу.",
+ "maps-googlemaps3-incompatbrowser": "Ваш браўзэр не сумяшчальны з Google Maps v3.",
+ "maps-googlemaps3-par-type": "Тып мапы, які будзе паказвацца ў пачатку.",
+ "maps-googlemaps3-par-types": "Тыпы мапаў, якія будуць даступныя праз элемэнт кіраваньня тыпамі.",
+ "maps-googlemaps3-par-layers": "Спэцыяльныя слаі для загрузкі ў мапу.",
+ "maps-googlemaps3-par-controls": "Элемэнты кіраваньня, якія будуць разьмешчаныя на мапе.",
+ "maps-googlemaps3-par-zoomstyle": "Стыль элемэнта кіраваньня маштабам.",
+ "maps-googlemaps3-par-typestyle": "Стыль элемэнта кіраваньня тыпамі.",
+ "maps-googlemaps3-par-autoinfowindows": "Аўтаматычна адкрываць ўсе інфармацыйныя вокны, пасьля таго, як старонка была загружаная.",
+ "maps-googlemaps3-par-gkml": "Файлы KML разьмешчаныя на Google для загрузкі на мапу.",
+ "maps-googlemaps3-par-kmlrezoom": "Зьмяняць маштаб мапы пасьля загрузкі KML-слаёў.",
+ "maps-googlemaps3-par-poi": "Паказаць выбітныя мясьціны.",
+ "mapeditor": "Рэдактар мап",
+ "specialpages-group-maps": "Мапы",
+ "mapeditor-parser-error": "У час разбору мэтазьвестак адбылася памылка. Ігнаруем уведзеныя зьвесткі.",
+ "mapeditor-none-text": "Нічога",
+ "mapeditor-done-button": "Зроблена",
+ "mapeditor-remove-button": "Выдаліць",
+ "mapeditor-import-button2": "Імпартаваць",
+ "mapeditor-export-button": "Экспартаваць у вікі-код",
+ "mapeditor-import-button": "Імпартаваць зь вікі-коду",
+ "mapeditor-select-button": "Выбраць гэты шматкутнік",
+ "mapeditor-mapparam-button": "Зьмяніць парамэтры мапы",
+ "mapeditor-clear-button": "Ачысьціць мапу",
+ "mapeditor-code-title": "Вікі-код",
+ "mapeditor-import-title": "Імпарт вікі-коду",
+ "mapeditor-import-note": "Заўважце, калі ласка, што парсэр апрацоўвае толькі карэктны вікі-код. Імпартаваны код на ўваходзе мусіць супадаць з экспартаваным кодам.",
+ "mapeditor-form-title": "Рэдагаваньне падрабязнасьцяў",
+ "mapeditor-link-title-switcher-popup-text": "Усплываючая падказка",
+ "mapeditor-link-title-switcher-link-text": "Спасылка",
+ "mapeditor-form-field-title": "Назва",
+ "mapeditor-form-field-text": "Тэкст",
+ "mapeditor-form-field-link": "Спасылка",
+ "mapeditor-form-field-icon": "Ікона",
+ "mapeditor-form-field-group": "Група",
+ "mapeditor-form-field-inlinelabel": "Тэкставая метка",
+ "mapeditor-form-field-strokecolor": "Колер контуру",
+ "mapeditor-form-field-strokeopacity": "Празрыстасьць контуру",
+ "mapeditor-form-field-strokeweight": "Таўшчыня контуру",
+ "mapeditor-form-field-fillcolor": "Колер зафарбоўкі",
+ "mapeditor-form-field-fillopcaity": "Празрыстасьць зафарбоўкі",
+ "mapeditor-form-field-showonhover": "Паказваць толькі пры навядзеньні",
+ "mapeditor-mapparam-title": "Зьмена парамэтраў мапы",
+ "mapeditor-mapparam-defoption": "-Выберыце парамэтар-",
+ "mapeditor-imageoverlay-button": "Накласьці выяву",
+ "mapeditor-form-field-image": "Выява",
+ "mapeditor-imageoverlay-title": "Накладаньне выявы",
+ "mapeditor-form-field-visitedicon": "Значак для наведанага",
+ "semanticmaps-unrecognizeddistance": "Значэньне $1 — няслушная адлегласьць.",
+ "semanticmaps-kml-link": "Паказаць KML-файл",
+ "semanticmaps-default-kml-pagelink": "Паказаць старонку $1",
+ "semanticmaps-forminput-locations": "Месцы",
+ "semanticmaps-par-staticlocations": "Сьпіс месцазнаходжаньняў для даданьня на мапу разам з запытанымі зьвесткамі. Напрыклад, разам з «display_points», Вы можаце дадаць назву, апісаньне і мініятуру для месцазнаходжаньня з дапамогай сымбаля «~» у якасьці разьдзяляльніка.",
+ "semanticmaps-par-showtitle": "Паказваць назву ў акне інфармацыі пра маркер ці не. Адключэньне гэтай функцыі часта карыснае падчас выкарыстаньня шаблёну для фарматаваньня зьместу акна інфармацыі.",
+ "semanticmaps-par-centre": "Цэнтар мапы. Калі ён не пададзены, мапа будзе аўтаматычна выбіраць аптымальны цэнтар для паказу ўсіх маркераў.",
+ "semanticmaps-par-template": "Шаблён для фарматаваньня зьместу акна інфармацыі.",
+ "semanticmaps-par-geocodecontrol": "Паказаць элемэнты кіраваньня геаграфічным кадаваньнем."
+}
diff --git a/www/wiki/extensions/Maps/i18n/bg.json b/www/wiki/extensions/Maps/i18n/bg.json
new file mode 100644
index 00000000..fdee0bff
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/bg.json
@@ -0,0 +1,33 @@
+{
+ "@metadata": {
+ "authors": [
+ "DCLXVI",
+ "පසිඳු කාවින්ද",
+ "StanProg",
+ "Plamen"
+ ]
+ },
+ "maps_map": "Карта",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-ns-layer": "Слой",
+ "maps-layer-value": "Стойност",
+ "maps-layer-errors": "Грешки",
+ "maps-abb-north": "С",
+ "maps-abb-east": "И",
+ "maps-abb-south": "Ю",
+ "maps-abb-west": "З",
+ "maps-latitude": "Географска ширина:",
+ "maps-longitude": "Географска дължина:",
+ "maps-googlemaps3-incompatbrowser": "Вашият браузър не е съвместим с Google Maps v3.",
+ "mapeditor": "Редактор на карти",
+ "specialpages-group-maps": "Карти",
+ "mapeditor-done-button": "Готово",
+ "mapeditor-remove-button": "Премахване",
+ "mapeditor-import-button2": "Внасяне",
+ "mapeditor-code-title": "Уики код",
+ "mapeditor-link-title-switcher-link-text": "Препратка",
+ "mapeditor-form-field-title": "Заглавие",
+ "mapeditor-form-field-text": "Текст",
+ "mapeditor-form-field-link": "Препратка",
+ "mapeditor-form-field-group": "Група"
+}
diff --git a/www/wiki/extensions/Maps/i18n/bjn.json b/www/wiki/extensions/Maps/i18n/bjn.json
new file mode 100644
index 00000000..4f1a7884
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/bjn.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ezagren"
+ ]
+ },
+ "maps-abb-north": "U",
+ "maps-abb-east": "T",
+ "maps-abb-south": "S",
+ "maps-abb-west": "B"
+}
diff --git a/www/wiki/extensions/Maps/i18n/bn.json b/www/wiki/extensions/Maps/i18n/bn.json
new file mode 100644
index 00000000..891ea3e4
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/bn.json
@@ -0,0 +1,114 @@
+{
+ "@metadata": {
+ "authors": [
+ "Tauhid16",
+ "Wikitanvir",
+ "Aftabuzzaman",
+ "আজিজ",
+ "আফতাবুজ্জামান"
+ ]
+ },
+ "right-geocode": "জিওকোড",
+ "action-geocode": "এই উইকিতে জিওকোডিং করুন",
+ "maps_map": "মানচিত্র",
+ "maps-loading-map": "মানচিত্র লোড করা হচ্ছে...",
+ "maps-load-failed": "মানচিত্র লোড করা সম্ভব হয়নি!",
+ "maps-markers": "চিহ্নিতকারী",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "ছাকনি চিহ্নিতকারী",
+ "maps-others": "অন্যান্য",
+ "maps-kml-parsing-failed": "একটি অথবা আরও বেশি KML ফাইল পদান্বয় ব্যর্থ। সাধারণত এটি আহরণ ব্যর্থতা বা ত্রুটিপূর্ণ XML-এর কারণে ঘটে।",
+ "maps-ns-layer": "স্তর",
+ "maps-ns-layer-talk": "স্তর আলোচনা",
+ "maps-layer-property": "সম্পত্তি",
+ "maps-layer-value": "মান",
+ "maps-layer-errors": "ত্রুটি",
+ "maps-layerdef-invalid": "অবৈধ {{PLURAL:$1|সংজ্ঞা}}",
+ "maps-layerdef-invalid-fatal": "ধ্বংসাত্মক অবৈধ সংজ্ঞা",
+ "maps-layerdef-wrong-namespace": "স্তর সজ্ঞা শুধুমাত্র \"$1\" নামস্থান পাতাসমূহে বৈধ",
+ "maps-layerdef-equal-layer-name": "স্তরের নামসমূহ একই স্তর পৃষ্ঠার মধ্যে অনন্য হওয়া আবশ্যক। \"$1\" ইতিমধ্যে আরেকটি স্তর দ্বারা ব্যবহৃত হয়েছে।",
+ "maps-layerpage-usage": "\"$1\" স্তর ব্যবহৃত মানচিত্র সহ পৃষ্ঠাসমূহ",
+ "maps-layerpage-nousage": "এই মুহূর্তে কোন পৃষ্ঠায় এই স্তরটি ব্যবহার হয়নি।",
+ "maps-error-invalid-layertype": "সেখানে \"$1\" ধরনের কোন স্তর নেই। শুধু {{PLURAL:$3|এই ধরনের}} স্তর সমর্থিত হবে: $2",
+ "maps-error-no-layertype": "আপনাকে স্তরের ধরন নির্দিষ্ট করতে হবে। শুধু {{PLURAL:$2|এই ধরনের}} স্তর সমর্থিত: $1",
+ "validation-error-invalid-layer": "\"$1\" পরামিতি অবশ্যই বৈধ স্তর হতে হবে।",
+ "validation-error-invalid-layers": "\"$1\" পরামিতি অবশ্যই এক বা একাধিক বৈধ স্তর হতে হবে।",
+ "validation-error-no-non-numeric": "\"$1\" পরামিতি অবশ্যই অ-সাংখ্যিক স্ট্রিং হতে হবে।",
+ "validation-error-no-non-numerics": "\"$1\" পরামিতি অবশ্যই এক বা একাধিক অ-সাংখ্যিক স্ট্রিং হতে হবে।",
+ "maps-layer-of-type": "\"$1\" ধরণের লেয়ার",
+ "maps-layer-of-type-and-name": "\"$2\" স্তরটি \"$1\" ধরণের",
+ "maps-par-scrollwheelzoom": "নির্দেশ করে যদি মাউস স্ক্রোলিং সক্রিয় করা উচিত বা উচিত না।",
+ "maps-finddestination-par-location": "প্রারম্ভিক অবস্থান",
+ "maps-finddestination-par-distance": "ভ্রমণ করার ব্যবধান",
+ "maps-displaymap-par-geojson": "একটি GeoJSON ফাইলের URL",
+ "validation-error-invalid-location": "স্থিতিমাপ \"$1\" একটি বৈধ অবস্থান হতে হবে।",
+ "validation-error-invalid-locations": "\"$1\" পরামিতি অবশ্যই এক বা একাধিক বৈধ অবস্থান হতে হবে।",
+ "validation-error-invalid-width": "\"$1\" পরামিতি অবশ্যই বৈধ প্রস্থ বিশিষ্ঠ হতে হবে।",
+ "validation-error-invalid-height": "\"$1\" পরামিতি অবশ্যই বৈধ উচ্চতা বিশিষ্ঠ হতে হবে।",
+ "validation-error-invalid-distance": "\"$1\" পরামিতি অবশ্যই বৈধ দূরত্ব বিশিষ্ঠ হতে হবে।",
+ "validation-error-invalid-distances": "\"$1\" পরামিতি অবশ্যই এক বা একাধিক বৈধ দূরত্ব বিশিষ্ঠ হতে হবে।",
+ "validation-error-invalid-image": "\"$1\" পরামিতি অবশ্যই বৈধ চিত্র যুক্ত হতে হবে।",
+ "validation-error-invalid-images": "\"$1\" পরামিতি অবশ্যই এক বা একাধিক বৈধ চিত্র যুক্ত হতে হবে।",
+ "validation-error-invalid-goverlay": "\"$1\" পরামিতি অবশ্যই বৈধ আচ্ছদন হতে হবে।",
+ "validation-error-invalid-goverlays": "\"$1\" পরামিতি অবশ্যই এক বা একাধিক বৈধ আচ্ছদন হতে হবে।",
+ "maps-abb-north": "উ",
+ "maps-abb-east": "পূ",
+ "maps-abb-south": "দ",
+ "maps-abb-west": "প",
+ "maps-latitude": "অক্ষাংশ:",
+ "maps-longitude": "দ্রাঘিমাংশ:",
+ "maps_coordinates_missing": "মানচিত্রের জন্য স্থানাঙ্ক প্রদান করা হয়নি।",
+ "maps_map_cannot_be_displayed": "এই মানচিত্রটি প্রদর্শন করা সম্ভব নয়।",
+ "maps-geocoder-not-available": "মানচিত্রে জিওকোডিং বৈশিষ্টটি উপলব্ধ নয়। আপনার অবস্থান জিওকোডেড করা যাবে না।",
+ "maps_leaflet": "প্রচারপত্র",
+ "maps_click_to_activate": "মানচিত্র চালু করতে ক্লিক করুন",
+ "maps_centred_on": "মানচিত্র $1, $2-এ কেন্দ্রীভূত।",
+ "maps-googlemaps3-par-poi": "আগ্রহের বিষয়সমূহ দেখাও।",
+ "mapeditor": "মানচিত্র সম্পাদক",
+ "specialpages-group-maps": "মানচিত্র",
+ "mapeditor-parser-error": "মেটাডেটা বিশ্লেষণ করার সময় একটি ত্রুটি ঘটেছে। ব্যবহারকারীর ইনপুট উপেক্ষা করা হয়েছে।",
+ "mapeditor-none-text": "কিছু নয়",
+ "mapeditor-done-button": "সম্পন্ন",
+ "mapeditor-remove-button": "সরান",
+ "mapeditor-import-button2": "আমদানি",
+ "mapeditor-export-button": "উইকি কোডে রপ্তানি করুন",
+ "mapeditor-import-button": "উইকি কোড থেকে আমদানি করুন",
+ "mapeditor-select-button": "এই বহুভুজ নির্বাচন করুন",
+ "mapeditor-mapparam-button": "মানচিত্রের পরামিতিগুলো সম্পাদনা করুন",
+ "mapeditor-clear-button": "মানচিত্র সাফ করুন",
+ "mapeditor-code-title": "উইকি কোড",
+ "mapeditor-import-title": "উইকি কোড আমদানি করুন",
+ "mapeditor-form-title": "তথ্য সংশোধন করুন",
+ "mapeditor-link-title-switcher-link-text": "সংযোগ",
+ "mapeditor-form-field-title": "শিরোনাম",
+ "mapeditor-form-field-text": "লেখা",
+ "mapeditor-form-field-link": "সংযোগ",
+ "mapeditor-form-field-icon": "আইকন",
+ "mapeditor-form-field-group": "দল",
+ "mapeditor-form-field-strokecolor": "রেখার রং",
+ "mapeditor-form-field-strokeopacity": "রেখার অস্পষ্টতা",
+ "mapeditor-form-field-strokeweight": "রেখার প্রস্থ",
+ "mapeditor-mapparam-title": "মানচিত্রের পরামিতিগুলো সম্পাদনা করুন",
+ "mapeditor-mapparam-defoption": "-পরামিতি বাছাই করুন-",
+ "mapeditor-imageoverlay-button": "চিত্র আচ্ছাদন যোগ করুন",
+ "mapeditor-form-field-image": "চিত্র",
+ "mapeditor-imageoverlay-title": "চিত্র আচ্ছদনের খুঁটিনাটি",
+ "semanticmaps-kml-link": "কেএমএল ফাইল দেখাও",
+ "semanticmaps-default-kml-pagelink": "$1 পাতা প্রদর্শন করো",
+ "semanticmaps-latitude": "অক্ষাংশ: $1",
+ "semanticmaps-longitude": "দ্রাঘিমাংশ: $1",
+ "semanticmaps-altitude": "উচ্চতা: $1",
+ "semanticmaps-forminput-locations": "অবস্থান",
+ "validator-type-mapscircle": "ভৌগলিক বৃত্ত",
+ "validator-type-mapscircle-list": "বৃত্তের তালিকা",
+ "validator-type-mapsimageoverlay": "চিত্র প্রতিস্থাপক",
+ "validator-type-mapsimageoverlay-list": "চিত্র প্রতিস্থাপকের তালিকা",
+ "validator-type-mapsline": "ভৌগলিক রেখা",
+ "validator-type-mapsline-list": "রেখার তালিকা",
+ "validator-type-mapslocation": "ভৌগলিক অবস্থান",
+ "validator-type-mapslocation-list": "অবস্থানগুলির তালিকা",
+ "validator-type-mapsrectangle": "ভৌগলিক আয়তক্ষেত্র",
+ "validator-type-mapsrectangle-list": "আয়তক্ষেত্রের তালিকা",
+ "validator-type-mapspolygon": "ভৌগলিক বহুভুজ",
+ "validator-type-mapspolygon-list": "ভৌগলিক বহুভুজের তালিকা"
+}
diff --git a/www/wiki/extensions/Maps/i18n/br.json b/www/wiki/extensions/Maps/i18n/br.json
new file mode 100644
index 00000000..3e172bb1
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/br.json
@@ -0,0 +1,127 @@
+{
+ "@metadata": {
+ "authors": [
+ "Fohanno",
+ "Fulup",
+ "Y-M D",
+ "Macofe"
+ ]
+ },
+ "maps-desc": "Aotren a ra ensoc'hañ kartennoù dinamek er pajennoù wiki, geokodañ chomlec'hioù hag oberiadennoù douaroniel all",
+ "right-geocode": "Geokod",
+ "maps_map": "Kartenn",
+ "maps-loading-map": "O kargañ ar gartenn...",
+ "maps-load-failed": "N'eus ket bet gallet kargañ ar gartenn !",
+ "maps-markers": "Merkerioù",
+ "maps-copycoords-prompt": "CTRL+C, KAS",
+ "maps-searchmarkers-text": "Merkerioù sil",
+ "maps-others": "re all",
+ "maps-ns-layer": "Gwiskad",
+ "maps-ns-layer-talk": "Kaozeadenn ar gwiskad",
+ "maps-layer-property": "Perzh",
+ "maps-layer-value": "Talvoudenn",
+ "maps-layer-errors": "Fazioù",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Termenadur|Termenadurioù}} direizh",
+ "maps-layerpage-usage": "Pajennoù gant kartennoù hag a implij ar gwiskad \"$1\"",
+ "maps-layerpage-nousage": "N'eus pajenn ebet hag a implij ar gwiskad-mañ evit poent.",
+ "maps-error-invalid-layertype": "N'eus ket a wiskad a seurt gant \"$1\". N'eo skoret nemet ar {{PLURAL:$3|seurt-mañ|seurtoù-mañ}} : $2",
+ "maps-error-no-layertype": "Ret eo deoc'h spisaat ar seurt gwiskad. N'eo skoret nemet ar {{PLURAL:$2|seurt-mañ|seurtoù-mañ}} : $1",
+ "validation-error-invalid-layer": "Rankout a ra an arventenn $1 bezañ ur gwiskad reizh.",
+ "validation-error-invalid-layers": "Rankout a ra an arventenn $1 bezañ evit ur gwiskad reizh, pe evit meur a hini.",
+ "maps-layer-of-type": "Gwiskad a seurt $1",
+ "maps-layer-of-type-and-name": "Gwiskad \"$2\" eus ar seurt \"$1\"",
+ "maps-layer-type-supported-by": "N'hall ar seurt gwiskad-mañ {{PLURAL:$2|bezañ implijet nemet gant ar sevij kartennaouiñ $1|bezañ implijet nemet gant ar servijoù kartennaouiñ-mañ : $1}}.",
+ "maps-geodistance-description": "Jediñ an hed douaroniel etre daou bik, adalek ha war-zu ne vern pe furmad skoret.",
+ "maps-coordinates-par-location": "An daveennoù a fell deoc'h furmadiñ.",
+ "maps-finddestination-par-location": "Al lec'hiadur orin.",
+ "maps-finddestination-par-bearing": "An durc'hadur orin.",
+ "maps-finddestination-par-distance": "An hed da vont drezañ.",
+ "maps-geocode-par-location": "Ar chomlec'h da c'heokodiñ.",
+ "maps-geocode-par-format": "Furmad an daveennoù savet da-heul.",
+ "maps-displaymap-par-circles": "Kelc'hioù da ziskwel",
+ "maps-displaymap-par-lines": "Linennoù da ziskwel",
+ "maps-displaymap-par-maxzoom": "Live zoum uhelañ",
+ "maps-displaymap-par-minzoom": "Live zoum izelañ",
+ "maps-displaymap-par-polygons": "Lieskornegoù da ziskwel",
+ "maps-displaymap-par-rectangles": "Hirgarrezennoù da ziskwel",
+ "maps-displaymap-par-static": "Lakaat ar gartenn da vezañ statek",
+ "maps-fullscreen-button": "Gweredekaat ar skramm leun",
+ "validation-error-invalid-location": "Rankout a ra an arventenn $1 bezañ evit ul lec'hiadur reizh.",
+ "validation-error-invalid-locations": "Rankout a ra an arventenn $1 bezañ evit ul lec'hiadur reizh, da nebeutañ.",
+ "validation-error-invalid-width": "Rankout a ra an arventenn $1 bezañ evit ul ledander reizh.",
+ "validation-error-invalid-height": "Rankout a ra an arventenn $1 bezañ evit un uhelder reizh.",
+ "validation-error-invalid-distance": "Rankout a ra an arventenn $1 bezañ evit un hed reizh.",
+ "validation-error-invalid-distances": "Rankout a ra an arventenn $1 bezañ evit un hed reizh, da nebeutañ.",
+ "validation-error-invalid-image": "Rankout a ra an arventenn $1 bezañ ur skeudenn reizh.",
+ "validation-error-invalid-images": "Rankout a ra an arventenn $1 bezañ ur skeudenn reizh, pe meur a hini.",
+ "validation-error-invalid-goverlay": "Rankout a ra an arventenn $1 bezañ evit ur goloadur reizh.",
+ "validation-error-invalid-goverlays": "Rankout a ra an arventenn $1 bezañ evit ur goloadur reizh, da nebeutañ.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "R",
+ "maps-abb-south": "S",
+ "maps-abb-west": "K",
+ "maps-latitude": "Ledred :",
+ "maps-longitude": "Hedred :",
+ "maps-invalid-coordinates": "N'eo ket bet anavezet an dalvoudenn $1 evel ur stroll daveennoù reizh.",
+ "maps_coordinates_missing": "N'eus bet spisaet daveenn ebet evit ar gartenn.",
+ "maps_geocoding_failed": "N'eus ket bet gallet geokodañ ar {{PLURAL:$2|chomlec'h|chomlec'h}} da-heul : $1.\nN'haller ket diskwel ar gartenn.",
+ "maps_geocoding_failed_for": "N'eus ket bet gallet geokodañ ar {{PLURAL:$2|chomlec'h|chomlec'h}} da-heul, setu {{PLURAL:$2|n'eo|n'int}} ket bet lakaet war ar gartenn :\n$1",
+ "maps_unrecognized_coords": "N'eo ket bet anavezet an {{PLURAL:$2|daveenn|daveennoù}} da-heul : $1.",
+ "maps_unrecognized_coords_for": "N'eo ket bet anavezet an {{PLURAL:$2|daveenn|daveennoù}} da-heul ha {{PLURAL:$2|n'eo|n'int}} ket bet lakaet war ar gartenn :\n$1",
+ "maps_map_cannot_be_displayed": "N'hall ket ar gartenn bezañ diskwelet.",
+ "maps-geocoder-not-available": "N'haller ket ober gant arc'hwel geokodañ ar c'hartennoù. N'haller ket geokodañ ho lec'hiadur.",
+ "maps_leaflet": "Plegfollenn",
+ "maps_click_to_activate": "Klikañ evit gweredekaat ar gartenn",
+ "maps_centred_on": "Kartenn kreizet war $1, $2.",
+ "maps-par-enable-fullscreen": "Gweredekaat ar bouton skramm leun",
+ "maps-par-kml": "Restroù KML da gargañ war ar gartenn.",
+ "maps-googlemaps3-incompatbrowser": "N'eo ket kenglotus ho merdeer gant Google Maps v3.",
+ "maps-googlemaps3-par-type": "Ar seurt kartenn da ziskouez da gentañ.",
+ "maps-googlemaps3-par-layers": "Gwiskadoù arbennik da gargañ war ar gartenn.",
+ "maps-googlemaps3-par-controls": "Ar c'hontrolloù da lakaat war ar gartenn.",
+ "maps-googlemaps3-par-zoomstyle": "Stil kontroll ar zoum.",
+ "maps-googlemaps3-par-typestyle": "Stil kontroll ar seurt.",
+ "maps-googlemaps3-par-autoinfowindows": "Digeriñ an holl brenestroù diouzhtu goude bezañ karget ar bajenn.",
+ "maps-googlemaps3-par-poi": "Diskouez al lec'hioù dudius.",
+ "mapeditor": "Aozer kartennoù",
+ "specialpages-group-maps": "Kartennoù",
+ "mapeditor-none-text": "Hini ebet",
+ "mapeditor-done-button": "Graet",
+ "mapeditor-remove-button": "Dilemel",
+ "mapeditor-import-button2": "Enporzhiañ",
+ "mapeditor-select-button": "Diuzañ al liestaleg-mañ",
+ "mapeditor-mapparam-button": "Aozañ arventennoù ar gartenn",
+ "mapeditor-clear-button": "Diverkañ ar gartenn",
+ "mapeditor-code-title": "Kod wiki",
+ "mapeditor-import-title": "Enporzhiañ ar c'hod wiki",
+ "mapeditor-form-title": "Aozañ ar munudoù",
+ "mapeditor-link-title-switcher-link-text": "Liamm",
+ "mapeditor-form-field-title": "Titl",
+ "mapeditor-form-field-text": "Testenn",
+ "mapeditor-form-field-link": "Liamm",
+ "mapeditor-form-field-icon": "Arlun",
+ "mapeditor-form-field-group": "Strollad",
+ "mapeditor-form-field-inlinelabel": "Label enlinenn",
+ "mapeditor-form-field-fillcolor": "Liv leuniañ",
+ "mapeditor-mapparam-title": "Kemmañ arventennoù ar gartenn",
+ "mapeditor-mapparam-defoption": "-Diuzañ un arventenn-",
+ "mapeditor-form-field-image": "Skeudenn",
+ "mapeditor-form-field-visitedicon": "Arlun gweladennet",
+ "semanticmaps-unrecognizeddistance": "An talvoud $1 n'eo ket un hed reizh anezhañ.",
+ "semanticmaps-kml-link": "Gwelet ar restr KML",
+ "semanticmaps-default-kml-pagelink": "Gwelet ar pennad $1",
+ "semanticmaps-latitude": "Ledred : $1",
+ "semanticmaps-longitude": "Hedred : $1",
+ "semanticmaps-altitude": "Uhelder : $1",
+ "semanticmaps-forminput-locations": "Lec'hiadurioù",
+ "semanticmaps-par-template": "Ur patrom d'ober gantañ da furmadiñ boued ar prenestr titouriñ.",
+ "semanticmaps-kml-title": "Titl dre ziouer evit an disoc'hoù",
+ "validator-type-mapscircle": "Kelc'h douaroniel",
+ "validator-type-mapscircle-list": "Roll kelc'hioù",
+ "validator-type-mapsline": "Linenn zouaroniel",
+ "validator-type-mapsline-list": "Roll linennoù",
+ "validator-type-mapslocation": "Lec'hiadur douaroniel",
+ "validator-type-mapslocation-list": "Roll lec'hiadurioù",
+ "validator-type-mapsrectangle": "Higarrezenn zouaroniel",
+ "validator-type-mapsrectangle-list": "Roll hirgarrezennoù"
+}
diff --git a/www/wiki/extensions/Maps/i18n/bs.json b/www/wiki/extensions/Maps/i18n/bs.json
new file mode 100644
index 00000000..86027688
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/bs.json
@@ -0,0 +1,51 @@
+{
+ "@metadata": {
+ "authors": [
+ "CERminator",
+ "KWiki",
+ "Macofe"
+ ]
+ },
+ "maps-desc": "Daje umetanje dinamičkih mapa u wiki stranice, geokodiranje adresa i druge geografske operacije",
+ "maps_map": "Mapa",
+ "maps-loading-map": "Učitavam kartu...",
+ "maps-markers": "Markeri",
+ "maps-others": "ostali",
+ "maps-ns-layer": "Sloj",
+ "maps-ns-layer-talk": "Razgovor o sloju",
+ "maps-layer-property": "Svojstvo",
+ "maps-layer-value": "Vrijednost",
+ "maps-layer-errors": "Greške",
+ "maps-error-invalid-layertype": "Nema slojeva tipa \"$1\". Samo {{PLURAL:$3|je ovaj tip podržan|su ovi tipovi podržani}}: $2",
+ "maps-error-no-layertype": "Morate odrediti tip sloja. {{PLURAL:$2|Samo ovaj tip je podržan|Ovi tipovi su podržani}}: $1",
+ "validation-error-invalid-layer": "Parametar $1 mora biti valjani sloj.",
+ "validation-error-invalid-layers": "Parametar $1 mora biti jedan ili više valjanih slojeva.",
+ "maps-layer-of-type": "Sloj tipa $1",
+ "maps-layer-type-supported-by": "Ovaj tip sloja može biti korišten {{PLURAL:$2|samo s $1 uslugom kartografiranja|s ovim uslugama kartografiranja: $1}}.",
+ "validation-error-invalid-location": "Parametar $1 mora biti valjana lokacija.",
+ "validation-error-invalid-locations": "Parametar $1 mora biti jedna ili više valjanih lokacija.",
+ "validation-error-invalid-width": "Parametar $1 mora biti valjana širina.",
+ "validation-error-invalid-height": "Parametar $1 mora biti valjana visina.",
+ "validation-error-invalid-distance": "Parametar $1 mora biti ispravno odstojanje.",
+ "validation-error-invalid-distances": "Parametar $1 mora biti jedna ili više valjanih udaljenosti.",
+ "validation-error-invalid-image": "Parametar $1 mora biti valjana slika.",
+ "validation-error-invalid-images": "Parametar $1 mora biti jedna ili više valjanih slika.",
+ "validation-error-invalid-goverlay": "Parametar $1 mora biti valjan sloj.",
+ "validation-error-invalid-goverlays": "Parametar $1 mora biti jedan ili više valjanih slojeva.",
+ "maps-abb-north": "S",
+ "maps-abb-east": "I",
+ "maps-abb-south": "J",
+ "maps-abb-west": "Z",
+ "maps-latitude": "Geografska širina:",
+ "maps-longitude": "Geografska dužina:",
+ "maps-invalid-coordinates": "Vrijednost $1 nije prepoznata kao valjan set koordinati.",
+ "maps_coordinates_missing": "Za mapu nisu navedene koordinate.",
+ "maps_geocoding_failed": "{{PLURAL:$2|Slijedeća adresa nije mogla biti geokodirana|Slijedeće adrese nisu mogle biti geokodirane}}: $1.\nMapa se ne može prikazati.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|Slijedeća adresa nije|Slijedeće adrese nisu}} mogle biti geokodiranje i {{PLURAL:$2|izostavljena je|izostavljene su}} iz mape:\n$1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|Slijedeća koordinata nije prepoznata|Slijedeće koordinate nisu prepoznate}}: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|Slijedeća koordinata nije|Slijedeće koordinate nisu}} prepoznate i {{PLURAL:$2|ignorirana je|ignorirane su}} na karti:\n$1",
+ "maps_map_cannot_be_displayed": "Karta se ne može prikazati.",
+ "maps-geocoder-not-available": "Mogućnost geokodiranja na Mapama nije dostupna. Vaša lokacija ne može biti geokodirana.",
+ "maps_click_to_activate": "Kliknite da aktivirate kartu",
+ "maps_centred_on": "Karta centrirana na $1, $2."
+}
diff --git a/www/wiki/extensions/Maps/i18n/ca.json b/www/wiki/extensions/Maps/i18n/ca.json
new file mode 100644
index 00000000..1d3e73e6
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ca.json
@@ -0,0 +1,28 @@
+{
+ "@metadata": {
+ "authors": [
+ "Paucabot",
+ "PerroVerd",
+ "Toniher"
+ ]
+ },
+ "maps_map": "Mapa",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Definició no vàlida|Definicions no vàlides}}",
+ "maps-layerdef-wrong-namespace": "Les definicions de capes només són vàlides en pàgines de l'espai de noms «$1»",
+ "maps-layerpage-nousage": "Cap pàgina està utilitzant aquesta capa de moment.",
+ "validation-error-no-non-numerics": "El paràmetre «$1» ha de ser una o més cadenes no numèriques.",
+ "maps-layer-of-type-and-name": "Capa «$2» de tipus «$1»",
+ "validation-error-invalid-locations": "El paràmetre «$1» ha de ser una o més ubicacions vàlides.",
+ "validation-error-invalid-width": "El paràmetre «$1» ha de ser una amplada vàlida.",
+ "validation-error-invalid-height": "El paràmetre «$1» ha de ser una alçada vàlida.",
+ "validation-error-invalid-distance": "El paràmetre «$1» ha de ser una distància vàlida.",
+ "validation-error-invalid-distances": "El paràmetre «$1» ha de ser una o més distàncies vàlides.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "O",
+ "maps-latitude": "Latitud:",
+ "maps-longitude": "Longitud:",
+ "maps_coordinates_missing": "No s'han proporcionat coordenades pel mapa.",
+ "maps_centred_on": "Mapa centrat en $1, $2."
+}
diff --git a/www/wiki/extensions/Maps/i18n/ce.json b/www/wiki/extensions/Maps/i18n/ce.json
new file mode 100644
index 00000000..9e323da3
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ce.json
@@ -0,0 +1,34 @@
+{
+ "@metadata": {
+ "authors": [
+ "Умар"
+ ]
+ },
+ "maps-others": "кхин",
+ "maps-ns-layer-talk": "ГӀатан дийцаре",
+ "maps-layer-property": "Билгало",
+ "maps-layer-value": "МаьӀна",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Ца магийна билгалдакхар|Ца магийна билгалдакхарш}}",
+ "maps-layerpage-usage": "Картанаш долу агӀоно лелош ю «$1»",
+ "validation-error-invalid-layer": "Параметр $1 нийса хила еза.",
+ "maps-layer-of-type": "Тайпан $1 чкъор",
+ "maps-layer-of-type-and-name": "Тайпан «$1» «$2» чкъор",
+ "validation-error-invalid-image": "Параметр $1 хила еза нийса сурт.",
+ "maps-abb-east": "Цу",
+ "maps-latitude": "Шоралла:",
+ "maps-longitude": "Дохалла:",
+ "mapeditor": "Картанаш тадерг",
+ "specialpages-group-maps": "Карташ",
+ "mapeditor-done-button": "Кийчча ю",
+ "mapeditor-export-button": "Экспорт ян викийоза сана",
+ "mapeditor-import-button": "Импорт ян викийозанах",
+ "mapeditor-mapparam-button": "Картан параметраш нисъе",
+ "mapeditor-clear-button": "ЦӀанде карта",
+ "mapeditor-link-title-switcher-link-text": "Хьажорг",
+ "mapeditor-form-field-title": "ЦӀе",
+ "mapeditor-form-field-link": "Хьажорг",
+ "mapeditor-mapparam-title": "Картан параметраш нисъе",
+ "mapeditor-imageoverlay-button": "ТӀетоха сурт тӀе диллар",
+ "mapeditor-form-field-image": "Сурт",
+ "mapeditor-imageoverlay-title": "Сурт"
+}
diff --git a/www/wiki/extensions/Maps/i18n/cs.json b/www/wiki/extensions/Maps/i18n/cs.json
new file mode 100644
index 00000000..1e2e22c1
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/cs.json
@@ -0,0 +1,91 @@
+{
+ "@metadata": {
+ "authors": [
+ "Mormegil",
+ "Vks",
+ "Utar",
+ "XenoPheX",
+ "Ilimanaq29"
+ ]
+ },
+ "right-geocode": "Geokódování",
+ "maps_map": "Mapa",
+ "maps-markers": "Značky",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-others": "ostatní",
+ "maps-ns-layer": "Vrstva",
+ "maps-ns-layer-talk": "Diskuse k vrstvě",
+ "maps-layer-property": "Vlastnost",
+ "maps-layer-value": "Hodnota",
+ "maps-layer-errors": "Chyby",
+ "maps-layer-of-type": "Vrstvy typu $1",
+ "maps-par-scrollwheelzoom": "Ukazuje, jestli by mělo být posouvání stránky myší povoleno nebo ne.",
+ "maps-finddestination-par-location": "Původní umístění.",
+ "maps-displaymap-par-circles": "Kruhy pro zobrazení",
+ "maps-displaymap-par-lines": "Kruhy ke zobrazení",
+ "maps-displaymap-par-maxzoom": "Maximální přiblížení",
+ "maps-displaymap-par-minzoom": "Minimální přiblížení",
+ "maps-displaymap-par-polygons": "Mnohoúhelníky ke zobrazení",
+ "maps-displaymap-par-rectangles": "Obdélníky ke zobrazení",
+ "validation-error-invalid-location": "Parametr $1 musí být platné umístění.",
+ "validation-error-invalid-locations": "Parametr $1 musí být jedeno nebo více platných umístění.",
+ "validation-error-invalid-width": "Parametr $1 musí být platná šířka.",
+ "validation-error-invalid-height": "Parametr $1 musí být platná výška.",
+ "validation-error-invalid-distance": "Parametr $1 musí být platná vzdálenost.",
+ "validation-error-invalid-distances": "Parametr $1 musí být jedna nebo více platných vzdáleností.",
+ "validation-error-invalid-image": "Parametr $1 musí být platný obrázek.",
+ "validation-error-invalid-images": "Parametr $1 musí být jeden nebo více platných obrázků.",
+ "maps-abb-north": "S",
+ "maps-abb-east": "V",
+ "maps-abb-south": "J",
+ "maps-abb-west": "Z",
+ "maps-latitude": "Zeměpisná šířka:",
+ "maps-longitude": "Zeměpisná délka:",
+ "maps_map_cannot_be_displayed": "Nelze zobrazit mapu.",
+ "maps_click_to_activate": "Mapu aktivujete kliknutím",
+ "maps-googlemaps3-par-poi": "Zobrazit body zájmu.",
+ "mapeditor": "Editor map",
+ "specialpages-group-maps": "Mapy",
+ "mapeditor-none-text": "Nic",
+ "mapeditor-done-button": "Hotovo",
+ "mapeditor-remove-button": "Odstranit",
+ "mapeditor-import-button2": "Importovat",
+ "mapeditor-export-button": "Export do wiki kódu",
+ "mapeditor-import-button": "Import z wiki kódu",
+ "mapeditor-select-button": "Vyberte tento mnohoúhelník",
+ "mapeditor-mapparam-button": "Upravit parametry mapy",
+ "mapeditor-clear-button": "Vyprázdnit mapu",
+ "mapeditor-code-title": "Kód wiki",
+ "mapeditor-import-title": "Importovat wiki kód",
+ "mapeditor-form-title": "Upravit podrobnosti",
+ "mapeditor-link-title-switcher-link-text": "Odkaz",
+ "mapeditor-form-field-title": "Oslovení",
+ "mapeditor-form-field-text": "Text",
+ "mapeditor-form-field-link": "Odkaz",
+ "mapeditor-form-field-icon": "Ikona",
+ "mapeditor-form-field-group": "Skupina",
+ "mapeditor-form-field-strokecolor": "Barva tahu",
+ "mapeditor-form-field-strokeopacity": "Krytí tahu",
+ "mapeditor-form-field-strokeweight": "Váha tahu",
+ "mapeditor-form-field-fillcolor": "Barva výplně",
+ "mapeditor-form-field-fillopcaity": "Krytí výplně",
+ "mapeditor-form-field-showonhover": "Zobrazit pouze pod myší",
+ "mapeditor-form-field-image": "Obrázek",
+ "mapeditor-form-field-visitedicon": "Navštívená ikona",
+ "semanticmaps-unrecognizeddistance": "Hodnota $1 není platná vzdálenost.",
+ "semanticmaps-kml-link": "Zobrazit soubor KML",
+ "semanticmaps-default-kml-pagelink": "Zobrazit stránku $1",
+ "semanticmaps-latitude": "Z. šířka: $1",
+ "semanticmaps-longitude": "Z. délka: $1",
+ "semanticmaps-altitude": "Nadm. výška: $1",
+ "semanticmaps-forminput-locations": "Místa",
+ "semanticmaps-par-staticlocations": "Seznam míst, která se přidají do mapy spolu s dotazovanými daty. Podobně jako u display_points můžete každé místo doplnit o titulek, popis a ikonu, za použití tildy „~“ jako oddělovače.",
+ "semanticmaps-par-showtitle": "Zobrazovat název v info okně značky či ne. Vypnutí je často užitečné, pokud je obsah informačního okna formátován pomocí šablony.",
+ "semanticmaps-par-centre": "Střed mapy. Není-li specifikován, mapa automaticky vybere optimální střed tak, aby byly zobrazeny všechny značky na ní.",
+ "semanticmaps-par-template": "Šablona formátování obsahu informačního okna",
+ "semanticmaps-par-geocodecontrol": "Zobrazit ovladač geocodingu.",
+ "semanticmaps-kml-text": "Text je přidružený ke každé stránce. Je přepsán dodatečnými dotazovanými vlastnostmi, jsou-li nějaké.",
+ "semanticmaps-kml-title": "Výchozí titulek pro výsledky",
+ "semanticmaps-kml-linkabsolute": "Mají být odkazy absolutní či ne (tj. relativní)",
+ "semanticmaps-kml-pagelinktext": "Text, který bude použit pro odkazy na stránku, ve kterém bude $1 nahrazeno názvem stránky"
+}
diff --git a/www/wiki/extensions/Maps/i18n/cu.json b/www/wiki/extensions/Maps/i18n/cu.json
new file mode 100644
index 00000000..8fd970fb
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/cu.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "ОйЛ"
+ ]
+ },
+ "maps-layer-errors": "блаꙁнꙑ"
+}
diff --git a/www/wiki/extensions/Maps/i18n/cv.json b/www/wiki/extensions/Maps/i18n/cv.json
new file mode 100644
index 00000000..dec01b43
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/cv.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Chuvash2014"
+ ]
+ },
+ "mapeditor-form-field-image": "Ӳкерчĕк"
+}
diff --git a/www/wiki/extensions/Maps/i18n/da.json b/www/wiki/extensions/Maps/i18n/da.json
new file mode 100644
index 00000000..747f6d39
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/da.json
@@ -0,0 +1,64 @@
+{
+ "@metadata": {
+ "authors": [
+ "Christian List",
+ "Peter Alberti",
+ "Saederup92"
+ ]
+ },
+ "maps_map": "Kort",
+ "maps-loading-map": "Indlæser kort...",
+ "maps-load-failed": "Kunne ikke indlæse kortet!",
+ "maps-markers": "Markører",
+ "maps-others": "andre",
+ "maps-ns-layer": "Lag",
+ "maps-layer-property": "Egenskab",
+ "maps-layer-value": "Værdi",
+ "maps-layer-errors": "Fejl",
+ "maps-layer-of-type": "Lag af typen \"$1\"",
+ "maps-layer-of-type-and-name": "Lag \"$2\" af typen \"$1\"",
+ "maps-displaymap-par-circles": "Cirkler at vise",
+ "maps-displaymap-par-lines": "Linjer at vise",
+ "maps-fullscreen-button": "Slå fuldskærm til eller fra",
+ "maps-abb-north": "N",
+ "maps-abb-east": "Ø",
+ "maps-abb-south": "S",
+ "maps-abb-west": "V",
+ "maps-latitude": "Breddegrad:",
+ "maps-longitude": "Længdegrad:",
+ "maps_map_cannot_be_displayed": "Kortet kan ikke vises.",
+ "maps_click_to_activate": "Tryk for at aktivere kort",
+ "maps-par-enable-fullscreen": "Aktiver fuldskærmknappen",
+ "specialpages-group-maps": "Kort",
+ "mapeditor-none-text": "Ingen",
+ "mapeditor-done-button": "Udført",
+ "mapeditor-remove-button": "Fjern",
+ "mapeditor-import-button2": "Importer",
+ "mapeditor-export-button": "Eksporter til wiki kode",
+ "mapeditor-import-button": "Importer fra wiki kode",
+ "mapeditor-clear-button": "Tøm kort",
+ "mapeditor-code-title": "Wiki kode",
+ "mapeditor-import-title": "Importer wiki kode",
+ "mapeditor-form-title": "Redigér detaljer",
+ "mapeditor-link-title-switcher-link-text": "Link",
+ "mapeditor-form-field-title": "Titel",
+ "mapeditor-form-field-text": "Tekst",
+ "mapeditor-form-field-link": "Link",
+ "mapeditor-form-field-icon": "Ikon",
+ "mapeditor-form-field-group": "Gruppe",
+ "mapeditor-mapparam-defoption": "-Vælg parameter-",
+ "mapeditor-form-field-image": "Billede",
+ "semanticmaps-unrecognizeddistance": "Værdien $1 er ikke en gyldig afstand.",
+ "semanticmaps-kml-link": "Vis KML-filen",
+ "semanticmaps-default-kml-pagelink": "Se siden $1",
+ "semanticmaps-latitude": "Breddegrad: $1",
+ "semanticmaps-longitude": "Længdegrad: $1",
+ "semanticmaps-altitude": "Højde: $1",
+ "semanticmaps-forminput-locations": "Steder",
+ "semanticmaps-kml-title": "Standardtitlen for resultater",
+ "validator-type-mapscircle-list": "Liste over cirkler",
+ "validator-type-mapsline": "Geografisk linje",
+ "validator-type-mapsline-list": "Liste over linjer",
+ "validator-type-mapslocation-list": "Liste over placeringer",
+ "validator-type-jsonfile": "tekst"
+}
diff --git a/www/wiki/extensions/Maps/i18n/de.json b/www/wiki/extensions/Maps/i18n/de.json
new file mode 100644
index 00000000..71905a10
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/de.json
@@ -0,0 +1,231 @@
+{
+ "@metadata": {
+ "authors": [
+ "Als-Holder",
+ "DaSch",
+ "Imre",
+ "Kghbln",
+ "Metalhead64",
+ "Purodha",
+ "The Evil IP address",
+ "Umherirrender"
+ ]
+ },
+ "maps-desc": "Ermöglicht das Einbinden dynamischer Karten, die Georeferenzierung von Adressen und andere geographische Operationen",
+ "right-geocode": "Georeferenzieren",
+ "action-geocode": "Georeferenzierungen auf diesem Wiki auszuführen",
+ "maps_map": "Karte",
+ "maps-tracking-category": "Seiten mit einer von der Maps-Erweiterung gerenderten Karte",
+ "maps-loading-map": "Die Karte wird geladen …",
+ "maps-load-failed": "Die Karte konnte nicht geladen werden.",
+ "maps-markers": "Markierungen",
+ "maps-copycoords-prompt": "STRG+C, ENTER",
+ "maps-searchmarkers-text": "Markierungen filtern",
+ "maps-others": "andere",
+ "maps-kml-parsing-failed": "Das Parsen einer oder mehrerer KML-Dateien ist fehlgeschlagen. Dies geschieht normalerweise aufgrund eines Abfragefehlers oder aufgrund von fehlerhaftem XML.",
+ "maps-ns-layer": "Ebene",
+ "maps-ns-layer-talk": "Ebene Diskussion",
+ "maps-layer-property": "Attribut",
+ "maps-layer-value": "Wert",
+ "maps-layer-errors": "Fehler",
+ "maps-layerdef-invalid": "Ungültige {{PLURAL:$1|Definition|Definitionen}}",
+ "maps-layerdef-invalid-fatal": "Die Definition ist ungültig und führt zu einem schwerwiegenden Fehler.",
+ "maps-layerdef-wrong-namespace": "Ebenendefinitionen sind nur auf Seiten des Namensraums „$1“ gültig",
+ "maps-layerdef-equal-layer-name": "Ebenennamen müssen auf der gleichen Ebenenseite einmalig sein. „$1“ wird bereits von einer anderen Ebene verwendet.",
+ "maps-layerpage-usage": "Seiten mit Karten, die die Ebene „$1“ verwenden",
+ "maps-layerpage-nousage": "Keine Seite verwendet momentan diese Ebene.",
+ "maps-error-invalid-layertype": "Es gibt keine Ebenen des Typs „$1“. Nur {{PLURAL:$3|dieser Typ wird|diese Typen werden}} unterstützt: $2",
+ "maps-error-no-layertype": "Der Ebenentyp muss angegeben werden. Nur {{PLURAL:$2|dieser Typ wird|diese Typen werden}} unterstützt: $1",
+ "validation-error-invalid-layer": "Der Parameter „$1“ muss einer gültigen Ebene entsprechen.",
+ "validation-error-invalid-layers": "Der Parameter „$1“ muss einer oder mehreren gültigen Ebenen entsprechen.",
+ "validation-error-no-non-numeric": "Der Parameter „$1“ muss eine nicht-numerische Zeichenfolge sein.",
+ "validation-error-no-non-numerics": "Der Parameter „$1“ muss eine oder mehrere nicht-numerische Zeichenfolgen sein.",
+ "maps-layer-of-type": "Ebene des Typs „$1“",
+ "maps-layer-of-type-and-name": "Ebene „$2“ des Typs „$1“",
+ "maps-layer-type-supported-by": "Dieser Ebenentyp kann {{PLURAL:$2|nur mit dem Kartografiedienst $1 genutzt werden|mit diesen Kartografiediensten genutzt werden: $1}}.",
+ "maps-coordinates-description": "Parserhook zur Koordinatenformatierung aus und in alle unterstützte Formate.",
+ "maps-displaymap-description": "Geographische Karten ohne jegliche im Wiki definierte Markierungen anzeigen.",
+ "maps-distance-description": "Konvertiere die Entfernung unter Verwendung einer der unterstützten Einheiten in ihr Äquivalent einer anderen unterstützen Einheit.",
+ "maps-finddestination-description": "Ein Ziel unter Angabe des Ausgangspunkts (kann in jedwedem unterstützten Format angegeben sein), der Peilung sowie der Entfernung ermitteln.",
+ "maps-geocode-description": "Aktiviert das Georeferenzieren von Adressen, also deren Umwandlung in Koordinaten. Mehrere Georeferenzierungsdienste werden unterstützt, was allerdings nicht mit den Kartografiediensten zu verwechseln ist.",
+ "maps-geodistance-description": "Berechne die Entfernung zwischen zwei Punkten von einer der unterstützten Einheiten in ihr Äquivalent einer anderen unterstützten Einheit.",
+ "maps-mapsdoc-description": "Zeigt eine Übersicht aller Parameter mitsamt deren Standardwerten einschließlich ihrer Beschreibung, die im Zusammenhang mit dem angegebenen Kartografiedienst genutzt werden können.",
+ "maps-layerdefinition-description": "Beschreibt eine benutzerdefinierte Ebene, die zusammen mit anderen Kartenfunktionen angezeigt werden kann.",
+ "maps-mapsdoc-par-service": "Der Kartografiedienst zu dessen Parametern die Dokumentation angezeigt werden soll.",
+ "maps-mapsdoc-par-language": "Die Sprache, in der die Dokumentation angezeigt werden soll. Sofern keine Übersetzung verfügbar ist, wird stattdessen Englisch verwendet werden.",
+ "maps-coordinates-par-location": "Die zu formatierenden Koordinaten.",
+ "maps-coordinates-par-format": "Das Zielformat der Koordinaten.",
+ "maps-coordinates-par-directional": "Gibt an, ob die Koordinaten eine Richtungsangabe haben sollen oder nicht.",
+ "maps-par-scrollwheelzoom": "Zeigt an, ob das Mausscrollen aktiviert werden soll oder nicht.",
+ "maps-distance-par-distance": "Die Entfernung zum konvertieren dieser Einheit in ihr Äquivalent einer angegebenen Einheit.",
+ "maps-distance-par-decimals": "Die bei der Ergebnisausgabe zu verwendende Höchstzahl an Nachkommastellen.",
+ "maps-distance-par-unit": "Die Ausgabeeinheit für die Entfernung.",
+ "maps-finddestination-par-location": "Der Ausgangspunkt.",
+ "maps-finddestination-par-bearing": "Die Ausgangspeilung.",
+ "maps-finddestination-par-distance": "Die Reiseentfernung.",
+ "maps-finddestination-par-format": "Das Ausgabeformat für das Ziel.",
+ "maps-finddestination-par-directional": "Gibt an, ob das Ausgabeformat des Ziels eine Richtungsangabe haben soll oder nicht.",
+ "maps-geocode-par-location": "Die Adresse, die geokodiert werden soll.",
+ "maps-geocode-par-format": "Das Ausgabeformat für die Koordinaten.",
+ "maps-geocode-par-directional": "Gibt an, ob die Koordinaten eine Richtungsangabe haben sollen oder nicht.",
+ "maps-geodistance-par-location1": "Der erste von zwei Punkten zwischen denen die Entfernung berechnet werden soll.",
+ "maps-geodistance-par-location2": "Der zweite von zwei Punkten zwischen denen die Entfernung berechnet werden soll.",
+ "maps-geodistance-par-unit": "Die Ausgabeeinheit für die Entfernung.",
+ "maps-geodistance-par-decimals": "Die bei der Ergebnisausgabe zu verwendende Höchstzahl an Nachkommastellen.",
+ "maps-displaymap-par-mappingservice": "Der Kartografiedienst, der zur Generierung der Karte genutzt werden soll",
+ "maps-displaymap-par-coordinates": "Die Postion auf welche die Karte zunächst zentriert werden soll",
+ "maps-displaymap-par-visitedicon": "Der Dateiname des Symbols, das anstelle der ursprünglichen Markierung angezeigt werden soll, sobald die Originalmarkierungen angeklickt wurden",
+ "maps-displaymap-par-zoom": "Der Anzeigestufe (Zoom) der Karte.\nSofern keine angegeben wurde und mehrerer Markierungen auf der Karte vorhanden sind, wird die für deren gemeinsame Darstellung passende Anzeigestufe verwendet und nicht der Standardwert des Kartografiediensts.",
+ "maps-displaymap-par-centre": "Die Koordinaten, die zur Zentrierung der Karte verwendet werden sollen.\nSowohl die direkte Angabe von Koordinaten wie auch die indirekte Angabe einer Adresse ist möglich.\nSofern nichts angegeben wurde, wird sich die Karte um die darzustellende Markierung, bzw. zwischen den darzustellenden Markierungen zentrieren.",
+ "maps-displaymap-par-title": "Der Text, der in den Pop-up-Fenstern zu den Markierungen angezeigt werden soll, die über keinen eigenständigen Titel verfügen. Sofern dieser zusammen mit einer Beschriftung genutzt wird, wird der Titel in Fettschrift und unterstrichen angezeigt.",
+ "maps-displaymap-par-label": "Der Text, der in den Pop-up-Fenstern der Markierungen angezeigt werden soll, die über keine eigenständige Beschriftung verfügen",
+ "maps-displaymap-par-icon": "Das Symbol, das für alle Markierungen genutzt werden soll",
+ "maps-displaymap-par-circles": "Anzuzeigende Kreise",
+ "maps-displaymap-par-copycoords": "Zeigt einen Dialog beim Anklicken eines Standortes an, von dem seine Koordinaten kopiert werden können.",
+ "maps-displaymap-par-lines": "Anzuzeigende Linien",
+ "maps-displaymap-par-maxzoom": "Die maximale Anzeigestufe",
+ "maps-displaymap-par-minzoom": "Die minimale Anzeigestufe",
+ "maps-displaymap-par-polygons": "Anzuzeigende Vielecke",
+ "maps-displaymap-par-rectangles": "Anzuzeigende Rechtecke",
+ "maps-displaymap-par-static": "Die Karte statisch machen",
+ "maps-displaymap-par-wmsoverlay": "Eine Web-Map-Service-Ebene verwenden",
+ "maps-displaymap-par-geojson": "URL einer Datei oder Name der Seite, die GeoJSON-Daten enthält",
+ "maps-fullscreen-button": "Auf Vollbild umschalten",
+ "maps-fullscreen-button-tooltip": "Die Karte als Vollbild oder eingebettet darstellen.",
+ "validation-error-invalid-location": "Parameter $1 muss einem gültigen Standort entsprechen.",
+ "validation-error-invalid-locations": "Parameter $1 muss einem oder mehreren gültigen Standorten entsprechen.",
+ "validation-error-invalid-width": "Parameter $1 muss einer gültigen Breite entsprechen.",
+ "validation-error-invalid-height": "Parameter $1 muss einer gültigen Höhe entsprechen.",
+ "validation-error-invalid-distance": "Parameter $1 muss einer gültigen Entfernung entsprechen.",
+ "validation-error-invalid-distances": "Parameter $1 muss einer oder mehreren gültigen Entfernungen entsprechen.",
+ "validation-error-invalid-image": "Parameter $1 muss einem gültigen Bild entsprechen.",
+ "validation-error-invalid-images": "Parameter $1 muss einem oder mehreren gültigen Bildern entsprechen.",
+ "validation-error-invalid-goverlay": "Parameter $1 muss einer gültigen Überlagerung entsprechen.",
+ "validation-error-invalid-goverlays": "Parameter $1 muss einer oder mehreren gültigen Überlagerungen entsprechen.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "O",
+ "maps-abb-south": "S",
+ "maps-abb-west": "W",
+ "maps-latitude": "Breitengrad:",
+ "maps-longitude": "Längengrad:",
+ "maps-invalid-coordinates": "Der Wert $1 bezeichnet kein gültiges Koordinatenpaar.",
+ "maps_coordinates_missing": "Es wurden keine Koordinaten für die Karte angegeben.",
+ "maps_geocoding_failed": "Die {{PLURAL:$2|folgende Adresse|folgenden Adressen}} konnten nicht geokodiert werden: $1.",
+ "maps_geocoding_failed_for": "Die {{PLURAL:$2|folgende Adresse konnte|folgenden Adressen konnten}} nicht georeferenziert werden und {{PLURAL:$2|wurde|wurden}} auf der Karte nicht berücksichtigt:\n$1",
+ "maps_unrecognized_coords": "Die {{PLURAL:$2|folgende Koordinate wurde|folgenden Koordinaten wurden}} nicht erkannt: $1.",
+ "maps_unrecognized_coords_for": "Die {{PLURAL:$2|folgende Koordinate wurde|folgenden Koordinaten wurden}} nicht erkannt und {{PLURAL:$2|wurde|wurden}} auf der Karte nicht berücksichtigt:\n$1",
+ "maps_map_cannot_be_displayed": "Diese Karte kann nicht angezeigt werden.",
+ "maps-geocoder-not-available": "Die Funktion zum Georeferenzierung von Karten ist nicht verfügbar. Der Standort kann nicht georeferenziert werden.",
+ "maps_googlemaps3": "Karte (Google Maps v3)",
+ "maps_leaflet": "Karte (Leaflet)",
+ "maps-leaflet-par-defzoom": "Erlaubt das Festlegen der Standardvergrößerungsstufe der Karte",
+ "maps-leaflet-par-layers": "Die Ebene, die angezeigt wird, wenn die Karte lädt.",
+ "maps-leaflet-par-overlaylayers": "Die Überlagerungsebenen, die angezeigt werden, wenn die Karte lädt.",
+ "maps-leaflet-par-maxclusterradius": "Der maximale Radius, den ein Cluster ab der Mittelmarkierung abdeckt (in Pixeln).",
+ "maps-leaflet-par-clusterspiderfy": "Bei Klicken auf einen Cluster in einer niedrigen Vergrößerungsstufe wird dieser netzförmig expandiert, so dass alle enthaltenen Markierungen eingesehen werden können.",
+ "maps_click_to_activate": "Klicken, um die Karte zu aktivieren.",
+ "maps_centred_on": "Karte ist auf $1, $2 zentriert.",
+ "maps-par-mappingservice": "Ermöglicht das Festlegen des Kartografiedienstes, der zum Erstellen der Karte verwendet werden soll",
+ "maps-par-resizable": "Die Karte durch Ziehen von der unteren rechten Ecke größenveränderbar machen können",
+ "maps-par-searchmarkers": "Erlaubt die Suche nach speziellen Markierungen über ein in die Karte eingebettetes Feld",
+ "maps-par-zoom": "Die Zoomstufe für die Karte. Karten mit Kennzeichnungen werden standardmäßig auf die Stufe gezoomt in der diese noch alle gemeinsam angezeigt werden können.",
+ "maps-par-width": "Die Kartenbreite, die genutzt werden soll. Standardmäßig wird Pixel (px) als Einheit angenommen. Jedoch kann auch eine der folgenden Einheiten angegeben werden: ex, em und %",
+ "maps-par-height": "Die Kartenhöhe, die genutzt werden soll. Standardmäßig wird Pixel (px) als Einheit angenommen. Jedoch kann auch eine der folgenden Einheiten angegeben werden: ex, em und %",
+ "maps-par-centre": "Die Koordinaten des Standorts (bpsw. <code>50.0093,8.2564</code>), an dem die Karte zentriert werden soll",
+ "maps-par-enable-fullscreen": "Vollbildschaltfläche aktivieren",
+ "maps-par-kml": "Die auf die Karte zu ladenden KML-Dateien.",
+ "maps-par-markercluster": "Erlaubt das Zusammenführen mehrerer benachbarter Markierungen zu einer Markierung",
+ "maps-googlemaps3-incompatbrowser": "Der Browser ist nicht mit Google Maps v3 kompatibel.",
+ "maps-googlemaps3-par-imageoverlays": "Erlaubt das Hinzufügen eines Bildes, das am angegebenen Ort auf der Karte angezeigt wird",
+ "maps-googlemaps3-par-type": "Die zunächst anzuzeigende Kartenart.",
+ "maps-googlemaps3-par-types": "Die Kartenarten, die über die Steuerung zu den Kartenarten verfügbar gemacht werden sollen.",
+ "maps-googlemaps3-par-layers": "Die auf die Karte zu ladenden Sonderebenen.",
+ "maps-googlemaps3-par-controls": "Die auf der Karte anzuzeigenden Steuerelemente.",
+ "maps-googlemaps3-par-zoomstyle": "Der Stil der Zoomsteuerung.",
+ "maps-googlemaps3-par-typestyle": "Der Stil der Steuerung zu den Kartenarten.",
+ "maps-googlemaps3-par-autoinfowindows": "Die Informationsfenster nach dem Laden der Seite automatisch öffnen oder nicht",
+ "maps-googlemaps3-par-gkml": "Die von Google gehosteten KML-Dateien, die auf die Karte geladen werden sollen",
+ "maps-googlemaps3-par-kmlrezoom": "Die Karte erneut zoomen, nachdem die KML-Ebenen geladen wurden.",
+ "maps-googlemaps3-par-poi": "Die Sehenswürdigkeiten anzeigen",
+ "maps-googlemaps3-par-clustergridsize": "Die Rastergröße eines Clusters in Pixeln",
+ "maps-par-clustermaxzoom": "Die maximale Vergrößerungsstufe, in der Cluster vorhanden sein können.",
+ "maps-par-clusterzoomonclick": "Ob das Standardverhalten beim Klicken auf einen Cluster dessen Vergrößerung ist.",
+ "maps-par-maxclusterradius": "Der maximale Radius, den ein Cluster abdeckt.",
+ "maps-googlemaps3-par-clusteraveragecenter": "Ob die Mitte jedes Clusters der Durchschnitt aller Markierungen des Clusters sein soll",
+ "maps-googlemaps3-par-clusterminsize": "Die Mindestzahl der Markierungen, die ein Cluster enthalten soll, bevor die Markierungen versteckt werden und ein Zähler angezeigt wird",
+ "mapeditor": "Karteneditor",
+ "specialpages-group-maps": "Karten",
+ "mapeditor-parser-error": "Ein Fehler ist beim Parsen der Metadaten aufgetreten. Ignoriere Benutzereingaben.",
+ "mapeditor-none-text": "Keine",
+ "mapeditor-done-button": "Fertig",
+ "mapeditor-remove-button": "Entfernen",
+ "mapeditor-import-button2": "Importieren",
+ "mapeditor-export-button": "In Wikicode exportieren",
+ "mapeditor-import-button": "Aus Wikicode importieren",
+ "mapeditor-select-button": "Dieses Vieleck auswählen",
+ "mapeditor-mapparam-button": "Kartenparameter bearbeiten",
+ "mapeditor-clear-button": "Karte leeren",
+ "mapeditor-code-title": "Wikicode",
+ "mapeditor-import-title": "Wikicode importieren",
+ "mapeditor-import-note": "Hinweis: Der Parser erwartet ein sehr strenges Format für den Wikicode. Der hier eingegebene Code sollte mit dem ausgegebenen Code der Exportfunktion übereinstimmen.",
+ "mapeditor-form-title": "Einzelheiten bearbeiten",
+ "mapeditor-link-title-switcher-popup-text": "Pop-up mit Text",
+ "mapeditor-link-title-switcher-link-text": "Link",
+ "mapeditor-form-field-title": "Titel",
+ "mapeditor-form-field-text": "Text",
+ "mapeditor-form-field-link": "Link",
+ "mapeditor-form-field-icon": "Symbol",
+ "mapeditor-form-field-group": "Gruppe",
+ "mapeditor-form-field-inlinelabel": "Eingebettete Bezeichnung",
+ "mapeditor-form-field-strokecolor": "Farbe der Schrift",
+ "mapeditor-form-field-strokeopacity": "Deckkraft der Schrift",
+ "mapeditor-form-field-strokeweight": "Stärke der Schrift",
+ "mapeditor-form-field-fillcolor": "Farbe der Füllung",
+ "mapeditor-form-field-fillopcaity": "Deckkraft der Füllung",
+ "mapeditor-form-field-showonhover": "Nur beim Darüberfahren anzeigen",
+ "mapeditor-mapparam-title": "Kartenparameter bearbeiten",
+ "mapeditor-mapparam-defoption": "-Parameter auswählen-",
+ "mapeditor-imageoverlay-button": "Bildeinblendung hinzufügen",
+ "mapeditor-form-field-image": "Bild",
+ "mapeditor-imageoverlay-title": "Einzelheiten zur Bildeinblendung",
+ "mapeditor-form-field-visitedicon": "Symbol sofern besucht",
+ "semanticmaps-unrecognizeddistance": "Der Wert $1 ist keine gültige Distanz.",
+ "semanticmaps-kml-link": "KML-Datei ansehen",
+ "semanticmaps-kml": "Export (KML)",
+ "semanticmaps-default-kml-pagelink": "Artikel $1 ansehen",
+ "semanticmaps-latitude": "Breitengrad: $1",
+ "semanticmaps-longitude": "Längengrad: $1",
+ "semanticmaps-altitude": "Höhe: $1",
+ "semanticmaps-forminput-locations": "Standort",
+ "semanticmaps-par-staticlocations": "Die Listen von Standorten, die zusammen mit den abgefragten Daten, der Karte hinzugefügt werden sollen. Analog zu den Anzeigepunkten können je Standort Titel, Beschreibung und Symbol, unter Verwendung einer Tilde „~“ als Trennzeichen, hinzugefügt werden.",
+ "semanticmaps-par-showtitle": "Den Titel im Informationsfenster der Kennzeichnung anzeigen oder nicht. Diese Option zu deaktivieren ist oftmals dann nützlich, sofern eine Vorlage zur Formatierung des Informationsfensterinhalts verwendet wird.",
+ "semanticmaps-par-hidenamespace": "Den Namen des Namensraums im Informationsfenster der Kennzeichnung anzeigen",
+ "semanticmaps-par-centre": "Das Zentrum der Karte. Sofern nicht angegeben wird automatisch das optimale Zentrum zur Darstellung aller Kennzeichnungen auf der Karte gewählt.",
+ "semanticmaps-par-template": "Die zur Formatierung des Informationsfensterinhalts zu verwendende Vorlage.",
+ "semanticmaps-par-geocodecontrol": "Die Steuerungsseite zum Geokodieren anzeigen.",
+ "semanticmaps-par-activeicon": "Das Symbol, das anstelle der Standardmarkierung angezeigt wird, sofern die aktive Seite dem Abfrageergebnis entspricht.",
+ "semanticmaps-par-pagelabel": "Sofern mit „yes“ (ja) festgelegt, verfügen alle Markierungen über eine eingebettete Anzeige mit einem Link zur entsprechenden Seite sowie den entsprechenden Koordinaten.",
+ "semanticmaps-par-ajaxcoordproperty": "Name des Koordinatenattributs, das zur Erstellung der Ajax-Abfrage verwendet wird.",
+ "semanticmaps-par-ajaxquery": "Eine zweite Abfrage, die über Ajax gesendet wird, um zusätzliche Koordinaten abzurufen.",
+ "semanticmaps-par-userparam": "Ein in jedem Vorlagenaufruf zu übergebender Wert, falls eine Vorlage verwendet wird.",
+ "semanticmaps-kml-text": "Der Text, der zu jeder Seite angezeigt wird. Wird im Fall zusätzlich abgefragter Attribute ersetzt.",
+ "semanticmaps-kml-title": "Der Standardtitel für die Ergebnisse",
+ "semanticmaps-kml-linkabsolute": "Die Links sollen absolut sein (anstatt relativ)",
+ "semanticmaps-kml-pagelinktext": "Der Text, der für die Links zur Seite genutzt werden soll. $1 wird dabei durch den Namen der Seite ersetzt.",
+ "semanticmaps-shapes-improperformat": "$1 ist falsch formatiert. Siehe hierzu die Dokumentation bezüglich Formatierungen.",
+ "semanticmaps-shapes-missingshape": "Für $1 wurden keine Formen gefunden. Siehe hierzu die Dokumentation bezüglich verfügbarer Formen.",
+ "validator-type-mapscircle": "Geografischer Kreis",
+ "validator-type-mapscircle-list": "Liste der Kreise",
+ "validator-type-mapsimageoverlay": "Bildüberlagerung",
+ "validator-type-mapsimageoverlay-list": "Liste der Bildüberlagerungen",
+ "validator-type-mapsline": "Geografische Linie",
+ "validator-type-mapsline-list": "Liste der Linien",
+ "validator-type-mapslocation": "Geografischer Standort",
+ "validator-type-mapslocation-list": "Liste der Standorte",
+ "validator-type-mapsrectangle": "Geografisches Rechteck",
+ "validator-type-mapsrectangle-list": "Liste der Rechtecke",
+ "validator-type-mapspolygon": "Geografisches Vieleck",
+ "validator-type-mapspolygon-list": "Liste geografischer Vielecke",
+ "validator-type-wmsoverlay": "Web-Map-Service-Überlagerung",
+ "validator-type-jsonfile": "text"
+}
diff --git a/www/wiki/extensions/Maps/i18n/diq.json b/www/wiki/extensions/Maps/i18n/diq.json
new file mode 100644
index 00000000..67c297e8
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/diq.json
@@ -0,0 +1,36 @@
+{
+ "@metadata": {
+ "authors": [
+ "Erdemaslancan",
+ "Gorizon",
+ "Mirzali"
+ ]
+ },
+ "maps_map": "Xerita",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-layer-value": "Erc",
+ "maps-abb-north": "Z",
+ "maps-abb-east": "A",
+ "maps-abb-south": "V",
+ "maps-abb-west": "P",
+ "maps-latitude": "Verıniye:",
+ "maps-longitude": "Derganiye:",
+ "maps_coordinates_missing": "Qan dê xerita koordinat nêdebya.",
+ "maps_googlemaps3": "Google Maps v3",
+ "maps_openlayers": "OpenLayers",
+ "mapeditor": "Vurnerê Xerita",
+ "specialpages-group-maps": "Xeritey",
+ "mapeditor-none-text": "Çıniyo",
+ "mapeditor-done-button": "Qeyd ke",
+ "mapeditor-remove-button": "Hewad",
+ "mapeditor-import-button2": "Zerre ke",
+ "mapeditor-code-title": "Wiki kode",
+ "mapeditor-form-title": "Detaya timar ke",
+ "mapeditor-link-title-switcher-link-text": "Gıre",
+ "mapeditor-form-field-title": "Sername",
+ "mapeditor-form-field-text": "Metın",
+ "mapeditor-form-field-link": "Gıre",
+ "mapeditor-form-field-icon": "Ikon",
+ "mapeditor-form-field-group": "Grube",
+ "mapeditor-form-field-image": "Resım"
+}
diff --git a/www/wiki/extensions/Maps/i18n/dsb.json b/www/wiki/extensions/Maps/i18n/dsb.json
new file mode 100644
index 00000000..c68f71e2
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/dsb.json
@@ -0,0 +1,77 @@
+{
+ "@metadata": {
+ "authors": [
+ "Michawiki",
+ "Macofe"
+ ]
+ },
+ "maps-desc": "Zmóžnijo zasajźowanje dynamiskich kórtow do wikibokow, geokoděrowanje adresow a druge geografiske akcije",
+ "maps_map": "Karta",
+ "maps-loading-map": "Kórta se zacytujo...",
+ "maps-markers": "Marki",
+ "maps-others": "druge",
+ "maps-ns-layer": "Rownina",
+ "maps-ns-layer-talk": "Diskusija rowniny",
+ "maps-layer-property": "Kakosć",
+ "maps-layer-value": "Gódnota",
+ "maps-layer-errors": "Zmólki",
+ "maps-error-invalid-layertype": "Njejsu žedne rowniny typa \"$1\". Jano {{PLURAL:$3|toś ten typ se pódpěra|toś tej typa se pódpěratej|toś te typy se pódpěraju|toś te typy se pódpěraju}}: $2",
+ "maps-error-no-layertype": "Musyš typ rowniny pódaś. Jano {{PLURAL:$2|toś ten typ se pódpěra|toś tej typa se pódpěratej|toś te typy se pódpěraju|toś te typy se pódpěraju}}: $1",
+ "validation-error-invalid-layer": "Parameter $1 musy płaśiwa rownina byś.",
+ "validation-error-invalid-layers": "Parameter $1 musy jadna płaśiwa rownina abo někotare płaśiwe rowniny byś.",
+ "maps-layer-of-type": "Rownina typa $1",
+ "maps-layer-type-supported-by": "Toś ten typ rowniny dajo se jano {{PLURAL:$2|z kartografiskeju słužbu $1 wužywaś|z toś tymi kartografiskimi słužbami wužywaś: $1}}.",
+ "maps-coordinates-par-location": "Koordinaty, kótarež coš formatěrowaś.",
+ "maps-coordinates-par-format": "Celowy format za koordinaty.",
+ "maps-finddestination-par-location": "Wuchadnišćo.",
+ "maps-finddestination-par-format": "Wudawański format za cel.",
+ "maps-geocode-par-location": "Adresa, kótaruž coš geokoděrowaś.",
+ "maps-geocode-par-format": "Wudawański format za koordinaty.",
+ "validation-error-invalid-location": "Parameter $1 musy płaśiwe městno byś.",
+ "validation-error-invalid-locations": "Parameter $1 musy jadne płaśiwe městno abo někotare płaśiwe městna byś.",
+ "validation-error-invalid-width": "Parameter $1 musy płaśiwa šyrokosć byś.",
+ "validation-error-invalid-height": "Parameter $1 musy płaśiwa wusokosć byś.",
+ "validation-error-invalid-distance": "Gódnota $1 musy płaśiwa distanca byś.",
+ "validation-error-invalid-distances": "Parameter $1 musy jadna płaśiwa distanca abo někotare płaśiwe distance byś.",
+ "validation-error-invalid-image": "Parameter $1 musy płaśiwy wobraz byś.",
+ "validation-error-invalid-images": "Parameter $1 musy jadne płaśiwy wobraz abo někotare płaśiwe wobraze byś.",
+ "validation-error-invalid-goverlay": "Parameter $1 musy płaśiwe pśewarstowanje byś.",
+ "validation-error-invalid-goverlays": "Parameter $1 musy jadne płaśiwe pśewarstowanje abo někotare płaśiwe pśewarstowanja byś.",
+ "maps-abb-north": "PP",
+ "maps-abb-east": "PZ",
+ "maps-abb-south": "PD",
+ "maps-abb-west": "PW",
+ "maps-latitude": "Šyrina:",
+ "maps-longitude": "Dlinina:",
+ "maps-invalid-coordinates": "Gódnota $1 njejo se spóznała ako płaśiwa sajźba koordinatow.",
+ "maps_coordinates_missing": "Za kórtu njejsu koordinaty pódane.",
+ "maps_geocoding_failed": "Geokoděrowanje {{PLURAL:$2|slědujuceje adrese|slědujuceju adresowu|slědujucych adresow|slědujucych adresow}} njejo móžno było: $1. Kórta njedajo se zwobrazniś.",
+ "maps_geocoding_failed_for": "Geokoděrowanje {{PLURAL:$2|slědujuceje adrese|slědujuceju adresowu|slědujucych adresow|slědujucych adresow}} njejo móžno było a togodla toś {{PLURAL:$2|ta adresa wuwóstaja|tej adresy wuwóstajotej|te adrese wuwóstajaju|te adresy wuwóstajaju}} se na kórśe: $1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|Slědujuca koordinata njejo se spóznała|Slědujucej koordinaśe njejstej se spóznałej|Slědujuce koordinaty njejsu se spóznali|Slědujuce koordinaty njejsu se spóznali}}: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|Slědujuca koordinata njejo se spóznała|Slědujucej koordinaśe stej se spóznałej|Slědujuce koordinaty su se spóznali|Slědujuce koordinaty su se spóznali}} a {{PLURAL:$2|njejo se wuwóstajiła|njejstej se wuwóstajiłej|njejsu wuwóstajili|njejsu se wuwóstajili}} na kórśe: $1",
+ "maps_map_cannot_be_displayed": "Kórta njedajo se zwobrazniś.",
+ "maps-geocoder-not-available": "Funkcija geokoděrowanja Kórtow njestoj k dispoziciji, twójo městno njedajo se geokoděrowaś.",
+ "maps_click_to_activate": "Klikni, aby kórtu aktiwěrował",
+ "maps_centred_on": "Kórta na $1, $2 centrěrowana.",
+ "semanticmaps-unrecognizeddistance": "Gódnota $1 njejo płaśiwa distanca.",
+ "semanticmaps-kml-link": "KML-dataju se woglědaś",
+ "semanticmaps-default-kml-pagelink": "Bok $1 se woglědaś",
+ "semanticmaps-latitude": "Šyrina: $1",
+ "semanticmaps-longitude": "Dlinina: $1",
+ "semanticmaps-altitude": "Wusokosć: $1",
+ "semanticmaps-forminput-locations": "Městna",
+ "semanticmaps-par-staticlocations": "Lisćina městnow, kótarež maju se zgromadnje z napšašowanymi datami kórśe pśidaś. Ako pla zwobraznjeńskich dypkow móžoš titel, wopisanje a symbol na městno z pomocu tildy \"~\" ako źěleńske znamuško pśidaś.",
+ "semanticmaps-par-showtitle": "Titel w informaciskem woknje marki pokazaś abo nic. Jo cesto wužytne, toś to nastajenje znjemóžniś, gaž pśedłoga se wužywa, aby se wopśimjeśe informaciskego wokna formatěrowało.",
+ "semanticmaps-par-hidenamespace": "Titel mjenjowego ruma w informaciskem woknje marki pokazaś",
+ "semanticmaps-par-centre": "Srjejźišćo kórty. Jolic njejo pódane, buźo kórta awtomatiski optimane srjejźišć wubraś, aby wšykne marki na kórśe zwobrazniła.",
+ "semanticmaps-par-template": "Pśedłoga, kótaraž ma se za formatěrowanje wopśimjeśa infowokna wužywaś,",
+ "semanticmaps-par-geocodecontrol": "Geokoděrowańske wóźenje pokazaś",
+ "semanticmaps-par-activeicon": "Symbol, kótaryž ma se město standardneje marki zwobrazniś, gaž aktiwny bok wótpowědujo napšašowańskemu wuslědkoju.",
+ "semanticmaps-par-pagelabel": "Jolic na \"jo\" stajone, změju wšykne marki \"inlineLabel\" z wótkazom k bokoju, kótaryž wopśimujo koordinaty marki.",
+ "semanticmaps-kml-text": "Tekst, kótaryž jo z kuždym bokom zwězany. Wuměnja se pśez pśidatne napšašowane kakosći, jolic su take.",
+ "semanticmaps-kml-title": "Standardny titel za wuslědki",
+ "semanticmaps-kml-linkabsolute": "Wótkaze by měli absolutne byś (nic relatiwne)",
+ "semanticmaps-kml-pagelinktext": "Tekst, kótaryž ma se za wótkaze k tomu bokoju wužywaś, w kótaremž $1 wuměnja se pśez titel boka",
+ "semanticmaps-shapes-improperformat": "$1 jo se wopak formatěrował. Pšosym glej dokumentaciju za formatěrowanje",
+ "semanticmaps-shapes-missingshape": "Formy za $1 njejsu se namakali. Pšosym glej dokumentaciju za k dispoziciji stojece formy."
+}
diff --git a/www/wiki/extensions/Maps/i18n/el.json b/www/wiki/extensions/Maps/i18n/el.json
new file mode 100644
index 00000000..755e14b0
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/el.json
@@ -0,0 +1,165 @@
+{
+ "@metadata": {
+ "authors": [
+ "Evropi",
+ "Glavkos",
+ "Omnipaedista",
+ "Protnet",
+ "ZaDiak",
+ "Macofe",
+ "Nikosgranturismogt"
+ ]
+ },
+ "maps-desc": "Επιτρέπει την ενσωμάτωση δυναμικών χαρτών σε σελίδες wiki, γεωκωδικοποίηση διευθύνσεων και άλλες γεωγραφικές λειτουργίες",
+ "right-geocode": "Γεωκωδικοποίηση",
+ "action-geocode": "κάνετε γεωκωδικοποίηση σε αυτό το wiki",
+ "maps_map": "Χάρτης",
+ "maps-loading-map": "Φόρτωση χάρτη...",
+ "maps-load-failed": "Δεν ήταν δυνατή η φόρτωση του χάρτη!",
+ "maps-markers": "Σημάδια",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "Φιλτράρισμα σημαδιών",
+ "maps-others": "άλλοι",
+ "maps-ns-layer": "Στρώση",
+ "maps-ns-layer-talk": "Συζήτηση στρώσης",
+ "maps-layer-property": "Ιδιότητα",
+ "maps-layer-value": "Τιμή",
+ "maps-layer-errors": "Σφάλματα",
+ "maps-layerpage-nousage": "Δεν υπάρχουν σελίδες που να χρησιμοποιούν αυτή τη στρώση επί του παρόντος.",
+ "maps-error-invalid-layertype": "Δεν υπάρχουν στρώσεις τύπου «$1». {{PLURAL:$3|Ο μόνος τύπος που υποστηρίζεται είναι αυτός|Οι μόνοι τύποι που υποστηρίζονται είναι αυτοί}}: $2",
+ "maps-error-no-layertype": "Χρειάζεται να καθορίσετε τον τύπο της στρώσης. {{PLURAL:$2|Ο μόνος τύπος που υποστηρίζεται είναι αυτός|Οι μόνοι τύποι που υποστηρίζονται είναι αυτοί}}: $1",
+ "validation-error-invalid-layer": "Η παράμετρος $1 πρέπει να είναι ένα έγκυρο στρώμα.",
+ "validation-error-invalid-layers": "Η παράμετρος $1 πρέπει να είναι ένα ή περισσότερα έγκυρα στρώματα.",
+ "validation-error-no-non-numeric": "Η παράμετρος «$1» πρέπει να είναι μη αριθμητική συμβολοσειρά.",
+ "validation-error-no-non-numerics": "Η παράμετρος «$1» πρέπει να είναι μία ή περισσότερες μη αριθμητικές συμβολοσειρές.",
+ "maps-layer-of-type": "Στρώση τύπου «$1»",
+ "maps-layer-of-type-and-name": "Στρώση «$2» τύπου «$1»",
+ "maps-layer-type-supported-by": "Αυτός ο τύπος στρώσης μπορεί να χρησιμοποιηθεί {{PLURAL:$2|μόνο με την υπηρεσία|με αυτές τις υπηρεσίες}} χαρτογράφησης: $1.",
+ "maps-displaymap-description": "Προβολή γεωγραφικών χαρτών χωρίς σημάδια ορισμένα μέσω wiki.",
+ "maps-coordinates-par-location": "Οι συντεταγμένες που θέλετε να μορφοποιήσετε.",
+ "maps-coordinates-par-format": "Η μορφή προορισμού για τις συντεταγμένες.",
+ "maps-coordinates-par-directional": "Δηλώνει εάν οι συντεταγμένες πρέπει να εκφραστούν κατευθυντικά ή όχι.",
+ "maps-distance-par-unit": "Μονάδα μέτρησης με την οποία να εκφράζεται η απόσταση.",
+ "maps-finddestination-par-location": "Η αρχική τοποθεσία.",
+ "maps-finddestination-par-distance": "Η απόσταση που θα διανυθεί.",
+ "maps-finddestination-par-format": "Μορφή με την οποία να εκφραστεί ο προορισμός.",
+ "maps-finddestination-par-directional": "Δηλώνει εάν η μορφή του προορισμού πρέπει να είναι κατευθυντική ή όχι.",
+ "maps-geocode-par-location": "Η διεύθυνση που θέλετε να γεωκωδικοποιήσετε.",
+ "maps-geocode-par-format": "Η μορφή για τις συντεταγμένες που προκύπτουν.",
+ "maps-geocode-par-directional": "Δηλώνει εάν οι συντεταγμένες πρέπει να εκφραστούν κατευθυντικά ή όχι.",
+ "maps-geodistance-par-location1": "Το πρώτο σημείο στο σύνολο για τον υπολογισμό της απόστασης μεταξύ τους.",
+ "maps-geodistance-par-location2": "Το δεύτερο σημείο στο σύνολο για τον υπολογισμό της απόστασης μεταξύ τους.",
+ "maps-geodistance-par-unit": "Η μονάδα μέτρησης με την οποία να εκφράζεται η απόσταση.",
+ "maps-geodistance-par-decimals": "Ο μέγιστος αριθμός δεκαδικών ψηφίων που να χρησιμοποιούνται στην τιμή που θα προκύπτει.",
+ "maps-displaymap-par-coordinates": "Μία ή περισσότερες τοποθεσίες προς εμφάνιση στο χάρτη. Θα υποδεικνύονται με σημάδι.",
+ "maps-displaymap-par-icon": "Επιτρέπει τον ορισμό του εικονιδίου που χρησιμοποιείται για όλα τα σημάδια.",
+ "maps-displaymap-par-circles": "Κύκλοι προς εμφάνιση",
+ "maps-displaymap-par-copycoords": "Εμφάνιση ενός παραθύρου διαλόγου όταν γίνεται κλικ σε μια τοποθεσία από την οποία οι συντεταγμένες της μπορούν να αντιγραφούν",
+ "maps-displaymap-par-lines": "Γραμμές προς εμφάνιση",
+ "maps-displaymap-par-maxzoom": "Το μέγιστο επίπεδο ζουμ",
+ "maps-displaymap-par-minzoom": "Το ελάχιστο επίπεδο ζουμ",
+ "maps-displaymap-par-polygons": "Πολύγωνα προς εμφάνιση",
+ "maps-displaymap-par-rectangles": "Ορθογώνια προς εμφάνιση",
+ "maps-displaymap-par-static": "Να γίνει ο χάρτης στατικός",
+ "maps-displaymap-par-wmsoverlay": "Χρήση μιας υπέρθεσης WMS",
+ "maps-fullscreen-button": "Εναλλαγή πλήρους οθόνης",
+ "maps-fullscreen-button-tooltip": "Προβολή του χάρτη σε πλήρη οθόνη ή ενσωματωμένου.",
+ "validation-error-invalid-location": "Η παράμετρος «$1» πρέπει να είναι μια έγκυρη τοποθεσία.",
+ "validation-error-invalid-locations": "Η παράμετρος «$1» πρέπει να είναι μία ή περισσότερες έγκυρες τοποθεσίες.",
+ "validation-error-invalid-width": "Η παράμετρος «$1» πρέπει να είναι ένα έγκυρο πλάτος.",
+ "validation-error-invalid-height": "Η παράμετρος «$1» πρέπει να είναι ένα έγκυρο ύψος.",
+ "validation-error-invalid-distance": "Η παράμετρος «$1» πρέπει να είναι μια έγκυρη απόσταση.",
+ "validation-error-invalid-distances": "Η παράμετρος «$1» πρέπει να είναι μία ή περισσότερες έγκυρες αποστάσεις.",
+ "validation-error-invalid-image": "Η παράμετρος «$1» πρέπει να είναι μια έγκυρη εικόνα.",
+ "validation-error-invalid-images": "Η παράμετρος «$1» πρέπει να είναι μία ή περισσότερες έγκυρες εικόνες.",
+ "validation-error-invalid-goverlay": "Η παράμετρος «$1» πρέπει να είναι μια έγκυρη υπέρθεση.",
+ "validation-error-invalid-goverlays": "Η παράμετρος «$1» πρέπει να είναι μία ή περισσότερες έγκυρες υπερθέσεις.",
+ "maps-abb-north": "Β",
+ "maps-abb-east": "Α",
+ "maps-abb-south": "Ν",
+ "maps-abb-west": "Δ",
+ "maps-latitude": "Γεωγραφικό πλάτος:",
+ "maps-longitude": "Γεωγραφικό μήκος:",
+ "maps-invalid-coordinates": "Η τιμή $1 δεν αναγνωρίστηκε ως έγκυρο σύνολο συντεταγμένων.",
+ "maps_coordinates_missing": "Δεν δόθηκαν συντεταγμένες για τον χάρτη.",
+ "maps_geocoding_failed": "{{PLURAL:$2|Η ακόλουθη διεύθυνση δεν ήταν δυνατό να γεωκωδικοποιηθεί|Οι ακόλουθες διευθύνσεις δεν ήταν δυνατό να γεωκωδικοποιηθούν}}: $1.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|Η ακόλουθη διεύθυνση δεν ήταν δυνατό να γεωκωδικοποιηθεί|Οι ακόλουθες διευθύνσεις δεν ήταν δυνατό να γεωκωδικοποιηθούν}} και {{PLURAL:$2|έχει|έχουν}} παραληφθεί από το χάρτη:\n$1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|Η ακόλουθη συντεταγμένη δεν αναγνωρίστηκε|Οι ακόλουθες συντεταγμένες δεν αναγνωρίστηκαν}}: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|Η ακόλουθη συντεταγμένη δεν αναγνωρίστηκε|Οι ακόλουθες συντεταγμένες δεν αναγνωρίστηκαν}} και {{PLURAL:$2|έχει|έχουν}} παραληφθεί από το χάρτη:\n$1",
+ "maps_map_cannot_be_displayed": "Ο χάρτης δεν μπορεί να προβληθεί.",
+ "maps-geocoder-not-available": "Το χαρακτηριστικό γεωκωδικοποίησης των Χαρτών δεν είναι διαθέσιμο. Η τοποθεσία σας δεν μπορεί να γεωκωδικοποιηθεί.",
+ "maps_click_to_activate": "Κάντε κλικ για να ενεργοποιήσετε το χάρτη",
+ "maps_centred_on": "Ο χάρτης είναι κεντραρισμένος στο $1, $2.",
+ "maps-par-resizable": "Κάνει το χάρτη να μπορεί να αλλάξει μέγεθος σύροντας την κάτω δεξιά γωνία του.",
+ "maps-par-searchmarkers": "Επιτρέπει την αναζήτηση για συγκεκριμένα σημάδια μέσω πεδίου ενσωματωμένου στο χάρτη.",
+ "maps-par-centre": "Η τοποθεσία στην οποία ο χάρτης θα πρέπει να είναι κεντραρισμένος",
+ "maps-par-enable-fullscreen": "Ενεργοποίηση κουμπιού πλήρους οθόνης",
+ "maps-par-kml": "Αρχεία KML για φόρτωση στο χάρτη.",
+ "maps-par-markercluster": "Επιτρέπει τη συγχώνευση πολλαπλών κοντινών σημαδιών σε ένα σημάδι",
+ "maps-googlemaps3-incompatbrowser": "Το πρόγραμμα περιήγησης δεν είναι συμβατό με την έκδοση 3 των Χαρτών Google.",
+ "maps-googlemaps3-par-imageoverlays": "Επιτρέπει την προσθήκη μιας εικόνας προς εμφάνιση στην καθοριζόμενη θέση στο χάρτη.",
+ "maps-googlemaps3-par-type": "Τύπος χάρτη που να εμφανίζεται αρχικά.",
+ "maps-googlemaps3-par-types": "Τύποι χάρτη που θα είναι διαθέσιμοι μέσω της ρύθμισης τύπου.",
+ "maps-googlemaps3-par-layers": "Ειδικές στρώσεις για φόρτωση επάνω στο χάρτη.",
+ "maps-googlemaps3-par-controls": "Στοιχεία ελέγχου για τοποθέτηση στο χάρτη.",
+ "maps-googlemaps3-par-zoomstyle": "Στυλ του στοιχείου ελέγχου του ζουμ.",
+ "maps-googlemaps3-par-typestyle": "Στυλ του στοιχείου ελέγχου του τύπου.",
+ "maps-googlemaps3-par-autoinfowindows": "Αυτόματο άνοιγμα όλων των παραθύρων πληροφοριών μετά τη φόρτωση της σελίδας.",
+ "maps-googlemaps3-par-gkml": "Αρχεία KML που φιλοξενούνται στη Google για φόρτωση στο χάρτη.",
+ "maps-googlemaps3-par-poi": "Εμφάνιση σημείων ενδιαφέροντος.",
+ "mapeditor": "Πρόγραμμα επεξεργασίας χαρτών",
+ "specialpages-group-maps": "Χάρτες",
+ "mapeditor-none-text": "Κανένα",
+ "mapeditor-done-button": "Ολοκληρώθηκε",
+ "mapeditor-remove-button": "Αφαίρεση",
+ "mapeditor-import-button2": "Εισαγωγή",
+ "mapeditor-export-button": "Εξαγωγή κώδικα wiki",
+ "mapeditor-import-button": "Εισαγωγή από κώδικα wiki",
+ "mapeditor-select-button": "Επιλογή αυτού του πολυγώνου",
+ "mapeditor-mapparam-button": "Επεξεργασία παραμέτρων χάρτη",
+ "mapeditor-clear-button": "Εκκαθάριση χάρτη",
+ "mapeditor-code-title": "Κώδικας wiki",
+ "mapeditor-import-title": "Εισαγωγή κώδικα wiki",
+ "mapeditor-form-title": "Επεξεργασία λεπτομερειών",
+ "mapeditor-link-title-switcher-popup-text": "Αναδυόμενο με κείμενο",
+ "mapeditor-link-title-switcher-link-text": "Σύνδεσμος",
+ "mapeditor-form-field-title": "Τίτλος",
+ "mapeditor-form-field-text": "Κείμενο",
+ "mapeditor-form-field-link": "Σύνδεσμος",
+ "mapeditor-form-field-icon": "Εικονίδιο",
+ "mapeditor-form-field-group": "Ομάδα",
+ "mapeditor-form-field-inlinelabel": "Ενδοκειμενική ετικέτα",
+ "mapeditor-form-field-strokecolor": "Χρώμα περιγράμματος",
+ "mapeditor-form-field-strokeopacity": "Αδιαφάνεια περιγράμματος",
+ "mapeditor-form-field-strokeweight": "Πάχος περιγράμματος",
+ "mapeditor-form-field-fillcolor": "Χρώμα γεμίσματος",
+ "mapeditor-form-field-fillopcaity": "Αδιαφάνεια γεμίσματος",
+ "mapeditor-form-field-showonhover": "Εμφάνιση μόνο με την κατάδειξη",
+ "mapeditor-mapparam-title": "Επεξεργασία παραμέτρων χάρτη",
+ "mapeditor-mapparam-defoption": "-Επιλογή παραμέτρου-",
+ "mapeditor-imageoverlay-button": "Προσθήκη υπέρθεσης εικόνας",
+ "mapeditor-form-field-image": "Εικόνα",
+ "mapeditor-imageoverlay-title": "Λεπτομέρειες υπέρθεσης εικόνας",
+ "mapeditor-form-field-visitedicon": "Επισκεφθέν εικονίδιο",
+ "semanticmaps-unrecognizeddistance": "Η τιμή $1 δεν είναι έγκυρη απόσταση.",
+ "semanticmaps-kml-link": "Προβολή του αρχείου KML",
+ "semanticmaps-default-kml-pagelink": "Προβολή σελίδας $1",
+ "semanticmaps-latitude": "Γεωγραφικό πλάτος: $1",
+ "semanticmaps-longitude": "Γεωγραφικό μήκος: $1",
+ "semanticmaps-altitude": "Υψόμετρο: $1",
+ "semanticmaps-forminput-locations": "Τοποθεσίες",
+ "semanticmaps-par-staticlocations": "Μια λίστα με τοποθεσίες για προσθήκη στο χάρτη μαζί με τα ερωτηθέντα δεδομένα. Όπως και με τα display_points, μπορείτε να προσθέσετε τίτλο, περιγραφή και εικονίδιο ανά τοποθεσία χρησιμοποιώντας την περισπωμένη «~» ως διαχωριστικό.",
+ "semanticmaps-par-showtitle": "Εμφάνιση ή μη του τίτλου στο παράθυρο πληροφοριών δείκτη. Η απενεργοποίησή του είναι συχνά χρήσιμη όταν χρησιμοποιείται πρότυπο για τη μορφοποίηση του περιεχομένου του παραθύρου πληροφοριών.",
+ "semanticmaps-par-hidenamespace": "Εμφάνιση τίτλου ονοματοχώρου στο παράθυρο πληροφοριών δείκτη",
+ "semanticmaps-par-centre": "Το κέντρο του χάρτη. Όταν δεν παρέχεται, ο χάρτης θα επιλέξει αυτόματα το βέλτιστο κέντρο για την προβολή όλων των δεικτών επάνω στο χάρτη.",
+ "semanticmaps-par-template": "Πρότυπο για να το χρησιμοποιήσετε για τη μορφοποίηση των περιεχομένων του παραθύρου πληροφοριών.",
+ "semanticmaps-par-geocodecontrol": "Εμφάνιση στοιχείου ελέγχου γεωκωδικοποίησης.",
+ "semanticmaps-par-activeicon": "Εικονίδιο που θα εμφανίζεται αντί του προεπιλεγμένου δείκτη, όταν η ενεργή σελίδα ισούται με το αποτέλεσμα του ερωτήματος",
+ "semanticmaps-kml-text": "Το κείμενο που σχετίζεται με κάθε σελίδα. Παρακάμπτεται από τις πρόσθετες ερωτηθέντες ιδιότητες αν υπάρχουν.",
+ "semanticmaps-kml-title": "Προεπιλεγμένος τίτλος για αποτελέσματα",
+ "semanticmaps-kml-linkabsolute": "Να είναι οι σύνδεσμοι απόλυτοι (ως αντιπαράθεση με τους σχετικούς)",
+ "semanticmaps-kml-pagelinktext": "Το κείμενο που θα χρησιμοποιείται για τους συνδέσμους προς τη σελίδα, στο οποίο το $1 θα αντικαθίσταται από τον τίτλο της σελίδας",
+ "semanticmaps-shapes-improperformat": "Εσφαλμένη μορφοποίηση του $1, ανατρέξτε στην τεκμηρίωση περί μορφοποίησης",
+ "semanticmaps-shapes-missingshape": "Δεν βρέθηκαν σχήματα για το $1, ανατρέξτε στην τεκμηρίωση για διαθέσιμα σχήματα",
+ "validator-type-jsonfile": "κείμενο"
+}
diff --git a/www/wiki/extensions/Maps/i18n/en-gb.json b/www/wiki/extensions/Maps/i18n/en-gb.json
new file mode 100644
index 00000000..af4e4598
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/en-gb.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Bruce89",
+ "Lloffiwr",
+ "Reedy",
+ "Shirayuki"
+ ]
+ },
+ "maps-displaymap-par-centre": "Allows setting the coordinates of the map's centre for display_point(s).\nAccepts both addresses and coordinates.\nWhen this property is not provided, the map will centre itself on the provided marker, or between the provided markers.",
+ "maps-invalid-coordinates": "The value $1 was not recognised as a valid set of coordinates.",
+ "maps_unrecognized_coords": "The following {{PLURAL:$2|coordinate was|coordinates were}} not recognised: $1.",
+ "maps_unrecognized_coords_for": "The following {{PLURAL:$2|coordinate was|coordinates were}} not recognised and {{PLURAL:$2|has|have}} been omitted from the map:\n$1",
+ "maps_centred_on": "Map centred on $1, $2.",
+ "maps-par-centre": "The location on which the map should be centred",
+ "mapeditor-form-field-strokecolor": "Stroke colour",
+ "mapeditor-form-field-fillcolor": "Fill colour",
+ "semanticmaps-par-centre": "The centre of the map. When not provided, the map will automatically pick the optimal centre to display all markers on the map."
+}
diff --git a/www/wiki/extensions/Maps/i18n/en.json b/www/wiki/extensions/Maps/i18n/en.json
new file mode 100644
index 00000000..1b5fa3b2
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/en.json
@@ -0,0 +1,225 @@
+{
+ "@metadata": {
+ "authors": [
+ "Jeroen De Dauw",
+ "Karsten Hoffmeyer (kghbln)"
+ ]
+ },
+ "maps-desc": "Enables embedding of dynamic maps into wiki pages, geocoding of addresses and other geographical operations",
+ "right-geocode": "Geocode",
+ "action-geocode": "do geocoding on this wiki",
+ "maps_map": "Map",
+ "maps-tracking-category": "Pages with a map rendered by the Maps extension",
+ "maps-loading-map": "Loading map...",
+ "maps-load-failed": "Could not load the map!",
+ "maps-markers": "Markers",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "Filter markers",
+ "maps-others": "others",
+ "maps-kml-parsing-failed": "Failed parsing one or more KML files. Usually this happens because of retrieval failure or malformed XML.",
+ "maps-ns-layer": "Layer",
+ "maps-ns-layer-talk": "Layer talk",
+ "maps-layer-property": "Property",
+ "maps-layer-value": "Value",
+ "maps-layer-errors": "Errors",
+ "maps-layerdef-invalid": "Invalid {{PLURAL:$1|definition|definitions}}",
+ "maps-layerdef-invalid-fatal": "Fatal invalid definition",
+ "maps-layerdef-wrong-namespace": "Layer definitions are only valid on pages of namespace \"$1\"",
+ "maps-layerdef-equal-layer-name": "Layer names must be unique within the same layer page. \"$1\" is already being used by another layer.",
+ "maps-layerpage-usage": "Pages with maps using layer \"$1\"",
+ "maps-layerpage-nousage": "No pages are using this layer at the moment.",
+ "maps-error-invalid-layertype": "There are no layers of type \"$1\". Only {{PLURAL:$3|this type is|these types are}} supported: $2",
+ "maps-error-no-layertype": "You need to specify the layer type. {{PLURAL:$2|Only this type is|These types are}} supported: $1",
+ "validation-error-invalid-layer": "Parameter \"$1\" must be a valid layer.",
+ "validation-error-invalid-layers": "Parameter \"$1\" must be one or more valid layers.",
+ "validation-error-no-non-numeric": "Parameter \"$1\" must be a non-numeric string.",
+ "validation-error-no-non-numerics": "Parameter \"$1\" must be one or more non-numeric strings.",
+ "maps-layer-of-type": "Layer of type \"$1\"",
+ "maps-layer-of-type-and-name": "Layer \"$2\" of type \"$1\"",
+ "maps-layer-type-supported-by": "This layer type can {{PLURAL:$2|only be used with the $1 mapping service|be used with these mapping services: $1}}.",
+ "maps-coordinates-description": "Parser hook to format coordinates, from and to any of the supported formats.",
+ "maps-displaymap-description": "Display geographical maps without any wiki-defined markers on them.",
+ "maps-distance-description": "Convert a distance using a certain supported unit to its equivalent using another unit.",
+ "maps-finddestination-description": "Find a destination given a starting point (that can be in any of the supported formats), an initial bearing and a distance.",
+ "maps-geocode-description": "Enables the geocoding of addresses, in other words, turning human readable locations into sets of coordinates. There is support for several geocoding services, which should not be confused with mapping services.",
+ "maps-geodistance-description": "Calculate the geographical distance between two points, from and to any of the supported formats.",
+ "maps-mapsdoc-description": "Display a table with the parameters for a specified mapping service together with their default values and descriptions.",
+ "maps-layerdefinition-description": "Describes a custom layer which can be displayed with other Map functions.",
+ "maps-mapsdoc-par-service": "The mapping service to display parameter documentation for.",
+ "maps-mapsdoc-par-language": "The language in which to display the documentation. If no such translation is available, English will be used instead.",
+ "maps-coordinates-par-location": "The coordinates you want to format.",
+ "maps-coordinates-par-format": "The target format for the coordinates.",
+ "maps-coordinates-par-directional": "Indicates if the coordinates should be outputted directional or not.",
+ "maps-par-scrollwheelzoom": "Indicates if mouse scrolling should be enabled or not.",
+ "maps-distance-par-distance": "The distance to convert to its equivalent with a specified unit.",
+ "maps-distance-par-decimals": "The maximum number of fractional digits to use in the resulting value.",
+ "maps-distance-par-unit": "The unit to output the distance in.",
+ "maps-finddestination-par-location": "The initial location.",
+ "maps-finddestination-par-bearing": "The initial bearing.",
+ "maps-finddestination-par-distance": "The distance to travel.",
+ "maps-finddestination-par-format": "The format in which to output the destination.",
+ "maps-finddestination-par-directional": "Indicates if the format of the destination should be directional or not.",
+ "maps-geocode-par-location": "The address you want to geocode.",
+ "maps-geocode-par-format": "The format for the resulting coordinates.",
+ "maps-geocode-par-directional": "Indicates if the coordinates should be outputted directional or not.",
+ "maps-geodistance-par-location1": "The first point in the set to calculate the distance between.",
+ "maps-geodistance-par-location2": "The second point in the set to calculate the distance between.",
+ "maps-geodistance-par-unit": "The unit to output the distance in.",
+ "maps-geodistance-par-decimals": "The maximum number of fractional digits to use in the resulting value.",
+ "maps-displaymap-par-mappingservice": "Allows setting the mapping service that will be used to generate the map.",
+ "maps-displaymap-par-coordinates": "One or more locations to display on the map. They will be indicated with a marker.",
+ "maps-displaymap-par-visitedicon": "The filename of an image to be used for marker icons after the original markers have been clicked",
+ "maps-displaymap-par-zoom": "Allows setting the zoom level of the map.\nWhen not provided and multiple markers are present on the map, the best fitting zoom will be taken, not the configurable default.",
+ "maps-displaymap-par-centre": "Allows setting the coordinates of the map's center for display_point(s).\nAccepts both addresses and coordinates.\nWhen this property is not provided, the map will center itself on the provided marker, or between the provided markers.",
+ "maps-displaymap-par-title": "Allows setting text that will be displayed in the pop-ups of all markers that do not have a specific title.\nWhen used together with label, the title will be bold and underlined.",
+ "maps-displaymap-par-label": "Allows setting text that will be displayed in the pop-ups of all markers that do not have a specific label.",
+ "maps-displaymap-par-icon": "Allows setting the icon used for all markers.",
+ "maps-displaymap-par-circles": "Circles to display",
+ "maps-displaymap-par-copycoords": "Show a dialog when clicking on a location from which its coordinates may be copied",
+ "maps-displaymap-par-lines": "Lines to display",
+ "maps-displaymap-par-maxzoom": "The maximum zoom level",
+ "maps-displaymap-par-minzoom": "The minimum zoom level",
+ "maps-displaymap-par-polygons": "Polygons to display",
+ "maps-displaymap-par-rectangles": "Rectangles to display",
+ "maps-displaymap-par-static": "Make the map static",
+ "maps-displaymap-par-wmsoverlay": "Use a WMS overlay",
+ "maps-displaymap-par-geojson": "URL of a file or name of the page containing GeoJSON data",
+ "maps-fullscreen-button": "Toggle fullscreen",
+ "maps-fullscreen-button-tooltip": "View the map as fullscreen or embedded.",
+ "validation-error-invalid-location": "Parameter \"$1\" must be a valid location.",
+ "validation-error-invalid-locations": "Parameter \"$1\" must be one or more valid locations.",
+ "validation-error-invalid-width": "Parameter \"$1\" must be a valid width.",
+ "validation-error-invalid-height": "Parameter \"$1\" must be a valid height.",
+ "validation-error-invalid-distance": "Parameter \"$1\" must be a valid distance.",
+ "validation-error-invalid-distances": "Parameter \"$1\" must be one or more valid distances.",
+ "validation-error-invalid-image": "Parameter \"$1\" must be a valid image.",
+ "validation-error-invalid-images": "Parameter \"$1\" must be one or more valid images.",
+ "validation-error-invalid-goverlay": "Parameter \"$1\" must be a valid overlay.",
+ "validation-error-invalid-goverlays": "Parameter \"$1\" must be one or more valid overlays.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "W",
+ "maps-latitude": "Latitude:",
+ "maps-longitude": "Longitude:",
+ "maps-invalid-coordinates": "The value $1 was not recognized as a valid set of coordinates.",
+ "maps_coordinates_missing": "No coordinates provided for the map.",
+ "maps_geocoding_failed": "The following {{PLURAL:$2|address|addresses}} could not be geocoded: $1.",
+ "maps_geocoding_failed_for": "The following {{PLURAL:$2|address|addresses}} could not be geocoded and {{PLURAL:$2|has|have}} been omitted from the map:\n$1",
+ "maps_unrecognized_coords": "The following {{PLURAL:$2|coordinate was|coordinates were}} not recognized: $1.",
+ "maps_unrecognized_coords_for": "The following {{PLURAL:$2|coordinate was|coordinates were}} not recognized and {{PLURAL:$2|has|have}} been omitted from the map:\n$1",
+ "maps_map_cannot_be_displayed": "The map cannot be displayed.",
+ "maps-geocoder-not-available": "The geocoding feature of Maps is not available. Your location cannot be geocoded.",
+ "maps_googlemaps3": "Google Maps v3",
+ "maps_leaflet": "Leaflet",
+ "maps-leaflet-par-defzoom": "Allows setting the default zoom level of the map.",
+ "maps-leaflet-par-layers": "The layers that will be available in the layer selector. The first layer will be shown when the map loads.",
+ "maps-leaflet-par-overlaylayers": "The overlay layers that will be shown when the map loads.",
+ "maps-leaflet-par-maxclusterradius": "The maximum radius that a cluster will cover from the central marker (in pixels).",
+ "maps-leaflet-par-clusterspiderfy": "When you click a cluster at the bottom zoom level we spiderfy it so you can see all of its markers.",
+ "maps_click_to_activate": "Click to activate map",
+ "maps_centred_on": "Map centered on $1, $2.",
+ "maps-par-mappingservice": "Allows setting the mapping service that will be used to generate the map.",
+ "maps-par-resizable": "Makes the map resizable by dragging at its lower right corner.",
+ "maps-par-searchmarkers": "Allows to search for specific markers via a field embedded into the map.",
+ "maps-par-zoom": "The zoom level for the map. For maps with markers this will default to the most zoomed in level that still shows all markers.",
+ "maps-par-width": "Allows setting the width of the map. By default pixels will be assumed as unit, but you can explicitly specify one of these units: px, ex, em, %.",
+ "maps-par-height": "Allows setting the height of the map. By default pixels will be assumed as unit, but you can explicitly specify one of these units: px, ex, em.",
+ "maps-par-centre": "The location on which the map should be centered",
+ "maps-par-enable-fullscreen": "Enable fullscreen button",
+ "maps-par-kml": "KML files to load onto the map.",
+ "maps-par-markercluster": "Allows merging of multiple nearby markers into one marker",
+ "maps-googlemaps3-incompatbrowser": "Your browser is not compatible with Google Maps v3.",
+ "maps-googlemaps3-par-imageoverlays": "Allows adding an image to be shown on the specified location on the map.",
+ "maps-googlemaps3-par-type": "The map type to initially show.",
+ "maps-googlemaps3-par-types": "The map types that will be available via the type control.",
+ "maps-googlemaps3-par-layers": "Special layers to load onto the map.",
+ "maps-googlemaps3-par-controls": "The controls to place on the map.",
+ "maps-googlemaps3-par-zoomstyle": "The style of the zoom control.",
+ "maps-googlemaps3-par-typestyle": "The style of the type control.",
+ "maps-googlemaps3-par-autoinfowindows": "Automatically open all info windows after the page has loaded.",
+ "maps-googlemaps3-par-gkml": "KML files hosted by Google to load onto the map.",
+ "maps-googlemaps3-par-kmlrezoom": "Rezoom the map after the KML layers have been loaded.",
+ "maps-googlemaps3-par-poi": "Show points of interest.",
+ "maps-googlemaps3-par-clustergridsize": "The grid size of a cluster in pixels.",
+ "maps-par-clustermaxzoom": "The maximum zoom level where clusters may exist.",
+ "maps-par-clusterzoomonclick": "Whether the default behaviour of clicking on a cluster is to zoom into it.",
+ "maps-par-maxclusterradius": "The maximum radius that a cluster will cover.",
+ "maps-googlemaps3-par-clusteraveragecenter": "Whether the center of each cluster should be the average of all markers in the cluster.",
+ "maps-googlemaps3-par-clusterminsize": "The minimum number of markers to be in a cluster before the markers are hidden and a count is shown.",
+ "mapeditor": "Map editor",
+ "specialpages-group-maps": "Maps",
+ "mapeditor-parser-error": "An error occurred when parsing metadata. Ignoring user input.",
+ "mapeditor-none-text": "None",
+ "mapeditor-done-button": "Done",
+ "mapeditor-remove-button": "Remove",
+ "mapeditor-import-button2": "Import",
+ "mapeditor-export-button": "Export to wiki code",
+ "mapeditor-import-button": "Import from wiki code",
+ "mapeditor-select-button": "Select this polygon",
+ "mapeditor-mapparam-button": "Edit map parameters",
+ "mapeditor-clear-button": "Clear map",
+ "mapeditor-code-title": "Wiki code",
+ "mapeditor-import-title": "Import wiki code",
+ "mapeditor-import-note": "Please note that the parser expects a very strict format on the wiki code. The code inputted here should match code outputted by the export functionality.",
+ "mapeditor-form-title": "Edit details",
+ "mapeditor-link-title-switcher-popup-text": "Popup with text",
+ "mapeditor-link-title-switcher-link-text": "Link",
+ "mapeditor-form-field-title": "Title",
+ "mapeditor-form-field-text": "Text",
+ "mapeditor-form-field-link": "Link",
+ "mapeditor-form-field-icon": "Icon",
+ "mapeditor-form-field-group": "Group",
+ "mapeditor-form-field-inlinelabel": "Inline label",
+ "mapeditor-form-field-strokecolor": "Stroke color",
+ "mapeditor-form-field-strokeopacity": "Stroke opacity",
+ "mapeditor-form-field-strokeweight": "Stroke weight",
+ "mapeditor-form-field-fillcolor": "Fill color",
+ "mapeditor-form-field-fillopcaity": "Fill opacity",
+ "mapeditor-form-field-showonhover": "Show only on hover",
+ "mapeditor-mapparam-title": "Edit map parameters",
+ "mapeditor-mapparam-defoption": "-Select parameter-",
+ "mapeditor-imageoverlay-button": "Add image overlay",
+ "mapeditor-form-field-image": "Image",
+ "mapeditor-imageoverlay-title": "Image overlay details",
+ "mapeditor-form-field-visitedicon": "Visited icon",
+ "semanticmaps-unrecognizeddistance": "The value $1 is not a valid distance.",
+ "semanticmaps-kml-link": "View the KML file",
+ "semanticmaps-kml": "KML",
+ "semanticmaps-default-kml-pagelink": "View page $1",
+ "semanticmaps-latitude": "Latitude: $1",
+ "semanticmaps-longitude": "Longitude: $1",
+ "semanticmaps-altitude": "Altitude: $1",
+ "semanticmaps-forminput-locations": "Locations",
+ "semanticmaps-par-staticlocations": "A list of locations to add to the map together with the queried data. Like with display_points, you can add a title, description and icon per location using the tilde \"~\" as separator.",
+ "semanticmaps-par-showtitle": "Show a title in the marker info window or not. Disabling this is often useful when using a template to format the info window content.",
+ "semanticmaps-par-hidenamespace": "Show the namespace title in the marker info window",
+ "semanticmaps-par-centre": "The center of the map. When not provided, the map will automatically pick the optimal center to display all markers on the map.",
+ "semanticmaps-par-template": "A template to use to format the info window contents.",
+ "semanticmaps-par-geocodecontrol": "Show the geocoding control.",
+ "semanticmaps-par-activeicon": "Icon to be displayed instead of default marker, when active page is equal to query result",
+ "semanticmaps-par-pagelabel": "When set to \"yes\", all markers will have an \"inlineLabel\" with a link to the page containing the coordinates for the marker",
+ "semanticmaps-par-ajaxcoordproperty": "Name of the coordinate property which is used to build the ajax query.",
+ "semanticmaps-par-ajaxquery": "A second query that is sent via ajax to fetch additional coordinates.",
+ "semanticmaps-par-userparam": "A value passed into each template call, if a template is used",
+ "semanticmaps-kml-text": "The text associated with each page. Overridden by the additional queried properties if any.",
+ "semanticmaps-kml-title": "The default title for results",
+ "semanticmaps-kml-linkabsolute": "Should links be absolute (as opposed to relative)",
+ "semanticmaps-kml-pagelinktext": "The text to use for the links to the page, in which $1 will be replaced by the page title",
+ "semanticmaps-shapes-improperformat": "Improper formatting of $1. Please see documentation for formatting",
+ "semanticmaps-shapes-missingshape": "No shapes found for $1. Please see documentation for available shapes",
+ "validator-type-mapscircle": "Geographical circle",
+ "validator-type-mapscircle-list": "List of circles",
+ "validator-type-mapsimageoverlay": "Image overlay",
+ "validator-type-mapsimageoverlay-list": "List of image overlays",
+ "validator-type-mapsline": "Geographical line",
+ "validator-type-mapsline-list": "List of lines",
+ "validator-type-mapslocation": "Geographical location",
+ "validator-type-mapslocation-list": "List of locations",
+ "validator-type-mapsrectangle": "Geographical rectangle",
+ "validator-type-mapsrectangle-list": "List of rectangles",
+ "validator-type-mapspolygon": "Geographical polygon",
+ "validator-type-mapspolygon-list": "List of geographical polygons",
+ "validator-type-wmsoverlay": "Web Map Service overlay",
+ "validator-type-jsonfile": "text"
+}
diff --git a/www/wiki/extensions/Maps/i18n/eo.json b/www/wiki/extensions/Maps/i18n/eo.json
new file mode 100644
index 00000000..cd5be1d8
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/eo.json
@@ -0,0 +1,15 @@
+{
+ "@metadata": {
+ "authors": [
+ "Yekrats"
+ ]
+ },
+ "maps-abb-north": "N",
+ "maps-abb-east": "O",
+ "maps-abb-south": "S",
+ "maps-abb-west": "U",
+ "maps-latitude": "Latitudo:",
+ "maps-longitude": "Longitudo:",
+ "maps_map_cannot_be_displayed": "La mapo ne esti montrebla.",
+ "maps_click_to_activate": "Klaku aktivigi mapon"
+}
diff --git a/www/wiki/extensions/Maps/i18n/es.json b/www/wiki/extensions/Maps/i18n/es.json
new file mode 100644
index 00000000..e9eaa1ed
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/es.json
@@ -0,0 +1,233 @@
+{
+ "@metadata": {
+ "authors": [
+ "Armando-Martin",
+ "Ciencia Al Poder",
+ "Crazymadlover",
+ "Dferg",
+ "Diego Grez",
+ "Fitoschido",
+ "Imre",
+ "Locos epraix",
+ "MarcoAurelio",
+ "Pertile",
+ "Peter17",
+ "Sanbec",
+ "Sethladan",
+ "Translationista",
+ "Vivaelcelta",
+ "Macofe",
+ "Themasterriot",
+ "Mor",
+ "Rafael.minuesa",
+ "Indiralena",
+ "Lemondoge",
+ "Rubentl134",
+ "AlvaroMolina",
+ "Dgstranz",
+ "Carlosmg.dg"
+ ]
+ },
+ "maps-desc": "Habilita la inserción de mapas dinámicos en páginas wikis, la geocodificación de direcciones y otras operaciones geográficas",
+ "right-geocode": "Geocodificar",
+ "action-geocode": "hacer geocodificación en este wiki",
+ "maps_map": "Mapa",
+ "maps-tracking-category": "Páginas con un mapa de la extensión Maps",
+ "maps-loading-map": "Cargando el mapa…",
+ "maps-load-failed": "No se pudo cargar el mapa.",
+ "maps-markers": "Marcadores",
+ "maps-copycoords-prompt": "Ctrl+C, Intro",
+ "maps-searchmarkers-text": "Marcadores de filtro",
+ "maps-others": "otros",
+ "maps-kml-parsing-failed": "Falló el análisis de uno o más archivos KML. Por lo general, esto se debe a fallos de recuperación o a XML con formato incorrecto.",
+ "maps-ns-layer": "Capa",
+ "maps-ns-layer-talk": "Discusión de capa",
+ "maps-layer-property": "Propiedad",
+ "maps-layer-value": "Valor",
+ "maps-layer-errors": "Errores",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Definición no válida|Definiciones no válidas}}",
+ "maps-layerdef-invalid-fatal": "Definición no válida fatal",
+ "maps-layerdef-wrong-namespace": "Las definiciones de capas solo son válidas en el espacio de nombres «$1».",
+ "maps-layerdef-equal-layer-name": "Los nombres de las capas deben ser únicos dentro de la misma página de capas. Otra capa ya está utilizando «$1».",
+ "maps-layerpage-usage": "Páginas con mapas que usan la capa «$1»",
+ "maps-layerpage-nousage": "No hay páginas que usen esta capa en este momento.",
+ "maps-error-invalid-layertype": "No hay capas de tipo \"$1\". Sólo {{PLURAL:$3|este tipo es compatible|estos tipos son compatibles}} :$2",
+ "maps-error-no-layertype": "Es necesario especificar el tipo de capa. {{PLURAL:$2| Solo este tipo es compatible|Estos tipos son compatibles}}:$1",
+ "validation-error-invalid-layer": "El parámetro $1 debe ser una capa válida.",
+ "validation-error-invalid-layers": "El parámetro $1 debe ser una o más capas válidas.",
+ "validation-error-no-non-numeric": "El parámetro «$1» no debe ser una cadena numérica.",
+ "validation-error-no-non-numerics": "El parámetro «$1» debe ser una o más cadenas no numéricas.",
+ "maps-layer-of-type": "Capa de tipo \"$1\"",
+ "maps-layer-of-type-and-name": "Capa «$2» del tipo «$1»",
+ "maps-layer-type-supported-by": "Este tipo de capa solo puede ser utilizado con {{PLURAL:$2|el servicio de mapas $1|estos servicios de mapas: $1 }}.",
+ "maps-coordinates-description": "Marcador del analizador para formatear las coordenadas, desde y hacia cualquiera de los formatos compatibles.",
+ "maps-displaymap-description": "Mostrar mapas geográficos sin ningún marcador definido por el wiki sobre ellos.",
+ "maps-distance-description": "Convertir una distancia definida en alguna unidad admitida en su equivalente en otra unidad.",
+ "maps-finddestination-description": "Encontrar un destino dado un punto de partida (que puede estar en cualquiera de los formatos compatibles), un orientación inicial y una distancia.",
+ "maps-geocode-description": "Permite la geocodificación de direcciones, en otras palabras, transformando las ubicaciones legibles por humanos en conjuntos de coordenadas. No hay soporte para varios servicios de geocodificación, que no deben confundirse con servicios de mapas.",
+ "maps-geodistance-description": "Calcular la distancia geográfica entre dos puntos, desde y hacia cualquiera de los formatos compatibles.",
+ "maps-mapsdoc-description": "Mostrar una tabla con los parámetros para un servicio de mapas especificado junto con sus valores predeterminados y descripciones.",
+ "maps-layerdefinition-description": "Describe una capa personalizada que puede ser visualizada con otras funciones del Mapa.",
+ "maps-mapsdoc-par-service": "El servicio de mapas desde el que mostrar la documentación del parámetro.",
+ "maps-mapsdoc-par-language": "El idioma en el que mostrar la documentación. Si dicha traducción no está disponible, se utilizará el inglés en su lugar.",
+ "maps-coordinates-par-location": "Las coordenadas a las que desee dar formato.",
+ "maps-coordinates-par-format": "El formato de destino de las coordenadas.",
+ "maps-coordinates-par-directional": "Indica si las coordenadas deben producirse con formato direccional o no.",
+ "maps-par-scrollwheelzoom": "Indica si se debe activar o no el desplazamiento con el ratón.",
+ "maps-distance-par-distance": "La distancia a convertir en su equivalente con una unidad especificada.",
+ "maps-distance-par-decimals": "El número máximo de dígitos fraccionarios a usar en el valor resultante.",
+ "maps-distance-par-unit": "La unidad en la que mostrar la distancia.",
+ "maps-finddestination-par-location": "La ubicación inicial.",
+ "maps-finddestination-par-bearing": "La orientación inicial.",
+ "maps-finddestination-par-distance": "La distancia que recorrer.",
+ "maps-finddestination-par-format": "El formato en el que mostrar el destino.",
+ "maps-finddestination-par-directional": "Indica si el formato de destino debe ser direccional o no.",
+ "maps-geocode-par-location": "La dirección que quieres geocodificar.",
+ "maps-geocode-par-format": "El formato de las coordenadas resultantes.",
+ "maps-geocode-par-directional": "Indica si las coordenadas deben producirse con formato direccional o no.",
+ "maps-geodistance-par-location1": "El primer punto del conjunto para calcular la distancia.",
+ "maps-geodistance-par-location2": "El segundo punto en el conjunto para calcular la distancia.",
+ "maps-geodistance-par-unit": "La unidad en la que mostrar la distancia.",
+ "maps-geodistance-par-decimals": "El número máximo de dígitos fraccionarios a usar en el valor resultante.",
+ "maps-displaymap-par-mappingservice": "Permite configurar el servicio de cartografía que se utilizará para generar el mapa.",
+ "maps-displaymap-par-coordinates": "La localización respecto de la cual el mapa aparecerá inicialmente centrado.",
+ "maps-displaymap-par-visitedicon": "El nombre de archivo de una imagen que será usada como iconos de marca luego de hacer clic en los marcadores originales",
+ "maps-displaymap-par-zoom": "Permite configurar el nivel de zoom del mapa.\nCuando no se proporciona y varios marcadores están presentes en el mapa, se tomará el mejor ajuste de zoom, y no el valor configurado por defecto.",
+ "maps-displaymap-par-centre": "Permite establecer las coordenadas del centro del mapa mediante display_point(s).\nAcepta tanto direcciones como coordenadas.\nCuando esta propiedad no se indica, el mapa se centrará en torno al marcador proporcionado, o entre los marcadores proporcionados.",
+ "maps-displaymap-par-title": "Permite configurar el texto que se mostrará en las ventanas emergentes de todos los marcadores que no tienen un título específico.\nCuando se utiliza junto con la etiqueta, el título aparecerá en negrita y subrayado.",
+ "maps-displaymap-par-label": "Permite establecer el texto que se mostrará en las ventanas emergentes de todos los marcadores que no tienen una etiqueta específica.",
+ "maps-displaymap-par-icon": "Permite configurar el icono utilizado para todos los marcadores.",
+ "maps-displaymap-par-circles": "Círculos que mostrar",
+ "maps-displaymap-par-copycoords": "Mostrar un cuadro de diálogo haciendo clic en una ubicación desde el que podrán ser copiadas sus coordenadas",
+ "maps-displaymap-par-lines": "Líneas que mostrar",
+ "maps-displaymap-par-maxzoom": "El nivel máximo de zoom",
+ "maps-displaymap-par-minzoom": "El nivel mínimo de zoom",
+ "maps-displaymap-par-polygons": "Polígonos que mostrar",
+ "maps-displaymap-par-rectangles": "Rectángulos que mostrar",
+ "maps-displaymap-par-static": "Hacer el mapa estático",
+ "maps-displaymap-par-wmsoverlay": "Utilizar una capa WMS",
+ "maps-displaymap-par-geojson": "URL de un archivo GeoJSON",
+ "maps-fullscreen-button": "Activar o desactivar pantalla completa",
+ "maps-fullscreen-button-tooltip": "Ver el mapa en toda la pantalla o incrustado.",
+ "validation-error-invalid-location": "El parámetro \"$1\" deber ser una ubicación válida.",
+ "validation-error-invalid-locations": "El parámetro \"$1\" debe ser una o más ubicaciones válidas.",
+ "validation-error-invalid-width": "El parámetro «$1» debe ser una anchura válida.",
+ "validation-error-invalid-height": "El parámetro $1 deber ser una altura válida.",
+ "validation-error-invalid-distance": "El parámetro \"$1\" debe ser una distancia válida.",
+ "validation-error-invalid-distances": "El parámetro \"$1\" debe ser una o más distancias válidas.",
+ "validation-error-invalid-image": "El parámetro \"$1\" debe ser una imagen válida.",
+ "validation-error-invalid-images": "El parámetro \"$1\" debe ser una o más imágenes válidas.",
+ "validation-error-invalid-goverlay": "El parámetro \"$1\" debe ser una superposición válida.",
+ "validation-error-invalid-goverlays": "El parámetro \"$1\" debe ser una o más superposiciones válidas.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "O",
+ "maps-latitude": "Latitud:",
+ "maps-longitude": "Longitud:",
+ "maps-invalid-coordinates": "El valor $1 no fue reconocido como un conjunto válido de coordenadas.",
+ "maps_coordinates_missing": "Sin coordenadas provistas para el mapa.",
+ "maps_geocoding_failed": "Las siguientes {{PLURAL:$2|dirección|direcciones}} no han podido ser geocodificadas: $1.\nNo se puede mostrar el mapa.",
+ "maps_geocoding_failed_for": "No fue posible geocodificar {{PLURAL:$2|la siguiente dirección, que ha sido omitida|las siguientes direcciones, que han sido omitidas}} del mapa:$1.",
+ "maps_unrecognized_coords": "{{PLURAL:$2|La siguiente coordenada no fue reconocida|Las siguientes coordenadas no fueron reconocidas}}: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|La coordenada siguiente no se reconoce y se ha|Las coordenadas siguientes no se reconocen y se han}} omitido del mapa:\n$1",
+ "maps_map_cannot_be_displayed": "No se puede mostrar el mapa.",
+ "maps-geocoder-not-available": "La funcionalidad de geocodificación de Maps no está disponible. Su ubicación no puede ser geocodificada.",
+ "maps-leaflet-par-defzoom": "Permite establecer el nivel de ampliación predeterminado del mapa.",
+ "maps-leaflet-par-layers": "Las capas que estarán disponibles en el selector de capas. La primera de ellas se mostrará al cargarse el mapa.",
+ "maps_click_to_activate": "Haz clic para activar el mapa",
+ "maps_centred_on": "Mapa centrado en $1, $2.",
+ "maps-par-mappingservice": "Permite configurar el servicio de cartografía que se utilizará para generar el mapa.",
+ "maps-par-resizable": "Hace que se puedan alterar las dimensiones del mapa arrastrando su esquina inferior derecha.",
+ "maps-par-searchmarkers": "Permite buscar marcadores específicos a través de un campo incrustado en el mapa.",
+ "maps-par-zoom": "El nivel de ampliación del mapa. Para mapas con marcadores el valor predeterminado será el máximo que aun muestre todos los marcadores.",
+ "maps-par-width": "Permite establecer la anchura del mapa. De forma predeterminada se emplearán los pixeles como unidad, pero puede especificar explícitamente una de estas unidades: px, ex, em, %.",
+ "maps-par-height": "Permite establecer la altura del mapa. De forma predeterminada se usarán los pixeles como unidad, pero puede especificar explícitamente una de estas unidades: px, ex, em, %.",
+ "maps-par-centre": "La ubicación en la que el mapa debe quedar centrado",
+ "maps-par-enable-fullscreen": "Activar el botón «Pantalla completa»",
+ "maps-par-kml": "Archivos KML para cargar en el mapa.",
+ "maps-par-markercluster": "Permite fusionar múltiples marcadores cercanos en un marcador",
+ "maps-googlemaps3-incompatbrowser": "Su navegador no es compatible con Google Maps v2.",
+ "maps-googlemaps3-par-imageoverlays": "Permite añadir una imagen que mostrar en la ubicación especificada en el mapa.",
+ "maps-googlemaps3-par-type": "El tipo de mapa que mostrar inicialmente.",
+ "maps-googlemaps3-par-types": "Los tipos de mapa que estarán disponibles a través del control de tipo.",
+ "maps-googlemaps3-par-layers": "Capas especiales para cargar en el mapa.",
+ "maps-googlemaps3-par-controls": "Los controles a colocar en el mapa.",
+ "maps-googlemaps3-par-zoomstyle": "El estilo del control de zoom.",
+ "maps-googlemaps3-par-typestyle": "El estilo del control de tipo.",
+ "maps-googlemaps3-par-autoinfowindows": "Abrir automáticamente todas las ventanas de información después de que la página se haya cargado.",
+ "maps-googlemaps3-par-gkml": "Archivos KML alojados por Google para cargar en el mapa.",
+ "maps-googlemaps3-par-kmlrezoom": "Ajustar el nivel de zoom del mapa después de que se hayan cargado las capas KML.",
+ "maps-googlemaps3-par-poi": "Mostrar puntos de interés.",
+ "maps-googlemaps3-par-clustergridsize": "La medida de la cuadrícula de una agrupación en píxeles.",
+ "maps-googlemaps3-par-clusterminsize": "El número mínimo de marcadores que están en una agrupación antes de que los marcadores se oculten y aparezca un recuento.",
+ "mapeditor": "Editor de mapas",
+ "specialpages-group-maps": "Mapas",
+ "mapeditor-parser-error": "Se ha producido un error al analizar los metadatos. Ignorando la entrada del usuario.",
+ "mapeditor-none-text": "Ninguno",
+ "mapeditor-done-button": "Hecho",
+ "mapeditor-remove-button": "Eliminar",
+ "mapeditor-import-button2": "Importar",
+ "mapeditor-export-button": "Exportar a código wiki",
+ "mapeditor-import-button": "Importar desde código wiki",
+ "mapeditor-select-button": "Selecciona este polígono",
+ "mapeditor-mapparam-button": "Editar parámetros del mapa",
+ "mapeditor-clear-button": "Limpiar mapa",
+ "mapeditor-code-title": "Código wiki",
+ "mapeditor-import-title": "Importar código wiki",
+ "mapeditor-import-note": "Tenga en cuenta que el analizador espera un formato muy estricto en el código wiki. El código introducido aquí debe coincidir con el código producido por la funcionalidad de exportación.",
+ "mapeditor-form-title": "Editar los detalles",
+ "mapeditor-link-title-switcher-popup-text": "Ventana emergente con texto",
+ "mapeditor-link-title-switcher-link-text": "Enlace",
+ "mapeditor-form-field-title": "Título",
+ "mapeditor-form-field-text": "Texto",
+ "mapeditor-form-field-link": "Enlace",
+ "mapeditor-form-field-icon": "Icono",
+ "mapeditor-form-field-group": "Grupo",
+ "mapeditor-form-field-inlinelabel": "Etiqueta en línea",
+ "mapeditor-form-field-strokecolor": "Color de trazo",
+ "mapeditor-form-field-strokeopacity": "Opacidad del trazo",
+ "mapeditor-form-field-strokeweight": "Grosor de trazo",
+ "mapeditor-form-field-fillcolor": "Color de relleno",
+ "mapeditor-form-field-fillopcaity": "Opacidad de relleno",
+ "mapeditor-form-field-showonhover": "Mostrar solo al posar el ratón por encima",
+ "mapeditor-mapparam-title": "Editar parámetros del mapa",
+ "mapeditor-mapparam-defoption": "-Seleccionar parámetro-",
+ "mapeditor-imageoverlay-button": "Agregar imagen superpuesta",
+ "mapeditor-form-field-image": "Imagen",
+ "mapeditor-imageoverlay-title": "Detalles de la imagen superpuesta",
+ "mapeditor-form-field-visitedicon": "Icono visitado",
+ "semanticmaps-unrecognizeddistance": "El valor $1 no es una distancia válida.",
+ "semanticmaps-kml-link": "Ver el archivo KML",
+ "semanticmaps-default-kml-pagelink": "Ver página $1",
+ "semanticmaps-latitude": "Latitud: $1",
+ "semanticmaps-longitude": "Longitud: $1",
+ "semanticmaps-altitude": "Altitud: $1",
+ "semanticmaps-forminput-locations": "Ubicaciones",
+ "semanticmaps-par-staticlocations": "Una lista de localizaciones para añadir al mapa junto a los datos consultados. De forma similar a display_points, puede añadir un título, una descripción o un icono por localización usando el signo \"~\" como separador.",
+ "semanticmaps-par-showtitle": "Mostrar o no mostrar un título en la ventana de información del marcador. La desactivación de esto es frecuentemente útil al utilizar una plantilla para dar formato al contenido de la ventana de información.",
+ "semanticmaps-par-hidenamespace": "Mostrar el título del espacio de nombres en la ventana de información del marcador.",
+ "semanticmaps-par-centre": "El centro del mapa. Cuando no se proporciona, el mapa escogerá automáticamente el mejor centro para mostrar todos los marcadores en el mapa.",
+ "semanticmaps-par-template": "Una plantilla a usar para dar formato al contenido de la ventana de información.",
+ "semanticmaps-par-geocodecontrol": "Mostrar el control de geocodificación.",
+ "semanticmaps-par-ajaxcoordproperty": "Nombre de la propiedad de coordenada que se utiliza para crear la consulta AJAX.",
+ "semanticmaps-par-ajaxquery": "Una segunda consulta que se envía mediante AJAX para obtener coordenadas adicionales.",
+ "semanticmaps-par-userparam": "Un valor pasado en cada llamada de plantilla,si una plantilla es usada",
+ "semanticmaps-kml-text": "El texto asociado a cada página. Es substituído por las propiedades recuperadas adicionales, si existen.",
+ "semanticmaps-kml-title": "El título predeterminado de los resultados",
+ "semanticmaps-kml-linkabsolute": "Los enlaces deberían ser absolutos (lo opuesto de relativos)",
+ "semanticmaps-kml-pagelinktext": "El texto a usar para los enlaces a la página, en las que $1 será substituído por el título de la página",
+ "semanticmaps-shapes-improperformat": "Formateo incorrecto de $1, por favor consulta la documentación sobre formateo",
+ "semanticmaps-shapes-missingshape": "No se ha encontrado ninguna forma para $1, por favor consulta la documentación sobre formas disponibles",
+ "validator-type-mapscircle": "Círculo geográfico",
+ "validator-type-mapscircle-list": "Lista de círculos",
+ "validator-type-mapsline": "Línea geográfica",
+ "validator-type-mapsline-list": "Lista de líneas",
+ "validator-type-mapslocation": "Ubicación geográfica",
+ "validator-type-mapslocation-list": "Lista de ubicaciones",
+ "validator-type-mapsrectangle": "Rectángulo geográfico",
+ "validator-type-mapsrectangle-list": "Lista de rectángulos",
+ "validator-type-mapspolygon": "Polígono geográfico",
+ "validator-type-mapspolygon-list": "Lista de polígonos geográficos",
+ "validator-type-jsonfile": "texto"
+}
diff --git a/www/wiki/extensions/Maps/i18n/et.json b/www/wiki/extensions/Maps/i18n/et.json
new file mode 100644
index 00000000..76ae0f48
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/et.json
@@ -0,0 +1,40 @@
+{
+ "@metadata": {
+ "authors": [
+ "Avjoska",
+ "Hendrik",
+ "Pikne"
+ ]
+ },
+ "maps_map": "Kaart",
+ "maps-loading-map": "Kaardi laadimine...",
+ "maps-others": "teised",
+ "maps-geocode-par-location": "Geokodeeritav aadress.",
+ "maps-displaymap-par-maxzoom": "Maksimaalne suumi tase",
+ "maps-displaymap-par-minzoom": "Minimaalne suumi tase",
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "W",
+ "maps-latitude": "Laius:",
+ "maps-longitude": "Pikkus:",
+ "maps_map_cannot_be_displayed": "Kaarti ei saa kuvada.",
+ "maps-osm-par-thumbs": "Näita pisipilte",
+ "maps-osm-par-photos": "Näita fotosid",
+ "mapeditor": "Kaardi toimetaja",
+ "specialpages-group-maps": "Kaardid",
+ "mapeditor-none-text": "Puudub",
+ "mapeditor-done-button": "Valmis",
+ "mapeditor-remove-button": "Eemalda",
+ "mapeditor-import-button2": "Impordi",
+ "mapeditor-clear-button": "Tühjenda kaart",
+ "mapeditor-code-title": "Viki kood",
+ "mapeditor-form-title": "Muuda üksikasju",
+ "mapeditor-link-title-switcher-popup-text": "Hüpikaken tekstiga",
+ "mapeditor-form-field-title": "Pealkiri",
+ "mapeditor-form-field-text": "Tekst",
+ "mapeditor-form-field-link": "Link",
+ "mapeditor-form-field-icon": "Ikoon",
+ "mapeditor-form-field-group": "Rühm",
+ "mapeditor-form-field-image": "Pilt"
+}
diff --git a/www/wiki/extensions/Maps/i18n/eu.json b/www/wiki/extensions/Maps/i18n/eu.json
new file mode 100644
index 00000000..815a4fbd
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/eu.json
@@ -0,0 +1,78 @@
+{
+ "@metadata": {
+ "authors": [
+ "Kobazulo",
+ "පසිඳු කාවින්ද",
+ "Subi",
+ "An13sa",
+ "Mikel Ibaiba"
+ ]
+ },
+ "maps_map": "Mapa",
+ "maps-loading-map": "Mapa kargatzen...",
+ "maps-load-failed": "Ezin da mapa kargatu!",
+ "maps-markers": "Markatzaileak",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "Markatzaileak iragazi",
+ "maps-others": "Bestelakoak",
+ "maps-ns-layer": "Geruza",
+ "maps-layer-property": "Propietatea",
+ "maps-layer-value": "Balioa",
+ "maps-layer-errors": "Erroreak",
+ "maps-layer-of-type": "\"$1\" motako geruza",
+ "maps-layer-of-type-and-name": "\"$2\" geruza \"$1\" motatakoa",
+ "maps-finddestination-par-location": "Hasierako kokapena.",
+ "maps-finddestination-par-distance": "Bidaiatzeko distantzia.",
+ "maps-displaymap-par-circles": "Erakusteko zirkuluak",
+ "maps-displaymap-par-lines": "Lerroak erakusteko",
+ "maps-displaymap-par-polygons": "Erakusteko poligonoak",
+ "maps-displaymap-par-rectangles": "Erakusteko laukizuzenak",
+ "maps-abb-north": "I",
+ "maps-abb-east": "E",
+ "maps-abb-south": "H",
+ "maps-abb-west": "M",
+ "maps-latitude": "Latitudea:",
+ "maps-longitude": "Longitudea:",
+ "maps_coordinates_missing": "Ez dago koordenaturik maparentzat.",
+ "maps_map_cannot_be_displayed": "Ezin da mapa erakutsi.",
+ "maps-googlemaps3-incompatbrowser": "Zure nabigatzailea ez da Google Maps v3-rekin bateragarria.",
+ "maps-googlemaps3-par-poi": "Erakutsi interesguneak",
+ "specialpages-group-maps": "Mapak",
+ "mapeditor-none-text": "Bat ere ez",
+ "mapeditor-done-button": "Egina",
+ "mapeditor-remove-button": "Kendu",
+ "mapeditor-import-button2": "Inportatu",
+ "mapeditor-export-button": "Esportatu wiki kodera",
+ "mapeditor-import-button": "Inportatu wiki kodetik",
+ "mapeditor-select-button": "Hautatu poligono hau",
+ "mapeditor-mapparam-button": "Editatu mapako parametroak",
+ "mapeditor-clear-button": "Garbitu mapa",
+ "mapeditor-code-title": "Wiki kodea",
+ "mapeditor-import-title": "Inportatu wiki kodea",
+ "mapeditor-form-title": "Editatu xehetasunak",
+ "mapeditor-link-title-switcher-link-text": "Lotura",
+ "mapeditor-form-field-title": "Izenburua",
+ "mapeditor-form-field-text": "Testua",
+ "mapeditor-form-field-link": "Lotura",
+ "mapeditor-form-field-icon": "Ikonoa",
+ "mapeditor-form-field-group": "Taldea",
+ "mapeditor-mapparam-title": "Editatu mapako parametroak",
+ "mapeditor-mapparam-defoption": "-Hautatu parametroa-",
+ "mapeditor-form-field-image": "Irudi",
+ "semanticmaps-kml-link": "Ikusi KML fitxategia",
+ "semanticmaps-default-kml-pagelink": "Ikusi $1 orrialdea",
+ "semanticmaps-latitude": "Latitudea: $1",
+ "semanticmaps-longitude": "Longitudea: $1",
+ "semanticmaps-altitude": "Altitudea: $1",
+ "semanticmaps-forminput-locations": "Kokapenak",
+ "validator-type-mapscircle": "Zirkulu geografikoa",
+ "validator-type-mapscircle-list": "Zirkuluen zerrenda",
+ "validator-type-mapsline": "Lerro geografikoa",
+ "validator-type-mapsline-list": "Lerroen zerrenda",
+ "validator-type-mapslocation": "Kokapen geografikoa",
+ "validator-type-mapslocation-list": "Kokapen-zerrenda",
+ "validator-type-mapsrectangle": "Laukizuzen geografikoa",
+ "validator-type-mapsrectangle-list": "Laukizuzenen zerrenda",
+ "validator-type-mapspolygon": "Poligono geografikoa",
+ "validator-type-mapspolygon-list": "Poligono geografikoen zerrenda"
+}
diff --git a/www/wiki/extensions/Maps/i18n/fa.json b/www/wiki/extensions/Maps/i18n/fa.json
new file mode 100644
index 00000000..f3e05a6b
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/fa.json
@@ -0,0 +1,194 @@
+{
+ "@metadata": {
+ "authors": [
+ "Armin1392",
+ "Ebraminio",
+ "Mjbmr",
+ "ZxxZxxZ",
+ "Alirezaaa",
+ "Macofe",
+ "Ommmmid"
+ ]
+ },
+ "maps-desc": "فعال کردن قرار دادن نقشه‌های پویا در صفحات ویکی، رمزگذاری جغرافیایی آدرس‌ها و دیگر عملیات جغرافیایی",
+ "right-geocode": "جئوکد",
+ "maps_map": "نقشه",
+ "maps-tracking-category": "صفحات با نقشهٔ‌ ارائه شده توسط گسترهٔ‌ نقشه‌ها",
+ "maps-loading-map": "در حال بارگیری نقشه...",
+ "maps-load-failed": "قادر به بارگیری نقشه نبود!",
+ "maps-markers": "نشانگرها",
+ "maps-copycoords-prompt": "CTRL+C, وارد کنید",
+ "maps-searchmarkers-text": "نشانگرهای فیلتر",
+ "maps-others": "دیگران",
+ "maps-kml-parsing-failed": "عدم موفقیت تجزیهٔ یک یا چند پوشهٔ کا‌ام‌ال، اغلب به علت عدم بازیابی یا ایکس‌ام‌ال ناقص.",
+ "maps-ns-layer": "لایه",
+ "maps-ns-layer-talk": "بحث لایه",
+ "maps-layer-property": "خاصیت",
+ "maps-layer-value": "مقدار",
+ "maps-layer-errors": "خطاها",
+ "maps-layerdef-invalid": "تعریف نامعتبر{{PLURAL:$1||s}}",
+ "maps-layerdef-invalid-fatal": "تعریف نامعتبر مهلک",
+ "maps-layerdef-wrong-namespace": "تعاریف لایه فقط در صفحات فضای نام \"$1\" معتبر هستند",
+ "maps-layerdef-equal-layer-name": "نام‌های لایه باید داخل صفحهٔ‌ لایهٔ مشابه منحصر به فرد باشد. \"$1\" در حال حاضر توسط لایهٔ دیگری استفاده شده‌.",
+ "maps-layerpage-usage": "صفحات دارای نقشه از لایهٔ‌ \"$1\" استفاده می‌کند",
+ "maps-layerpage-nousage": "هیچ صفحه‌ای در حال حاضر از این لایه استفاده نمی‌کند.",
+ "maps-error-invalid-layertype": "هیچ لایه‌ای از نوع «$1» وجود ندارد. فقط {{PLURAL:$3|این نوع|این نوع‌ها}} پشتیبانی می‌شوند: $2",
+ "maps-error-no-layertype": "باید نوع لایه را مشخص کنید. {{PLURAL:$2|فقط این نوع پشتیبانی می‌شود|این نوع‌ها پشتیبانی می‌شوند}}: $1",
+ "validation-error-invalid-layer": "پارامتر $1 باید یک لایهٔ معتبر باشد.",
+ "validation-error-invalid-layers": "پارامتر $1 باید یک یا چند لایهٔ معتبر باشد.",
+ "validation-error-no-non-numeric": "پارامتر \"$1\" باید یک مجموعهٔ غیرعددی باشد.",
+ "validation-error-no-non-numerics": "پارامتر \"$1\" باید یک یا چند مجموعهٔ‌ غیرعددی باشد.",
+ "maps-layer-of-type": "لایه ای از نوع $1",
+ "maps-layer-of-type-and-name": "لایهٔ \"$2\" از نوع \"$1\"",
+ "maps-layer-type-supported-by": "این نوع لایه می‌تواند {{PLURAL:$2|فقط با سرویس نقشه‌برداری $1 استفاده شود|با این سرویس‌های نقشه‌برداری استفاده شود: $1}}.",
+ "maps-coordinates-description": "قلاب تجزیه کننده برای فرمت مختصات، از و به هر فرمت پشتیبانی شده.",
+ "maps-displaymap-description": "نمایش نقشه‌های جغرافیایی بدون هر نشانگر تعریف شده‌ٔ ویکی بر روی آنها.",
+ "maps-distance-description": "تبدیل یک فاصله با استفاده از یک واحد مشخص پشتیبانی شده برابر با استفاده از واحد دیگری.",
+ "maps-finddestination-description": "پیدا کردن یک مقصد که یک نقطهٔ شروع داده (که می‌تواند در هر فرمت پشتیبانی شده‌ای باشد)، وضع اولیه و یک فاصله.",
+ "maps-geocode-description": "فعل کردن آدرس‌های جئوکدینگ، به عبارت دیگر تبدیل مکان‌های انسانی قابل خواندن به مجموعهٔ مختصات. پشتیبانی برای چندین سرویس های جئوکدینگ، که نباید با سرویس‌های نقشه برداری اشتباه شود، وجود دارد.",
+ "maps-geodistance-description": "محاسبهٔ فاصلهٔ جغرافیایی بین دو نقطه، از و به هر فرمت پشتیبانی شده.",
+ "maps-mapsdoc-description": "نمایش یک جدول با پارامتر‌ها برای سرویس نقشه‌برداری تعیین شده باهم با مقادیر پیش‌فرض آنها و توصیفات.",
+ "maps-layerdefinition-description": "توصیف یک لایهٔ عادت که می‌تواند با دیگر مختصات نقشه نمایش داده شود.",
+ "maps-mapsdoc-par-service": "سرویس نقشه برای نمایش مستندات پارامتر.",
+ "maps-mapsdoc-par-language": "زبانی که برای اسناد نمایش داده می‌شود. اگر چنین ترجمه‌ای در دسترس نسیت، به جای آن انگلیسی استفاده خواهد شد.",
+ "maps-coordinates-par-location": "مختصاتی که می‌خواهید شکل‌بندی کنید.",
+ "maps-coordinates-par-format": "شکل موردنظر برای مختصات.",
+ "maps-coordinates-par-directional": "نمایش دادن اینکه مختصات باید خروجی هدایتی باشد یا نباشد.",
+ "maps-distance-par-distance": "فاصله برای تبدیل به نعادل آن با یک واحد مشخص شده.",
+ "maps-distance-par-decimals": "حداکثر تعداد ارقام کسری برای استفاده در نتیجهٔ ارزش.",
+ "maps-distance-par-unit": "بخشی برای عرضه کردن فاصله.",
+ "maps-finddestination-par-location": "مکان اولیه.",
+ "maps-finddestination-par-bearing": "وضع اولیه.",
+ "maps-finddestination-par-distance": "مسافت برای مسافرت.",
+ "maps-finddestination-par-format": "فرمت برای خروجی مقصد.",
+ "maps-finddestination-par-directional": "نمایش اینکه فرمت مقصد باید هدایتی باشد یا نباشد.",
+ "maps-geocode-par-location": "آدرسی را که برای جئوکد می‌خواهید.",
+ "maps-geocode-par-format": "فرمت برای نتیجهٔ مختصات.",
+ "maps-geocode-par-directional": "نمایش اینکه مختصات باید خروجی هدایتی باشد یا نباشد.",
+ "maps-geodistance-par-location1": "اولین نکته در مجموعه برای برای محاسبهٔ‌ بین فاصله‌ها.",
+ "maps-geodistance-par-location2": "دومین نکته در مجموعه برای برای محاسبهٔ‌ بین فاصله‌ها.",
+ "maps-geodistance-par-unit": "بخشی برای عرضه کردن فاصله.",
+ "maps-geodistance-par-decimals": "حداکثر تعداد ارقام کسری برای استفاده در نتیجهٔ ارزش.",
+ "maps-displaymap-par-mappingservice": "اجازهٔ تنظیم سرویس نقشه‌برداری که برای تولید نقشه استفاده خواهد شد.",
+ "maps-displaymap-par-coordinates": "یک یا چند مکان برای نمایش بر روی یک نقشه. آنها با یک علامت نشان داده خواهد شد.",
+ "maps-displaymap-par-visitedicon": "نام پوشهٔ یک صفحه برای علامت آیکون‌ها که پس از علامت‌های اصلی کلیک شده‌است، مورد استفاده قرار می‌گیرد.",
+ "maps-displaymap-par-zoom": "اجازهٔ تنظیم سطح زوم نقشه.\nهنگامی که ارائه نشده و چند علامت بر روی نقشه ارائه شده، بهترین زوم مناسب گرفته خواهد شد، نه پیش‌فرض قابل پیکربندی.",
+ "maps-displaymap-par-centre": "اجازهٔ تنظیم مختصات مرکز نقشه برای display_point(s).\nآدرس‌ها و مختصات هر دو پذیرفته شدند.\nهنگامی که این ویژگی ارائه نشده‌است، نقشه خود بر روی علامت ارائه شده مرکز، یا بین علامت‌های ارائه شده خواهد بود.",
+ "maps-displaymap-par-title": "تنظیم متنی که در پاپ آپس همهٔ علامت‌هایی که یک عنوان خاص ندارند، نمایش داده خواهد شد.\nهنگامی که با برچسب باهم استفاده شود، عنوان پررنگ‌تر و تأکید دار خواهد بود.",
+ "maps-displaymap-par-label": "اجازهٔ تنظیم متنی که در پاپ آپس همهٔ علامت‌هایی که یک برچسب خاص ندارند، نمایش داده خواهد شد.",
+ "maps-displaymap-par-icon": "اجازهٔ تنظیم آیکون مورد استفاده برای همهٔ علامت‌ها.",
+ "maps-displaymap-par-circles": "حوزه‌ها برای نمایش",
+ "maps-displaymap-par-copycoords": "نمایش یک پنجره محاوره‌ای هنگام کلیک روی یک موقعیت که از مختصاتش می‌تواند نسخه‌برداری شود",
+ "maps-displaymap-par-lines": "خطوط برای نمایش",
+ "maps-displaymap-par-maxzoom": "حداکثر سطح زوم",
+ "maps-displaymap-par-minzoom": "حداقل سطح زوم",
+ "maps-displaymap-par-polygons": "چند ضلعی‌ها برای نمایش",
+ "maps-displaymap-par-rectangles": "مستطیل‌ها برای نمایش",
+ "maps-displaymap-par-static": "اگر نقشه‌ها ایستا باشد",
+ "maps-displaymap-par-wmsoverlay": "استفاده از یک پوشش وی‌ام‌اس",
+ "maps-fullscreen-button": "ضامن تمام صفحه",
+ "maps-fullscreen-button-tooltip": "مشاهدهٔ‌ نقشه به عنوان تمام صفحه یا تعبیه شده.",
+ "validation-error-invalid-location": "پارامتر $1 باید یک مکان معتبر باشد.",
+ "validation-error-invalid-locations": "پارامتر $1 باید یک مکان یا مکان‌های بیشتر معتبری باشد.",
+ "validation-error-invalid-width": "پارامتر $1 باید یک عرض معتبر باشد.",
+ "validation-error-invalid-height": "پارامتر $1 باید یک ارتفاع معتبر باشد.",
+ "validation-error-invalid-distance": "پارامتر $1 باید یک فاصلهٔ‌ معتبر باشد.",
+ "validation-error-invalid-distances": "پارامتر $1 باید یک فاصله یا فاصله‌های بیشتر معتبر باشد.",
+ "validation-error-invalid-image": "پارامتر $1 باید یک عکس معتبر باشد.",
+ "validation-error-invalid-images": "پارامتر $1 باید یک عکس یا عکس‌های بیشتر باشد.",
+ "validation-error-invalid-goverlay": "پارامتر $1 باید یک پوشش معتبر باشد.",
+ "validation-error-invalid-goverlays": "پارامتر $1 باید یک پوشش یا پوشش‌های معتبر باشد.",
+ "maps-abb-north": "شمال",
+ "maps-abb-east": "شرق",
+ "maps-abb-south": "جنوب",
+ "maps-abb-west": "غرب",
+ "maps-latitude": "عرض جغرافیایی:",
+ "maps-longitude": "طول جغرافیایی:",
+ "maps-invalid-coordinates": "مقدار $1 به عنوان یک تنظیم مختصات معتبر شناخته نشده.",
+ "maps_coordinates_missing": "هیچ مختصاتی برای نقشه ارائه نشده.",
+ "maps_geocoding_failed": "{{PLURAL:$2|آدرس|آدرس‌های}} زیر نتوانستند رمزگذاری جغرافیایی شوند: $1.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|آدرس|آدرس‌های}} زیر نتوانستند رمزگذاری جغرافیایی شوند و {{PLURAL:$2|has|have}} از نقشه حذف شده‌است: \n$1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|مختصات بود|مختصات بودند}} زیر شناخته شده نبودند:‌ $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|مختصات بود|مختصات بودند}} زیر نتوانستند شناخته شوند و {{PLURAL:$2|has|have}} از نقشه حذف شده‌است: \n$1",
+ "maps_map_cannot_be_displayed": "نقشه نمی‌تواند نمایش داده شود.",
+ "maps-geocoder-not-available": "رمزگداری ویژگی نقشه در دسترس نیست. مکان شما نمی‌تواند رمزگذاری جغرافیایی شود.",
+ "maps-leaflet-par-layers": "لایه‌هایی که در انتخاب کننده‌ٔ لایه در دسترس خواهند بود. اولین لایه هنگامی نمایش داده خواهد شد که نقشه بازگداری می‌شود.",
+ "maps_click_to_activate": "کلیک کردن برای فعال کردن نقشهٔ.",
+ "maps_centred_on": "نقشهٔ متمرکز شده در $1، $2.",
+ "maps-par-mappingservice": "اجازهٔ تنظیم سرویس نقشه‌برداری که برای تولید نقشه استفاده خواهد شد.",
+ "maps-par-resizable": "درست کردن نقشه‌ای که توسط کشیدن در گوشهٔ پایین سمت راست دوباره قابل ملاحظه باشد.",
+ "maps-par-zoom": "سطح زوم برای نقشه. برای نقشه‌ها با علامت‌های این، برای زوم‌ شدهٔ بیشتر در سطحی که هنوز همهٔ علامت‌ها را نشان می‌دهد،پیش‌فرض خواهد بود.",
+ "maps-par-width": "اجازهٔ تنظیم عرض نقشه. با پیکسل‌های پیش‌فرض، به عنوان واحد فرض خواهد شد، اما شما می‌توانید یکی از این واحدها را به طور واضح مشخص کنید: پی‌ایکس، ای‌ایکس، ای‌ام، ٪.",
+ "maps-par-height": "اجازهٔ تنظیم ارتفاع نقشه. با پیکسل‌های پیش‌فرض، به عنوان واحد فرض خواهد شد، اما شما می‌توانید یکی از این واحدها را به طور واضح مشخص کنید: پی‌ایکس، ای‌ایکس، ای‌ام، ٪.",
+ "maps-par-centre": "مکانی که در نقشه باید مرکز باشد",
+ "maps-par-enable-fullscreen": "فعا کردن دکمهٔ تمام صفحه",
+ "maps-par-kml": "پوشه‌های کا‌ام‌ال برای بارگذاری بر روی نقشه.",
+ "maps-googlemaps3-incompatbrowser": "مرورگر شما با نقشه‌های گوگل وی۳ سازگار نیست.",
+ "maps-googlemaps3-par-type": "نوع نقشه برای ابتدا نشان دادن.",
+ "maps-googlemaps3-par-types": "انواع نقشه‌ای که از طریق کنترل نوع در دسترس خواهد بود.",
+ "maps-googlemaps3-par-layers": "لایه‌های ویژه برای بارگذاری برروی نقشه.",
+ "maps-googlemaps3-par-controls": "کنترل‌ها برای قرار دادن بر روی نقشه.",
+ "maps-googlemaps3-par-zoomstyle": "سبک کنترل زوم.",
+ "maps-googlemaps3-par-typestyle": "سبک کنترل نوع.",
+ "maps-googlemaps3-par-autoinfowindows": "به طور خودکار باز شدن همهٔ صفحات اطلاعات، پس از بارگذاری صفحات.",
+ "maps-googlemaps3-par-gkml": "پوشه‌های کا‌ام‌ال توسط گوگل برای بارگذاری بر روی نقشه میزبانی شده‌.",
+ "maps-googlemaps3-par-kmlrezoom": "زوم مجدد نقشه پس از لایه‌های کا‌ام‌ال که بارگذاری شده‌اند.",
+ "maps-googlemaps3-par-poi": "نمایش نقاط مورد علاقه.",
+ "mapeditor": "تدوین‌گر نقشه",
+ "specialpages-group-maps": "نقشه‌ها",
+ "mapeditor-parser-error": "خطاای هنگام تجزیه‌ٔ فراداده اتفاق افتاد. نادیده گرفتن ورودی کاربر.",
+ "mapeditor-none-text": "هیچ کدام",
+ "mapeditor-done-button": "انجام شد",
+ "mapeditor-remove-button": "حذف",
+ "mapeditor-import-button2": "درون‌ریزی",
+ "mapeditor-export-button": "صادرات به کد ویکی",
+ "mapeditor-import-button": "واردات از کد ویکی",
+ "mapeditor-select-button": "انتخاب این چندضلعی",
+ "mapeditor-mapparam-button": "ویرایش پارامترهای نقشه",
+ "mapeditor-clear-button": "واضح کردن نقشه",
+ "mapeditor-code-title": "کد ویکی",
+ "mapeditor-import-title": "واردات کد ویکی",
+ "mapeditor-import-note": "لطفاً توجه داشته باشید که تجزیه‌کننده، یک فرمت بسیار سخت در کد ویکی انتظار دارد. کد ورودی اینجا باید با کد خروجی توسط اصول صادرات، مطابقت داشته باشد.",
+ "mapeditor-form-title": "ویرایش جزئیات",
+ "mapeditor-link-title-switcher-popup-text": "پوپاپ با متن",
+ "mapeditor-link-title-switcher-link-text": "پیوند",
+ "mapeditor-form-field-title": "عنوان",
+ "mapeditor-form-field-text": "متن",
+ "mapeditor-form-field-link": "پیوند",
+ "mapeditor-form-field-icon": "شمایل",
+ "mapeditor-form-field-group": "گروه",
+ "mapeditor-form-field-inlinelabel": "برچسب اینلاین",
+ "mapeditor-form-field-strokecolor": "رنگ استروک",
+ "mapeditor-form-field-strokeopacity": "کدری استروک",
+ "mapeditor-form-field-strokeweight": "وزن استروک",
+ "mapeditor-form-field-fillcolor": "پر کردن رنگ",
+ "mapeditor-form-field-fillopcaity": "پر کردن کدری",
+ "mapeditor-form-field-showonhover": "نمایش فقط در شناور",
+ "mapeditor-mapparam-title": "ویرایش پارامترهای نقشه",
+ "mapeditor-mapparam-defoption": "-انتخاب پارامتر-",
+ "mapeditor-imageoverlay-button": "افزودن روی هم قرار دادن عکس",
+ "mapeditor-form-field-image": "تصویر",
+ "mapeditor-imageoverlay-title": "جزئیات روی هم قرار دادن عکس",
+ "mapeditor-form-field-visitedicon": "نمادهای بازدید شده",
+ "semanticmaps-unrecognizeddistance": "مقدار $1 یک فاصلهٔ معتبر نیست.",
+ "semanticmaps-kml-link": "مشاهدهٔ پوشهٔ کا‌ام‌ال",
+ "semanticmaps-default-kml-pagelink": "مشاهده صفحهٔ $1",
+ "semanticmaps-latitude": "عرض جغرافیایی: $1",
+ "semanticmaps-longitude": "طول جغرافیایی: $1",
+ "semanticmaps-altitude": "ارتفاع: $1",
+ "semanticmaps-forminput-locations": "مکان‌ها",
+ "semanticmaps-par-staticlocations": "فهرست مکان‌های برای افزودن به نقشه با اطلاعات سوال شده. مانند با display_points، شما می توانید یک عنوان، توصیف و نماد در هر مکان با استفاده از عنوان جدا کننده \"~\".",
+ "semanticmaps-par-showtitle": "نمایش یک عنوان در پنجره اطلاعات نشانگر یا نمایش ندادن آن.غیرفعال کردن این اغلب مفید است هنگام استفاده از الگو برای فرمت کردن محتوای پنجره اطلاعات.",
+ "semanticmaps-par-hidenamespace": "نمایش عنوان فضای نام در پنجره اطلاعات نشانگر",
+ "semanticmaps-par-centre": "مرکز نقشه. هنگامی که ارائه نشد،نقشه به طور خودکار مرکز مطلوب را برای نمایش همه نشانگرها در نقشه انتخاب می‌کند.",
+ "semanticmaps-par-template": "الگو برای استفاده از فرمت محتویات پنجره اطلاعات.",
+ "semanticmaps-par-geocodecontrol": "نشان دادن کنترل جئوکدینگ.",
+ "semanticmaps-par-activeicon": "نماد به جای نشانگر پیش‌فرض نمایش داده می‌شود هنگامی که صفحه فعال برابر با نتیجه پرس‌و‌جوی است",
+ "semanticmaps-par-pagelabel": "هنگامی که به «بله» تنظیم می‌شود، همه نشانگرها، یک «inlineLabel» با یک پیوند به صفحه دارند که شامل مختصات برای نشانگر است",
+ "semanticmaps-kml-text": "متن با هر صفحه مرتبط است. اگر هر خواص پرس‌وجو اضافی وجود داشته باشد، به‌وسیله آن خذف لغو شده.",
+ "semanticmaps-kml-title": "عنوان پیش‌زمینه برای نتایج",
+ "semanticmaps-kml-linkabsolute": "پیوندها باید (به عنوان مخالف به نسبی) مطلق باشند",
+ "semanticmaps-kml-pagelinktext": "متن برای استفاده پیوندها به صفحه، که در آن توسط عنوان صفحه $1 جایگزین خواهد شد",
+ "semanticmaps-shapes-improperformat": "شکل‌بندی نادرست $1. لطفاً مستندات را برای شکل‌بندی مشاهده کنید",
+ "semanticmaps-shapes-missingshape": "هیچ شکلی برای$1 پیدا نشد. لطفاً مستندات را برای شکل‌های دردسترس مشاهده کنید"
+}
diff --git a/www/wiki/extensions/Maps/i18n/fi.json b/www/wiki/extensions/Maps/i18n/fi.json
new file mode 100644
index 00000000..516c2703
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/fi.json
@@ -0,0 +1,158 @@
+{
+ "@metadata": {
+ "authors": [
+ "Beluga",
+ "Cimon Avaro",
+ "Crt",
+ "Nedergard",
+ "Nike",
+ "Str4nd",
+ "ZeiP",
+ "Macofe",
+ "Pahkiqaz",
+ "Pyscowicz"
+ ]
+ },
+ "maps-desc": "Mahdollistaa dynaamisten karttojen upottamisen wikisivuille, osoitteiden geokoodauksen ja muita karttoihin liittyviä toimintoja",
+ "right-geocode": "Geokoodata",
+ "maps_map": "Kartta",
+ "maps-loading-map": "Ladataan karttaa...",
+ "maps-load-failed": "Karttaa ei voitu ladata.",
+ "maps-markers": "Kohdemerkit",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "Suodatuksen kohdemerkit",
+ "maps-others": "muut",
+ "maps-kml-parsing-failed": "Yhden tai useamman kml-tiedoston jäsentäminen epäonnistui. Todennäköinen syy on lataamisen epäonnistuminen tai viallinen XML-syntaksi.",
+ "maps-ns-layer": "Taso",
+ "maps-ns-layer-talk": "Keskustelu tasosta",
+ "maps-layer-property": "Ominaisuus",
+ "maps-layer-value": "Arvo",
+ "maps-layer-errors": "Virheet",
+ "maps-error-invalid-layertype": "Tasotyyppiä ”$1” ei ole. Vain {{PLURAL:$3|tätä tyyppiä|näitä tyyppejä}} tuetaan: $2",
+ "maps-error-no-layertype": "Tasotyyppi on määritettävä. Vain {{PLURAL:$2|tätä tyyppiä|näitä tyyppejä}} tuetaan: $1",
+ "validation-error-invalid-layer": "Parametrin $1 on oltava sallittu taso.",
+ "validation-error-invalid-layers": "Parametrin $1 on oltava yksi tai useampi sallittu taso.",
+ "maps-layer-of-type": "Taso, jonka tyyppi on $1",
+ "maps-layer-type-supported-by": "Tätä tasotyyppiä voidaan käyttää vain, jos {{PLURAL:$2|karttapalveluna on $1}}.",
+ "maps-coordinates-description": "Jäsentimen laajennuskoodi kordinaattien muotoiluun. Mikä tahansa tuettu muoto voi olla lähtö- tai tulomuoto.",
+ "maps-geocode-description": "Mahdollistaa osoitteiden geokoodauksen eli muuntaa selkokieliset sijainnit koordinaattisarjaksi. Käytettävissä on joukko geokoodauspalveluja, joita ei pidä kuitenkaan sekoittaa karttapalveluihin.",
+ "maps-mapsdoc-description": "Näyttää taulukon, jossa on määritetyn karttapalvelun parametrit, niiden oletusarvot ja kuvaukset.",
+ "maps-mapsdoc-par-service": "Karttapalvelu jolle parametridokumentaatio näytetään.",
+ "maps-mapsdoc-par-language": "Ohjeistuksen kieli. Jos käännöstä ei ole, näytetään englanninkielinen versio.",
+ "maps-coordinates-par-location": "Muotoiltavat koordinaatit",
+ "maps-coordinates-par-format": "Koordinaattien tulosmuoto.",
+ "maps-distance-par-decimals": "Kohdearvon desimaalien enimmäismäärä.",
+ "maps-distance-par-unit": "Etäisyyden näyttämiseen käytettävä yksikkö.",
+ "maps-finddestination-par-location": "Aloitussijainti.",
+ "maps-finddestination-par-distance": "Kuljettava matka.",
+ "maps-finddestination-par-format": "Kohteen tulostusmuoto",
+ "maps-geocode-par-location": "Geokoodattava osoite.",
+ "maps-geocode-par-format": "Tuloskoordinaattien muoto.",
+ "maps-geocode-par-directional": "Tulostetaanko koordinatit suuntatiedon kanssa vai ei.",
+ "maps-geodistance-par-location1": "Etäisyyden laskemisen ensimmäinen piste.",
+ "maps-geodistance-par-location2": "Etäisyyden laskemisen toinen piste.",
+ "maps-geodistance-par-unit": "Etäisyyden näyttämiseen käytettävä yksikkö.",
+ "maps-geodistance-par-decimals": "Kohdearvon desimaalien enimmäismäärä.",
+ "maps-displaymap-par-mappingservice": "Mahdollistaa kartan tuottavan karttapalvelun määrityksen.",
+ "maps-displaymap-par-coordinates": "Kartalla on yksi tai useampi kohde. Ne osoitetaan kohdemerkillä.",
+ "maps-displaymap-par-zoom": "Mahdollistaa kartan loitonnustason asettamisen.\nJos sitä ei ole asetettu ja kartalla on kohdemerkkejä, loitonnustasoksi valitaan parhaiten sopiva asetusten oletusarvon sijasta.",
+ "maps-displaymap-par-icon": "Mahdollistaa kaikkien kohdemerkkien kuvakkeen asettamisen.",
+ "maps-displaymap-par-circles": "Näytettävät ympyrät",
+ "maps-displaymap-par-lines": "Näytettävät viivat",
+ "maps-displaymap-par-maxzoom": "Suppein loitonnustaso",
+ "maps-displaymap-par-minzoom": "Laajin loitonnustaso",
+ "maps-displaymap-par-polygons": "Näytettävät monikulmiot",
+ "maps-displaymap-par-rectangles": "Näytettävät suorakulmiot",
+ "maps-displaymap-par-static": "Tee kartasta staattinen",
+ "maps-displaymap-par-geojson": "GeoJSON-tiedoston URL-osoite",
+ "maps-fullscreen-button": "Koko näytön tila",
+ "maps-fullscreen-button-tooltip": "Näytä kartta koko näytön tilassa tai upotettuna.",
+ "validation-error-invalid-location": "Parametrin $1 on oltava sallittu sijainti.",
+ "validation-error-invalid-locations": "Parametrin $1 on oltava yksi tai useampi sallittu sijainti.",
+ "validation-error-invalid-width": "Parametrin $1 on oltava sallittu leveys.",
+ "validation-error-invalid-height": "Parametrin $1 on oltava sallittu korkeus.",
+ "validation-error-invalid-distance": "Parametrin $1 on oltava sallittu etäisyys.",
+ "validation-error-invalid-distances": "Parametrin $1 on oltava yksi tai useampi sallittu etäisyys.",
+ "validation-error-invalid-image": "Parametrin $1 on oltava sallittu kuva.",
+ "validation-error-invalid-images": "Parametrin $1 on oltava yksi tai useampi sallittu kuva.",
+ "maps-abb-north": "P",
+ "maps-abb-east": "I",
+ "maps-abb-south": "E",
+ "maps-abb-west": "L",
+ "maps-latitude": "Leveyspiiri",
+ "maps-longitude": "Pituuspiiri",
+ "maps-invalid-coordinates": "Arvoa $1 ei tunnistettu oikeaksi koordinaattisarjaksi.",
+ "maps_coordinates_missing": "Karttaa varten ei tarjottu koordinaatteja.",
+ "maps_geocoding_failed": "{{PLURAL:$2|Seuraavaa osoitetta|Seuraavia osoitteita}} ei voitu geokoodata: $1.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|Seuraavaa osoitetta|Seuraavia osoitteita}} ei voitu geokoodata ja {{PLURAL:$2|se|ne}} on jätetty pois kartasta: $1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|Seuraavaa koordinaattia|Seuraavia koordinaatteja}} ei tunnistettu: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|Seuraavaa koordinaattia|Seuraavia koordinaatteja}} ei tunnistettu ja {{PLURAL:$2|se|ne}} on jätetty pois kartasta:\n$1",
+ "maps_map_cannot_be_displayed": "Karttaa ei voida näyttää.",
+ "maps-geocoder-not-available": "Maps-laajennoksen geokoodausominaisuus ei ole käytössä. Sijaintia ei voitu geokoodata.",
+ "maps_click_to_activate": "Napsauta aktivoidaksesi kartan",
+ "maps_centred_on": "Kartta keskitetty kohtaan $1, $2.",
+ "maps-par-mappingservice": "Mahdollistaa kartan tuottavan karttapalvelun määrityksen.",
+ "maps-par-zoom": "Kartan loitonnustaso. Jos kartalla on kohdemerkkejä, kartta loitonnetaan oletusarvoisesti niin, että kaikki kohdemerkit näkyvät.",
+ "maps-par-width": "Mahdollistaa kartan leveyden asettamisen. Oletusyksikkönä on pikseli, mutta voit erikseen määrittää jonkin seuraavista yksiköistä: px, ex, em, %.",
+ "maps-par-height": "Mahdollistaa kartan korkeuden asettamisen. Oletusyksikkönä on pikseli, mutta voit erikseen määrittää jonkin seuraavista yksiköistä: px, ex, em, %.",
+ "maps-par-centre": "Sijainti, johon kartta keskitetään",
+ "maps-par-enable-fullscreen": "Koko näytön tila -painike",
+ "maps-par-kml": "Kartalle ladattavat KML-tiedostot.",
+ "maps-googlemaps3-incompatbrowser": "Selaimesi ei ole yhteensopiva Google Maps v3:n kanssa.",
+ "maps-googlemaps3-par-type": "Ensimmäiseksi näytettävä karttatyyppi.",
+ "maps-googlemaps3-par-types": "Karttatasovalitsimen käyttämät karttatyypit.",
+ "maps-googlemaps3-par-layers": "Kartalle ladattavat erikoistasot.",
+ "maps-googlemaps3-par-controls": "Kartalle sijoitettavat ohjaimet.",
+ "maps-googlemaps3-par-zoomstyle": "Loitonnusohjaimen tyyli.",
+ "maps-googlemaps3-par-typestyle": "Karttatasovalitsimen tyyli.",
+ "maps-googlemaps3-par-autoinfowindows": "Avaa automaattisesti kaikki tietoikkunat sen jälkeen, kun sivu on ladattu.",
+ "maps-googlemaps3-par-kmlrezoom": "Valitse paras loitonnustaso uudelleen sen jälkeen, kun KML-tasot on ladattu.",
+ "mapeditor": "Kartan muokkausohjelma",
+ "specialpages-group-maps": "Kartat",
+ "mapeditor-parser-error": "Metadatan jäsennys epäonnistui. Käyttäjän antamat tiedot ohitetaan.",
+ "mapeditor-none-text": "Ei mitään",
+ "mapeditor-done-button": "Valmis",
+ "mapeditor-remove-button": "Poista",
+ "mapeditor-import-button2": "Tuo",
+ "mapeditor-export-button": "Vie wikikoodiin",
+ "mapeditor-import-button": "Tuo wikikoodista",
+ "mapeditor-select-button": "Valitse tämä monikulmio",
+ "mapeditor-mapparam-button": "Muokkaa kartan parametreja",
+ "mapeditor-clear-button": "Tyhjennä kartta",
+ "mapeditor-code-title": "Wikikoodi",
+ "mapeditor-import-title": "Tuo wikikoodi",
+ "mapeditor-import-note": "Huomaa, että jäsennin odottaa erittäin täsmällistä wikikoodin muotoa. Kirjoitetun koodin on siksi vastattava vientitoiminnallisuuden tulostamaa koodia.",
+ "mapeditor-form-title": "Muokkaa lisätietoja",
+ "mapeditor-link-title-switcher-popup-text": "Ponnahdusikkuna tekstillä",
+ "mapeditor-link-title-switcher-link-text": "Linkki",
+ "mapeditor-form-field-title": "Otsikko",
+ "mapeditor-form-field-text": "Teksti",
+ "mapeditor-form-field-link": "Linkki",
+ "mapeditor-form-field-icon": "Kuvake",
+ "mapeditor-form-field-group": "Ryhmä",
+ "mapeditor-form-field-fillcolor": "Täyttöväri",
+ "mapeditor-form-field-showonhover": "Näytä työkaluvihjeenä",
+ "mapeditor-mapparam-title": "Kartan parametrien muokkaus",
+ "mapeditor-mapparam-defoption": "-Valitse parametri-",
+ "mapeditor-form-field-image": "Kuva",
+ "semanticmaps-unrecognizeddistance": "Arvoa $1 ei ole sallittu etäisyys.",
+ "semanticmaps-kml-link": "Näytä KLM-tiedosto",
+ "semanticmaps-default-kml-pagelink": "Näytä sivu $1",
+ "semanticmaps-latitude": "Leveyspiiri: $1",
+ "semanticmaps-longitude": "Pituuspiiri: $1",
+ "semanticmaps-altitude": "Korkeus: $1",
+ "semanticmaps-forminput-locations": "Sijainnit",
+ "semanticmaps-par-staticlocations": "Sijaintien luettelo voidaan lisätä karttaan kyselydatan lisäksi. Kuten display_points-parametrissa voit lisätä sijaintikohtaisen otsikon, kuvauksen ja kuvakkeen; erottimena on \"~\".",
+ "semanticmaps-par-showtitle": "Näyttää kohdemerkin tietoikkunan otsikon tai ei. Käytöstä poisto on usein hyödyllistä, jos tietoikkunan sisältö muotoillaan mallineella.",
+ "semanticmaps-par-hidenamespace": "Näytä nimiavaruuden otsikko kohdemerkin tietoikkunassa",
+ "semanticmaps-par-centre": "Kartan keskus. Jos sitä ei määritetä, kartta laskee automaattisesti optimaalisen keskuksen kartalla olevien kohdemerkkien perusteella.",
+ "semanticmaps-par-template": "Tietoikkunan sisällön muotoilussa käytettävä malline.",
+ "semanticmaps-par-geocodecontrol": "Näytä geokoodausohjaimet.",
+ "semanticmaps-kml-text": "Kuhunkin sivuun liittyvä teksti. Jos kyselyllä on lisäominaisuuksia, ne syrjäyttävät tämän.",
+ "semanticmaps-kml-title": "Tulossivun oletusotsikko",
+ "semanticmaps-kml-linkabsolute": "Ovatko linkit absoluuttisia (eivätkä suhteellisia)",
+ "semanticmaps-kml-pagelinktext": "Sivulinkeissä käytettävä teksti, jossa $1 korvataan sivun otsikolla",
+ "semanticmaps-shapes-improperformat": "$1 on muotoiltu väärin. Katso muotoilun dokumentaatiota.",
+ "semanticmaps-shapes-missingshape": "$1: muotoja ei löytynyt. Dokumentaatiossa kerrotaan sallituista muodoista.",
+ "validator-type-mapslocation-list": "Luettelo sijainneista"
+}
diff --git a/www/wiki/extensions/Maps/i18n/fr.json b/www/wiki/extensions/Maps/i18n/fr.json
new file mode 100644
index 00000000..b1984cc9
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/fr.json
@@ -0,0 +1,243 @@
+{
+ "@metadata": {
+ "authors": [
+ "Brunoperel",
+ "Crochet.david",
+ "DavidL",
+ "Gomoko",
+ "Hashar",
+ "IAlex",
+ "Jean-Frédéric",
+ "McDutchie",
+ "Od1n",
+ "Peter17",
+ "PieRRoMaN",
+ "Sherbrooke",
+ "Verdy p",
+ "Wyz",
+ "Weft",
+ "Jonathan1",
+ "Lbayle",
+ "Yasten",
+ "Trial",
+ "Wladek92",
+ "Urhixidur",
+ "Grondin"
+ ]
+ },
+ "maps-desc": "Permet d’inclure des cartes dynamiques dans les pages du wiki, des adresses géo-codées et d'autres opérations géographiques.",
+ "right-geocode": "Géocode",
+ "action-geocode": "géocoder sur ce wiki",
+ "maps_map": "Carte",
+ "maps-tracking-category": "Pages avec une carte provenant de l’extension Maps",
+ "maps-loading-map": "Chargement de la carte...",
+ "maps-load-failed": "Impossible de charger la carte !",
+ "maps-markers": "Marqueurs",
+ "maps-copycoords-prompt": "CTRL+C, ENTRÉE",
+ "maps-searchmarkers-text": "Marqueurs de filtre",
+ "maps-others": "autres",
+ "maps-kml-parsing-failed": "Échec de l’analyse d’un ou plusieurs fichiers KML, le plus souvent à cause d’échecs de récupération ou de XML mal formé.",
+ "maps-ns-layer": "Couche",
+ "maps-ns-layer-talk": "Page de discussion des couches",
+ "maps-layer-property": "Propriété",
+ "maps-layer-value": "Valeur",
+ "maps-layer-errors": "Erreurs",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Définition non valide|Définitions non valides}}",
+ "maps-layerdef-invalid-fatal": "Définition non valide fatale",
+ "maps-layerdef-wrong-namespace": "Les définitions de couche ne sont valides que sur les pages de l’espace de noms « $1 »",
+ "maps-layerdef-equal-layer-name": "Les noms de couche doivent être uniques dans la même page de couche. « $1 » est déjà utilisé par une autre couche.",
+ "maps-layerpage-usage": "Pages avec des cartes utilisant la couche « $1 »",
+ "maps-layerpage-nousage": "Aucune page n’utilise cette couche pour l’instant.",
+ "maps-error-invalid-layertype": "Il n’y a pas de couche de type « $1 ». Seul {{PLURAL:$3|ce type est|ces types sont}} pris en charge : $2",
+ "maps-error-no-layertype": "Vous devez spécifier le type de couche. {{PLURAL:$2|Seul ce type est|Ces types sont}} pris en charge : $1",
+ "validation-error-invalid-layer": "Le paramètre $1 doit être une couche valide.",
+ "validation-error-invalid-layers": "Le paramètre $1 doit être une ou plusieurs couche(s) valide(s).",
+ "validation-error-no-non-numeric": "Le paramètre « $1 » doit être une chaîne non numérique.",
+ "validation-error-no-non-numerics": "Le paramètre « $1 » doit être une ou plusieurs chaînes non numériques.",
+ "maps-layer-of-type": "Couche de type « $1 »",
+ "maps-layer-of-type-and-name": "Couche « $2 » de type « $1 »",
+ "maps-layer-type-supported-by": "Ce type de couche peut {{PLURAL:$2|être utilisé uniquement avec le service de cartographie $1|être utilisé avec les services de cartographie suivants : $1}}.",
+ "maps-coordinates-description": "Crochet de l’analyseur syntaxique pour formater les coordonnées, depuis et vers n’importe quel format pris en charge.",
+ "maps-displaymap-description": "Affiche les cartes géographiques sans aucun marqueur wiki associé.",
+ "maps-distance-description": "Convertit une distance d'une certaine unité prise en charge à son équivalent utilisant une autre unité.",
+ "maps-finddestination-description": "Trouver une destination à partir d'un point de départ donné (qui peut être dans n’importe lequel des formats pris en charge), d'une orientation initiale et d'une distance.",
+ "maps-geocode-description": "Permet le géocodage d'adresses, en d'autres termes, la transformation des positions humainement lisible en ensembles de coordonnées. Plusieurs services de géocodage sont pris en charge, qui ne doivent pas être confondus avec les services de cartographie.",
+ "maps-geodistance-description": "Calculer la distance géographique entre deux points, depuis et vers n'importe quel format pris en charge.",
+ "maps-mapsdoc-description": "Affiche une table avec les paramètres pour un service de cartographie spécifié, avec leurs valeurs par défaut et leur description.",
+ "maps-layerdefinition-description": "Décrit une couche personnalisée qui peut être affichée avec d’autres fonctions de carte.",
+ "maps-mapsdoc-par-service": "Le service de cartographie pour afficher la documentation concernant les paramètres.",
+ "maps-mapsdoc-par-language": "La langue dans laquelle afficher la documentation. Si aucune traduction n'est disponible, l'anglais sera utilisé à la place.",
+ "maps-coordinates-par-location": "Les coordonnées que vous souhaitez formater.",
+ "maps-coordinates-par-format": "Le format cible pour les coordonnées.",
+ "maps-coordinates-par-directional": "Indique si les coordonnées doivent être imprimées directionnellement ou non.",
+ "maps-par-scrollwheelzoom": "Indique si le défilement à la souris doit être ou non activé.",
+ "maps-distance-par-distance": "La distance à convertir dans son équivalent avec une unité spécifiée.",
+ "maps-distance-par-decimals": "Le nombre maximal de chiffres après la virgule à utiliser dans le résultat.",
+ "maps-distance-par-unit": "L'unité utilisée pour afficher la distance.",
+ "maps-finddestination-par-location": "L'emplacement initial.",
+ "maps-finddestination-par-bearing": "La direction initiale.",
+ "maps-finddestination-par-distance": "La distance à parcourir.",
+ "maps-finddestination-par-format": "Le format dans lequel afficher la destination.",
+ "maps-finddestination-par-directional": "Indique si le format de destination doit être directionnel ou non.",
+ "maps-geocode-par-location": "L'adresse que vous souhaitez géocoder.",
+ "maps-geocode-par-format": "Le format des coordonnées qui en résultent.",
+ "maps-geocode-par-directional": "Indique si les coordonnées doivent être imprimées directionnellement ou non.",
+ "maps-geodistance-par-location1": "Le premier point dans le jeu pour à utiliser pour calculer la distance.",
+ "maps-geodistance-par-location2": "Le deuxième point dans le jeu pour à utiliser pour calculer la distance.",
+ "maps-geodistance-par-unit": "L'unité utilisée pour afficher la distance.",
+ "maps-geodistance-par-decimals": "Le nombre maximal de chiffres après la virgule à utiliser dans le résultat.",
+ "maps-displaymap-par-mappingservice": "Permet de définir le service de cartographie qui sera utilisé pour générer la carte.",
+ "maps-displaymap-par-coordinates": "Un ou plusieurs emplacements à afficher sur la carte. Ils seront représentés par un indicateur.",
+ "maps-displaymap-par-visitedicon": "Le nom de fichier d’une image à utiliser comme icône de marquage une fois que les marqueurs d’origine auront été cliqués",
+ "maps-displaymap-par-zoom": "Permet de définir le niveau de zoom de la carte.\nLorsqu'il n'est pas fourni et que plusieurs marqueurs sont présents sur la carte, le meilleur zoom sera pris, mais non pas la valeur par défaut configurable.",
+ "maps-displaymap-par-centre": "Permet de définir les coordonnées du centre de la carte pour display_point(s).\nAccepte les adresses et les coordonnées.\nLorsque cette propriété n'est pas fournie, la carte se recentre sur le marqueur fourni, ou entre les marqueurs fournis.",
+ "maps-displaymap-par-title": "Permet de définir le texte qui sera affiché dans les pop-ups de tous les marqueurs qui ne disposent pas d'un titre spécifique.\nLorsqu'il est utilisé avec le label, le titre sera en gras et souligné.",
+ "maps-displaymap-par-label": "Permet de définir le texte qui sera affiché dans les fenêtres surgissantes de tous les marqueurs qui n'ont pas d'étiquette spécifique.",
+ "maps-displaymap-par-icon": "Permet de définir l'icône utilisée pour tous les marqueurs.",
+ "maps-displaymap-par-circles": "Cercles à afficher",
+ "maps-displaymap-par-copycoords": "Afficher une boîte de dialogue lors du clic sur un emplacement depuis lequel ses coordonnées peuvent être copiées.",
+ "maps-displaymap-par-lines": "Lignes à afficher",
+ "maps-displaymap-par-maxzoom": "Le niveau maximal de grossissement",
+ "maps-displaymap-par-minzoom": "Le niveau minimal de grossissement",
+ "maps-displaymap-par-polygons": "Polygones à afficher",
+ "maps-displaymap-par-rectangles": "Rectangles à afficher",
+ "maps-displaymap-par-static": "Rendre la carte statique",
+ "maps-displaymap-par-wmsoverlay": "Utiliser une superposition WMS",
+ "maps-displaymap-par-geojson": "URL de fichier ou nom de page contenant des données GeoJSON",
+ "maps-fullscreen-button": "Basculer en plein écran",
+ "maps-fullscreen-button-tooltip": "Visualiser la carte en plein écran ou incorporé.",
+ "validation-error-invalid-location": "Le paramètre « $1 » doit être un emplacement valide.",
+ "validation-error-invalid-locations": "Le paramètre « $1 » doit être un ou plusieurs emplacement(s) valide(s).",
+ "validation-error-invalid-width": "Le paramètre « $1 » doit être une largeur valide.",
+ "validation-error-invalid-height": "Le paramètre « $1 » doit être une hauteur valide.",
+ "validation-error-invalid-distance": "Le paramètre « $1 » doit être une distance valide.",
+ "validation-error-invalid-distances": "Le paramètre « $1 » doit être une ou plusieurs distance(s) valide(s).",
+ "validation-error-invalid-image": "Le paramètre « $1 » doit être une image valide.",
+ "validation-error-invalid-images": "Le paramètre « $1 » doit être une ou plusieurs image(s) valide(s).",
+ "validation-error-invalid-goverlay": "Le paramètre « $1 » doit être un recouvrement valide.",
+ "validation-error-invalid-goverlays": "Le paramètre « $1 » doit être un ou plusieurs recouvrement(s) valide(s).",
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "O",
+ "maps-latitude": "Latitude :",
+ "maps-longitude": "Longitude :",
+ "maps-invalid-coordinates": "La valeur $1 n'a pas été reconnue comme un ensemble valide de coordonnées.",
+ "maps_coordinates_missing": "Aucune coordonnée n'a été fournie pour le plan.",
+ "maps_geocoding_failed": "{{PLURAL:$2|L′adresse suivante n'as pu être géocodée|Les adresses suivantes n'ont pas pu être géocodées}} : $1.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|L′adresse suivante n’as pu être géocodée|Les adresses suivantes n’ont pas pu être géocodées}} et {{PLURAL:$2|n’est pas affichée|ne sont pas affichées}} sur le plan : \n$1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|La coordonnée suivante n'a pas été reconnue|Les coordonnées suivantes n'ont pas été reconnues}} : $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|La coordonnée suivante n'a pas été reconnue|Les coordonnées suivantes n'ont pas été reconnues}} et {{PLURAL:$2|a été omise|ont été omises}} sur la carte :\n$1",
+ "maps_map_cannot_be_displayed": "La carte ne peut pas être affichée.",
+ "maps-geocoder-not-available": "La fonctionnalité géocodage des cartes n'est pas disponible. Votre emplacement ne peut être géocodé.",
+ "maps_leaflet": "Dépliant",
+ "maps-leaflet-par-defzoom": "Permet de définir le niveau de zoom par défaut de la carte.",
+ "maps-leaflet-par-layers": "Les couches qui seront disponibles dans le sélecteur de couche. La première est celle qui sera affichée pendant le chargement de la carte.",
+ "maps-leaflet-par-overlaylayers": "Les surcouches qui seront affichées pendant que la carte se charge.",
+ "maps-leaflet-par-maxclusterradius": "Rayon maximal qu'un agrégat peut couvrir en partant du marqueur central (en pixels).",
+ "maps-leaflet-par-clusterspiderfy": "Lorsque vous cliquez sur un cluster à bas niveau de zoom nous l'explicitons afin que vous puissiez voir l'ensemble de ses marqueurs.",
+ "maps_click_to_activate": "Cliquer pour activer la carte",
+ "maps_centred_on": "Carte centrée sur $1, $2.",
+ "maps-par-mappingservice": "Permet de régler le service de cartographie qui sera utilisé pour générer la carte.",
+ "maps-par-resizable": "Rendre la carte redimensionnable en faisant glisser son coin inférieur droit.",
+ "maps-par-searchmarkers": "Permet de rechercher des marqueurs spécifiques via un champ inclus dans la carte.",
+ "maps-par-zoom": "Le niveau de zoom de la carte. Pour les cartes avec des marqueurs, ceci positionne la valeur par défaut du plus grand zoom qui montre encore tous les marqueurs.",
+ "maps-par-width": "Permet de définir la largeur de la carte. Par défaut les pixels seront considérés comme unité, mais vous pouvez spécifier explicitement une de ces unités : px, ex, em, %.",
+ "maps-par-height": "Permet de définir la hauteur de la carte. Par défaut les pixels seront considérés comme unité, mais vous pouvez spécifier explicitement une de ces unités : px, ex, em.",
+ "maps-par-centre": "Le lieu sur lequel la carte devra être centrée",
+ "maps-par-enable-fullscreen": "Activer le bouton plein écran",
+ "maps-par-kml": "Fichiers KML à charger sur la carte.",
+ "maps-par-markercluster": "Autoriser la fusion de plusieurs repères à proximité en un seul repère",
+ "maps-googlemaps3-incompatbrowser": "Votre navigateur n'est pas compatible avec Google Maps v3.",
+ "maps-googlemaps3-par-imageoverlays": "Permet d'ajouter une image à l'emplacement indiqué sur la carte.",
+ "maps-googlemaps3-par-type": "Le type de carte à afficher initialement.",
+ "maps-googlemaps3-par-types": "Les types de carte qui seront disponibles via le contrôle de type.",
+ "maps-googlemaps3-par-layers": "Couches spéciales à charger sur la carte.",
+ "maps-googlemaps3-par-controls": "Les contrôles à placer sur la carte.",
+ "maps-googlemaps3-par-zoomstyle": "Style du contrôle de zoom.",
+ "maps-googlemaps3-par-typestyle": "Style du contrôle de type.",
+ "maps-googlemaps3-par-autoinfowindows": "Ouvrir automatiquement toutes les fenêtres d'information après le chargement de la page.",
+ "maps-googlemaps3-par-gkml": "Les fichiers KML hébergés par Google à charger sur la carte.",
+ "maps-googlemaps3-par-kmlrezoom": "Zoomer de nouveau la carte une fois que les couches KML ont été chargées.",
+ "maps-googlemaps3-par-poi": "Afficher les points d’intérêt.",
+ "maps-googlemaps3-par-clustergridsize": "La taille de la grille (en pixels) d'un agrégat.",
+ "maps-par-clustermaxzoom": "Niveau maximal de zoom pour lequel des agrégats peuvent exister.",
+ "maps-par-clusterzoomonclick": "Si le comportement par défaut de cliquer sur un agrégat est de zoomer.",
+ "maps-par-maxclusterradius": "Le rayon maximal que ce groupe couvrira.",
+ "maps-googlemaps3-par-clusteraveragecenter": "Si le centre de chaque groupe est le barycentre des marqueurs de l'agrégat.",
+ "maps-googlemaps3-par-clusterminsize": "Le nombre minimum de marqueurs dans un agrégat avant que les marqueurs ne soient cachés et qu'un compteur ne les remplace.",
+ "mapeditor": "Éditeur de carte",
+ "specialpages-group-maps": "Cartes",
+ "mapeditor-parser-error": "Une erreur s'est produite lors de l'analyse des métadonnées. Entrées de l'utilisateur ignorées.",
+ "mapeditor-none-text": "Aucun",
+ "mapeditor-done-button": "Fait",
+ "mapeditor-remove-button": "Supprimer",
+ "mapeditor-import-button2": "Importer",
+ "mapeditor-export-button": "Exporter en code wiki",
+ "mapeditor-import-button": "Importer à partir de code wiki",
+ "mapeditor-select-button": "Sélectionner ce polygone",
+ "mapeditor-mapparam-button": "Modifier les paramètres de la carte",
+ "mapeditor-clear-button": "Effacer la carte",
+ "mapeditor-code-title": "Code wiki",
+ "mapeditor-import-title": "Importer le code wiki",
+ "mapeditor-import-note": "Veuillez noter que l'analyseur syntaxique s'attend à un format très strict sur le code wiki. Le code saisi ici doit correspondre au code généré par la fonctionnalité d'exportation.",
+ "mapeditor-form-title": "Modifier les détails",
+ "mapeditor-link-title-switcher-popup-text": "Popup avec texte",
+ "mapeditor-link-title-switcher-link-text": "Lien",
+ "mapeditor-form-field-title": "Titre",
+ "mapeditor-form-field-text": "Texte",
+ "mapeditor-form-field-link": "Lien",
+ "mapeditor-form-field-icon": "Icône",
+ "mapeditor-form-field-group": "Groupe",
+ "mapeditor-form-field-inlinelabel": "Étiquette en ligne",
+ "mapeditor-form-field-strokecolor": "Couleur de trait",
+ "mapeditor-form-field-strokeopacity": "Opacité du trait",
+ "mapeditor-form-field-strokeweight": "Poids du trait",
+ "mapeditor-form-field-fillcolor": "Couleur de remplissage",
+ "mapeditor-form-field-fillopcaity": "Opacité de remplissage",
+ "mapeditor-form-field-showonhover": "Montrer seulement au survol",
+ "mapeditor-mapparam-title": "Modifier les paramètres de la carte",
+ "mapeditor-mapparam-defoption": "-Sélectionnez un paramètre-",
+ "mapeditor-imageoverlay-button": "Ajouter la couverture d'image",
+ "mapeditor-form-field-image": "Image",
+ "mapeditor-imageoverlay-title": "Détails de la couverture d'image",
+ "mapeditor-form-field-visitedicon": "Icône visitée",
+ "semanticmaps-unrecognizeddistance": "La valeur $1 n'est pas une distance valide.",
+ "semanticmaps-kml-link": "Voir le fichier KML",
+ "semanticmaps-default-kml-pagelink": "Voir l’article $1",
+ "semanticmaps-latitude": "Latitude : $1",
+ "semanticmaps-longitude": "Longitude : $1",
+ "semanticmaps-altitude": "Altitude : $1",
+ "semanticmaps-forminput-locations": "Emplacements",
+ "semanticmaps-par-staticlocations": "Une liste des endroits à ajouter à la carte avec les données demandées. Comme avec display_points, vous pouvez ajouter un titre, une description et une icône par emplacement en utilisant le tilde « ~ » comme séparateur.",
+ "semanticmaps-par-showtitle": "Afficher un titre dans la fenêtre d'informations des marqueurs ou non. La désactivation de ceci est souvent utile lorsque vous utilisez un modèle pour formater le contenu de la fenêtre d'informations.",
+ "semanticmaps-par-hidenamespace": "Afficher le titre de l’espace de noms dans la fenêtre d’information du marqueur",
+ "semanticmaps-par-centre": "Le centre de la carte. Lorsqu'il n'est pas fourni, la carte va choisir automatiquement le centre optimal pour afficher tous les marqueurs sur la carte.",
+ "semanticmaps-par-template": "Un modèle à utiliser pour mettre en forme le contenu de la fenêtre d'informations.",
+ "semanticmaps-par-geocodecontrol": "Afficher le contrôle de géocodage.",
+ "semanticmaps-par-activeicon": "Icône à afficher à la place du marqueur par défaut, quand la page active est égale au résultat de la recherche",
+ "semanticmaps-par-pagelabel": "Quand il vaut « oui », tous les marqueurs auront un « inlineLabel » avec un lien vers la page contenant les coordonnées du marqueur",
+ "semanticmaps-par-ajaxcoordproperty": "Nom de la propriété de coordonnées utilisée pour construire la requête Ajax.",
+ "semanticmaps-par-ajaxquery": "Une seconde requête qui est envoyée via Ajax pour récupérer les coordonnées supplémentaires.",
+ "semanticmaps-par-userparam": "Une valeur passée dans chaque appel de modèle, si un modèle est utilisé",
+ "semanticmaps-kml-text": "Le texte associé avec chaque page. Remplacé par des propriétés récupérées supplémentaires s'il y en a.",
+ "semanticmaps-kml-title": "Le titre par défaut pour les résultats",
+ "semanticmaps-kml-linkabsolute": "Si les titres doivent être absolus (au contraire de relatifs)",
+ "semanticmaps-kml-pagelinktext": "Le texte à utiliser pour les liens vers la page, dans lesquels $1 sera remplacé par le titre de la page",
+ "semanticmaps-shapes-improperformat": "Format de $1 incorrect, veuillez vous reporter à la documentation pour le format attendu",
+ "semanticmaps-shapes-missingshape": "Aucune forme trouvée pour $1; veuillez voir dans la documentation les formes disponibles",
+ "validator-type-mapscircle": "Cercle géographique",
+ "validator-type-mapscircle-list": "Liste des cercles",
+ "validator-type-mapsimageoverlay": "Superposition d’image",
+ "validator-type-mapsimageoverlay-list": "Liste des superpositions d’image",
+ "validator-type-mapsline": "Ligne géographique",
+ "validator-type-mapsline-list": "Liste des lignes",
+ "validator-type-mapslocation": "Emplacement géographique",
+ "validator-type-mapslocation-list": "Liste des emplacements",
+ "validator-type-mapsrectangle": "Rectangle géographique",
+ "validator-type-mapsrectangle-list": "Liste des rectangles",
+ "validator-type-mapspolygon": "Polygone géographique",
+ "validator-type-mapspolygon-list": "Liste des polygones géographiques",
+ "validator-type-wmsoverlay": "Surcouche de Service de Carte web",
+ "validator-type-jsonfile": "texte"
+}
diff --git a/www/wiki/extensions/Maps/i18n/frp.json b/www/wiki/extensions/Maps/i18n/frp.json
new file mode 100644
index 00000000..cf05d3a1
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/frp.json
@@ -0,0 +1,84 @@
+{
+ "@metadata": {
+ "authors": [
+ "ChrisPtDe"
+ ]
+ },
+ "right-geocode": "G·eocode",
+ "maps_map": "Mapa",
+ "maps-loading-map": "Chargement de la mapa...",
+ "maps-load-failed": "Empossiblo de chargiér la mapa !",
+ "maps-markers": "Marcors",
+ "maps-copycoords-prompt": "CTRL+C, ENTRÂ",
+ "maps-searchmarkers-text": "Marquiors de filtro",
+ "maps-others": "ôtros",
+ "maps-ns-layer": "Cuche",
+ "maps-ns-layer-talk": "Discussion cuche",
+ "maps-layer-property": "Propriètât",
+ "maps-layer-value": "Valor",
+ "maps-layer-errors": "Èrrors",
+ "validation-error-invalid-layer": "Lo paramètre $1 dêt étre una cuche valida.",
+ "validation-error-invalid-layers": "Lo paramètre $1 dêt étre yona ou ben un mouél de cuches valides.",
+ "maps-layer-of-type": "Cuche de tipo $1",
+ "maps-finddestination-par-location": "Lo premiér emplacement.",
+ "maps-finddestination-par-bearing": "La premiére dirèccion.",
+ "maps-finddestination-par-distance": "La distance a parcorir.",
+ "maps-displaymap-par-circles": "Cèrcllos a fâre vêre",
+ "maps-displaymap-par-lines": "Legnes a fâre vêre",
+ "maps-displaymap-par-maxzoom": "Lo nivél maximon de zoome",
+ "maps-displaymap-par-minzoom": "Lo nivél minimon de zoome",
+ "maps-displaymap-par-polygons": "Poligonos a fâre vêre",
+ "maps-displaymap-par-rectangles": "Rèctangllos a fâre vêre",
+ "maps-displaymap-par-static": "Se la mapa dêt étre statica",
+ "maps-displaymap-par-wmsoverlay": "Empleyér na supèrposicion WMS",
+ "maps-abb-north": "B",
+ "maps-abb-east": "L",
+ "maps-abb-south": "M",
+ "maps-abb-west": "P",
+ "maps-latitude": "Latituda :",
+ "maps-longitude": "Longituda :",
+ "maps_click_to_activate": "Clicâd por activar la mapa",
+ "maps_centred_on": "Mapa centrâ dessus $1, $2.",
+ "maps-googlemaps3-par-poi": "Montrar los pouents d’entèrèt.",
+ "mapeditor": "Changior de mapa",
+ "specialpages-group-maps": "Mapes",
+ "mapeditor-none-text": "Nion",
+ "mapeditor-done-button": "Fêt",
+ "mapeditor-remove-button": "Enlevar",
+ "mapeditor-import-button2": "Importar",
+ "mapeditor-export-button": "Èxportar en code vouiqui",
+ "mapeditor-import-button": "Importar dês lo code vouiqui",
+ "mapeditor-select-button": "Chouèsir ceti poligono",
+ "mapeditor-mapparam-button": "Changiér los paramètros de la mapa",
+ "mapeditor-clear-button": "Èfaciér la mapa",
+ "mapeditor-code-title": "Code vouiqui",
+ "mapeditor-import-title": "Importar lo code vouiqui",
+ "mapeditor-form-title": "Changiér los dètalys",
+ "mapeditor-link-title-switcher-popup-text": "Popup avouéc tèxto",
+ "mapeditor-link-title-switcher-link-text": "Lim",
+ "mapeditor-form-field-title": "Titro",
+ "mapeditor-form-field-text": "Tèxto",
+ "mapeditor-form-field-link": "Lim",
+ "mapeditor-form-field-icon": "Icôna",
+ "mapeditor-form-field-group": "Groupo",
+ "mapeditor-form-field-inlinelabel": "Lambél en legne",
+ "mapeditor-form-field-strokecolor": "Color de trèt",
+ "mapeditor-form-field-strokeopacity": "Opacitât du trèt",
+ "mapeditor-form-field-strokeweight": "Pêds du trèt",
+ "mapeditor-form-field-fillcolor": "Color de remplissâjo",
+ "mapeditor-form-field-fillopcaity": "Opacitât de remplissâjo",
+ "mapeditor-form-field-showonhover": "Fâre vêre solament u survôlo",
+ "mapeditor-mapparam-title": "Changiér los paramètros de la mapa",
+ "mapeditor-mapparam-defoption": "-Chouèsésséd un paramètro-",
+ "mapeditor-imageoverlay-button": "Apondre la cuvèrta d’émâge",
+ "mapeditor-form-field-image": "Émâge",
+ "mapeditor-imageoverlay-title": "Dètalys de la cuvèrta d’émâge",
+ "mapeditor-form-field-visitedicon": "Icôna visitâ",
+ "semanticmaps-unrecognizeddistance": "La valor $1 est pas una distance valida.",
+ "semanticmaps-kml-link": "Vêre lo fichiér KML",
+ "semanticmaps-default-kml-pagelink": "Vêre la pâge $1",
+ "semanticmaps-latitude": "Latituda : $1",
+ "semanticmaps-longitude": "Longituda : $1",
+ "semanticmaps-altitude": "Hôtior : $1",
+ "semanticmaps-forminput-locations": "Emplacements"
+}
diff --git a/www/wiki/extensions/Maps/i18n/fur.json b/www/wiki/extensions/Maps/i18n/fur.json
new file mode 100644
index 00000000..7fec1794
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/fur.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "Klenje"
+ ]
+ },
+ "maps-desc": "Al furnìs la possibilitât di mostrâ i dâts de coordinadis e lis direzions geocodificadis intune mape ([http://mapping.referata.com/wiki/Maps_examples demo]).\nServizis di mapis disponibii: $1",
+ "maps_map": "Mape",
+ "maps_coordinates_missing": "Nissune coordenade furnide pe mape.",
+ "maps_geocoding_failed": "{{PLURAL:$2|La direzion ca sot no pues jessi geocodificade|Lis direzions ca sot no puedin jessi geocodificadis}}: $1.\nLa mape no pues jessi mostrade.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|La direzion|Lis direzions}} ca sot no {{PLURAL:$2|pues|puedin}} jessi {{PLURAL:$2|geocodificade|geocodificadis}} e {{PLURAL:$2|no je mostrade|no son mostradis}} te mape:\n$1"
+}
diff --git a/www/wiki/extensions/Maps/i18n/fy.json b/www/wiki/extensions/Maps/i18n/fy.json
new file mode 100644
index 00000000..0ac2a2c7
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/fy.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Robin0van0der0vliet",
+ "Robin van der Vliet"
+ ]
+ },
+ "maps-layer-property": "Eigenskip",
+ "maps-layer-value": "Wearde",
+ "mapeditor-form-field-image": "Ôfbylding"
+}
diff --git a/www/wiki/extensions/Maps/i18n/ga.json b/www/wiki/extensions/Maps/i18n/ga.json
new file mode 100644
index 00000000..c4bf9b08
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ga.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "පසිඳු කාවින්ද"
+ ]
+ },
+ "mapeditor-none-text": "Tada"
+}
diff --git a/www/wiki/extensions/Maps/i18n/gl.json b/www/wiki/extensions/Maps/i18n/gl.json
new file mode 100644
index 00000000..a69ecd90
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/gl.json
@@ -0,0 +1,220 @@
+{
+ "@metadata": {
+ "authors": [
+ "Gallaecio",
+ "Toliño",
+ "Banjo",
+ "Elisardojm",
+ "Navhy"
+ ]
+ },
+ "maps-desc": "Permite incorporar mapas dinámicos, enderezos xeocodificados e outras operacións xeográficas nas páxinas do wiki",
+ "right-geocode": "Xeocodificar",
+ "action-geocode": "xeocodificar neste wiki",
+ "maps_map": "Mapa",
+ "maps-tracking-category": "Páxinas cun mapa renderizado coa extensión Maps",
+ "maps-loading-map": "Cargando o mapa...",
+ "maps-load-failed": "Non se puido cargar o mapa!",
+ "maps-markers": "Marcadores",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "Filtrar os marcadores",
+ "maps-others": "outros",
+ "maps-kml-parsing-failed": "Erro ao analizar un ou máis ficheiros KML. Normalmente, isto débese a un erro de recuperación ou a que o XML non ten o formato axeitado.",
+ "maps-ns-layer": "Capa",
+ "maps-ns-layer-talk": "Conversa capa",
+ "maps-layer-property": "Propiedade",
+ "maps-layer-value": "Valor",
+ "maps-layer-errors": "Erros",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Definición non válida|Definicións non válidas}}",
+ "maps-layerdef-invalid-fatal": "Definición non válida fatal",
+ "maps-layerdef-wrong-namespace": "As definicións das capas son válidas unicamente nas páxinas do espazo de nomes \"$1\"",
+ "maps-layerdef-equal-layer-name": "Os nomes das capas deben ser únicos dentro da mesma páxina de capa. \"$1\" xa está en uso noutra capa.",
+ "maps-layerpage-usage": "Páxinas con mapas que usan a capa \"$1\"",
+ "maps-layerpage-nousage": "Nestes intres non hai ningunha páxina que use esta capa.",
+ "maps-error-invalid-layertype": "Non existen capas do tipo \"$1\". Só {{PLURAL:$3|está soportado este tipo|están soportados estes tipos}}: $2",
+ "maps-error-no-layertype": "Cómpre especificar o tipo de capa. {{PLURAL:$3|Só está soportado este tipo|Están soportados estes tipos}}: $1",
+ "validation-error-invalid-layer": "O parámetro \"$1\" debe ser unha capa válida.",
+ "validation-error-invalid-layers": "O parámetro \"$1\" debe ser unha ou máis capas válidas.",
+ "validation-error-no-non-numeric": "O parámetro \"$1\" debe ser unha cadea non numérica.",
+ "validation-error-no-non-numerics": "O parámetro \"$1\" debe ser unha ou máis cadeas non numéricas.",
+ "maps-layer-of-type": "Capa de tipo \"$1\"",
+ "maps-layer-of-type-and-name": "Capa \"$2\" de tipo \"$1\"",
+ "maps-layer-type-supported-by": "Este tipo de capa só se pode empregar {{PLURAL:$2|co servizo de mapas $1|con estes servizos de mapas: $1}}.",
+ "maps-coordinates-description": "Asociador do analizador para dar formato ás coordenadas, desde e cara a calquera formato soportado.",
+ "maps-displaymap-description": "Amosar os mapas xeográficos sen marcadores wiki sobre eles.",
+ "maps-distance-description": "Converter unha distancia nunha certa unidade soportada na súa equivalente noutra unidade.",
+ "maps-finddestination-description": "Atopar un destino a partir dun punto de partida (que pode ser en calquera dos formatos soportados), unha orientación inicial e unha distancia.",
+ "maps-geocode-description": "Permite a xeocodificación de enderezos; noutras palabras, transformar as localizacións lexibles por humanos en conxuntos de coordenadas. Hai soporte para diversos servizos de xeocodificación, que non se deben confundir cos servizos de cartografía.",
+ "maps-geodistance-description": "Calcular a distancia xeográfica entre dous puntos, desde e cara a calquera dos formatos soportados.",
+ "maps-mapsdoc-description": "Mostar unha táboa cos parámetros para un servizo de mapas específico, cos seus valores por defecto e as súas descricións.",
+ "maps-layerdefinition-description": "Describe unha capa personalizada que se pode amosar con outras funcións de mapa.",
+ "maps-mapsdoc-par-service": "O servizo de cartografía do que amosar a documentación do parámetro.",
+ "maps-mapsdoc-par-language": "A lingua na que amosar a documentación. Se non houbera tradución dispoñible, amosarase a versión en inglés.",
+ "maps-coordinates-par-location": "As coordenadas ás que quere dar formato.",
+ "maps-coordinates-par-format": "O formato de destino para as coordenadas.",
+ "maps-coordinates-par-directional": "Indica se as coordenadas deben producirse con formato direccional ou non.",
+ "maps-par-scrollwheelzoom": "Indica se debe activarse ou non o escorregamento co rato.",
+ "maps-distance-par-distance": "A distancia a converter na súa equivalente nunha unidade especificada.",
+ "maps-distance-par-decimals": "O número máximo de díxitos fraccionarios a utilizar no valor resultante.",
+ "maps-distance-par-unit": "A unidade na que amosar a distancia.",
+ "maps-finddestination-par-location": "A localización inicial.",
+ "maps-finddestination-par-bearing": "A dirección inicial.",
+ "maps-finddestination-par-distance": "A distancia a percorrer.",
+ "maps-finddestination-par-format": "O formato no que amosar o destino.",
+ "maps-finddestination-par-directional": "Indica se o formato de destino debe producirse con formato direccional ou non.",
+ "maps-geocode-par-location": "O enderezo que quere xeocodificar.",
+ "maps-geocode-par-format": "O formato das coordenadas resultantes.",
+ "maps-geocode-par-directional": "Indica se as coordenadas deben producirse con formato direccional ou non.",
+ "maps-geodistance-par-location1": "O primeiro punto do conxunto para calcular a distancia.",
+ "maps-geodistance-par-location2": "O segundo punto do conxunto para calcular a distancia.",
+ "maps-geodistance-par-unit": "A unidade utilizada para amosar a distancia.",
+ "maps-geodistance-par-decimals": "O número máximo de díxitos fraccionarios a utilizar no valor resultante.",
+ "maps-displaymap-par-mappingservice": "Permite configurar o servizo de mapas que se empregará para xerar o mapa.",
+ "maps-displaymap-par-coordinates": "Unha ou máis localizacións a amosar no mapa. Indicaranse cun marcador.",
+ "maps-displaymap-par-visitedicon": "O nome de ficheiro dunha imaxe a utilizar como iconas de marcador despois de premer nos marcadores orixinais",
+ "maps-displaymap-par-zoom": "Permite configurar o nivel de zoom do mapa.\nSe non se proporciona e hai presentes no mapa múltiples marcadores, tomarase o mellor axuste de zoom e non o valor configurado por defecto.",
+ "maps-displaymap-par-centre": "Permite configurar as coordenadas do centro do mapa para display_point(s).\nAdmite tanto enderezos como coordenadas.\nCando non se indica esa propiedade, o mapa centrarase no marcador proporcionado ou entre os marcadores proporcionados.",
+ "maps-displaymap-par-title": "Permite configurar o texto que aparecerá na ventás emerxentes de todos os marcadores que non teñen un título específico.\nAo empregalo conxuntamente coa etiqueta, o título aparecerá en negra e suliñado.",
+ "maps-displaymap-par-label": "Permite configurar o texto que aparecerá na ventás emerxentes de todos os marcadores que non teñen unha etiqueta específica.",
+ "maps-displaymap-par-icon": "Permite configurar a icona usada para todos os marcadores.",
+ "maps-displaymap-par-circles": "Círculos a amosar",
+ "maps-displaymap-par-copycoords": "Ao premer nunha localización, amosar un cadro de diálogo desde o que poder copiar as coordenadas do lugar",
+ "maps-displaymap-par-lines": "Liñas a amosar",
+ "maps-displaymap-par-maxzoom": "O nivel máximo de zoom",
+ "maps-displaymap-par-minzoom": "O nivel mínimo de zoom",
+ "maps-displaymap-par-polygons": "Polígonos a amosar",
+ "maps-displaymap-par-rectangles": "Rectángulos a amosar",
+ "maps-displaymap-par-static": "Facer estático o mapa",
+ "maps-displaymap-par-wmsoverlay": "Utilizar unha sobreposición WMS",
+ "maps-displaymap-par-geojson": "URL dun ficheiro ou nome da páxina que contén datos GeoJSON",
+ "maps-fullscreen-button": "Activar ou desactivar a pantalla completa",
+ "maps-fullscreen-button-tooltip": "Amosar o mapa en pantalla completa ou incrustado.",
+ "validation-error-invalid-location": "O parámetro \"$1\" debe ser unha localización válida.",
+ "validation-error-invalid-locations": "O parámetro \"$1\" debe ser unha ou máis localizacións válidas.",
+ "validation-error-invalid-width": "O parámetro \"$1\" debe ser un largo válido.",
+ "validation-error-invalid-height": "O parámetro \"$1\" debe ser unha altura válida.",
+ "validation-error-invalid-distance": "O parámetro \"$1\" debe ser unha distancia válida.",
+ "validation-error-invalid-distances": "O parámetro \"$1\" debe ser unha ou máis distancias válidas.",
+ "validation-error-invalid-image": "O parámetro \"$1\" debe ser unha imaxe válida.",
+ "validation-error-invalid-images": "O parámetro \"$1\" debe ser unha ou máis imaxes válidas.",
+ "validation-error-invalid-goverlay": "O parámetro \"$1\" debe ser unha capa de sobreposición válida.",
+ "validation-error-invalid-goverlays": "O parámetro \"$1\" debe ser unha ou máis capas de sobreposición válidas.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "L",
+ "maps-abb-south": "S",
+ "maps-abb-west": "O",
+ "maps-latitude": "Latitude:",
+ "maps-longitude": "Lonxitude:",
+ "maps-invalid-coordinates": "O valor $1 non foi recoñecido como un conxunto de coordenadas válido.",
+ "maps_coordinates_missing": "Non se proporcionou ningunha coordenada para o mapa.",
+ "maps_geocoding_failed": "{{PLURAL:$2|O seguinte enderezo non se puido xeocodificar|Os seguintes enderezos non se puideron xeocodificar}}: $1.\nO mapa non se pode amosar.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|O seguinte enderezo non se puido xeocodificar|Os seguintes enderezos non se puideron xeocodificar}} e {{PLURAL:$2|omitiuse|omitíronse}} no mapa: $1.",
+ "maps_unrecognized_coords": "Non se {{PLURAL:$2|recoñeceu a seguinte coordenada|recoñeceron as seguintes coordenadas}}: $1.",
+ "maps_unrecognized_coords_for": "Non se {{PLURAL:$2|recoñeceu a seguinte coordenada|recoñeceron as seguintes coordenadas}} e {{PLURAL:$2|foi omitida|foron omitidas}} do mapa:\n$1",
+ "maps_map_cannot_be_displayed": "O mapa non se pode amosar.",
+ "maps-geocoder-not-available": "A funcionalidade de xeocodificación de mapas non está dispoñible; non se pode xeocodificar a súa situación.",
+ "maps_leaflet": "Folleto",
+ "maps-leaflet-par-defzoom": "Permite definir o nivel de zoom por defecto do mapa.",
+ "maps-leaflet-par-layers": "As capas que estarán dispoñibles no selector de capas. A primeira delas amosarase ó cargarse o mapa.",
+ "maps-leaflet-par-overlaylayers": "As capas de transparencia que se amosarán cando se cargue o mapa.",
+ "maps_click_to_activate": "Prema para activar o mapa",
+ "maps_centred_on": "Mapa centrado en $1, $2.",
+ "maps-par-mappingservice": "Permite configurar o servizo de mapas que se empregará para xerar o mapa.",
+ "maps-par-resizable": "Fai que se poidan alterar as dimensións do mapa, arrastrando o canto inferior dereito.",
+ "maps-par-searchmarkers": "Permite procurar marcadores específicos a través dun campo integrado no mapa.",
+ "maps-par-zoom": "O nivel de zoom no mapa. Para os mapas con marcadores o valor por defecto usará o nivel máximo de zoom que abranga todos os marcadores.",
+ "maps-par-width": "Permite definir o largo do mapa. Por defecto, os píxeles asúmense como unidade, pero tamén pode especificar unha destas unidades: px, ex, em, %.",
+ "maps-par-height": "Permite definir a altura do mapa. Por defecto, os píxeles asúmense como unidade, pero tamén pode especificar unha destas unidades: px, ex, em, %.",
+ "maps-par-centre": "O lugar no que se debe centrar o mapa",
+ "maps-par-enable-fullscreen": "Activar o botón de pantalla completa",
+ "maps-par-kml": "Ficheiros KML que cargar no mapa.",
+ "maps-par-markercluster": "Permite fusionar múltiples marcadores cercanos nun só marcador",
+ "maps-googlemaps3-incompatbrowser": "O seu navegador é compatible co Google Maps v3.",
+ "maps-googlemaps3-par-imageoverlays": "Permite engadir unha imaxe a amosar na situación especificada no mapa.",
+ "maps-googlemaps3-par-type": "O tipo de mapa que amosar inicialmente.",
+ "maps-googlemaps3-par-types": "Os tipos de mapas que estarán dispoñibles a través do control de tipos.",
+ "maps-googlemaps3-par-layers": "As capas especiais que cargar no mapa.",
+ "maps-googlemaps3-par-controls": "Os controis que incluír no mapa.",
+ "maps-googlemaps3-par-zoomstyle": "O estilo do control do zoom.",
+ "maps-googlemaps3-par-typestyle": "O estilo do control do tipo.",
+ "maps-googlemaps3-par-autoinfowindows": "Abrir automaticamente todas as ventás de información tras a carga da páxina.",
+ "maps-googlemaps3-par-gkml": "Ficheiros KML aloxados polo Google que cargar no mapa.",
+ "maps-googlemaps3-par-kmlrezoom": "Axustar o nivel de zoom despois da carga das capas KML.",
+ "maps-googlemaps3-par-poi": "Amosar os puntos de interese.",
+ "maps-googlemaps3-par-clustergridsize": "A medida da reixa dun grupo de píxeles.",
+ "maps-googlemaps3-par-clusterminsize": "O número mínimo de marcadores que están nun grupo antes de que se oculten os marcadores e se amose un contador.",
+ "mapeditor": "Editor de mapa",
+ "specialpages-group-maps": "Mapas",
+ "mapeditor-parser-error": "Houbo un erro ao analizar os metadatos. Ignórase a entrada do usuario.",
+ "mapeditor-none-text": "Ningún",
+ "mapeditor-done-button": "Feito",
+ "mapeditor-remove-button": "Eliminar",
+ "mapeditor-import-button2": "Importar",
+ "mapeditor-export-button": "Exportar en código wiki",
+ "mapeditor-import-button": "Importar a partir de código wiki",
+ "mapeditor-select-button": "Seleccione este polígono",
+ "mapeditor-mapparam-button": "Editar os parámetros do mapa",
+ "mapeditor-clear-button": "Limpar o mapa",
+ "mapeditor-code-title": "Código wiki",
+ "mapeditor-import-title": "Importar o código wiki",
+ "mapeditor-import-note": "Teña en conta que o analizador agarda un formato moi estrito no código wiki. O código introducido aquí debe coincidir co código producido pola funcionalidade de exportación.",
+ "mapeditor-form-title": "Editar os detalles",
+ "mapeditor-link-title-switcher-popup-text": "Ventá emerxente co texto",
+ "mapeditor-link-title-switcher-link-text": "Ligazón",
+ "mapeditor-form-field-title": "Título",
+ "mapeditor-form-field-text": "Texto",
+ "mapeditor-form-field-link": "Ligazón",
+ "mapeditor-form-field-icon": "Icona",
+ "mapeditor-form-field-group": "Grupo",
+ "mapeditor-form-field-inlinelabel": "Etiqueta en liña",
+ "mapeditor-form-field-strokecolor": "Cor do trazo",
+ "mapeditor-form-field-strokeopacity": "Opacidade do trazo",
+ "mapeditor-form-field-strokeweight": "Grosor do trazo",
+ "mapeditor-form-field-fillcolor": "Cor do recheo",
+ "mapeditor-form-field-fillopcaity": "Opacidade do recheo",
+ "mapeditor-form-field-showonhover": "Amosar unicamente ao pasar o rato por riba",
+ "mapeditor-mapparam-title": "Editar os parámetros do mapa",
+ "mapeditor-mapparam-defoption": "-Seleccione un parámetro-",
+ "mapeditor-imageoverlay-button": "Engadir a sobreposición da imaxe",
+ "mapeditor-form-field-image": "Imaxe",
+ "mapeditor-imageoverlay-title": "Detalles da sobreposición da imaxe",
+ "mapeditor-form-field-visitedicon": "Icona visitada",
+ "semanticmaps-unrecognizeddistance": "O valor $1 non é unha distancia válida.",
+ "semanticmaps-kml-link": "Ollar o ficheiro KML",
+ "semanticmaps-default-kml-pagelink": "Ver a páxina \"$1\"",
+ "semanticmaps-latitude": "Latitude: $1",
+ "semanticmaps-longitude": "Lonxitude: $1",
+ "semanticmaps-altitude": "Altitude: $1",
+ "semanticmaps-forminput-locations": "Localizacións",
+ "semanticmaps-par-staticlocations": "Unha lista de localizacións para engadir ao mapa xunto aos datos consultados. Como con display_points, pode engadir un título, unha descrición e mais unha icona por localización mediante o signo \"~\" como separador.",
+ "semanticmaps-par-showtitle": "Amosar ou non un título na ventá de información do marcador. Frecuentemente, desactivar isto é útil ao utilizar un modelo para dar formato ao contido da ventá de información.",
+ "semanticmaps-par-hidenamespace": "Amosar o título do espazo de nomes na ventá de información do marcador",
+ "semanticmaps-par-centre": "O centro do mapa. Cando non se proporciona, o mapa ha escoller automaticamente o mellor centro para amosar tódolos marcadores no mapa.",
+ "semanticmaps-par-template": "Un modelo a empregar para dar formato ao contido da ventá de información.",
+ "semanticmaps-par-geocodecontrol": "Amosar o control de xeocodificación.",
+ "semanticmaps-par-activeicon": "Icona a amosar no canto do marcador predeterminado, cando a páxina é igual ao resultado da pescuda",
+ "semanticmaps-par-pagelabel": "Ao definirse en \"si\", todos os macadores terán un \"inlineLabel\" cunha ligazón cara á páxina que conten as coordenadas do marcador",
+ "semanticmaps-par-ajaxcoordproperty": "Nome da propiedade de coordenadas usada para construír a consulta Ajax.",
+ "semanticmaps-par-ajaxquery": "Unha segunda consulta que é enviada vía Ajax para recuperar as coordenadas adicionais.",
+ "semanticmaps-par-userparam": "O valor pasado en cada chamada de modelo, se se empregase algún modelo",
+ "semanticmaps-kml-text": "O texto asociado a cada páxina. Substituído polas propiedades pescudadas adicionais, se existen.",
+ "semanticmaps-kml-title": "O título por defecto para os resultados",
+ "semanticmaps-kml-linkabsolute": "Se as ligazóns deberían ser absolutas (contrario a relativas)",
+ "semanticmaps-kml-pagelinktext": "O texto a usar para as ligazóns cara á páxina, nas que \"$1\" será substituído polo título da páxina",
+ "semanticmaps-shapes-improperformat": "Formato incorrecto de \"$1\"; consulte a documentación sobre os formatos",
+ "semanticmaps-shapes-missingshape": "Non se atopou forma ningunha para \"$1\"; consulte a documentación sobre as formas dispoñibles",
+ "validator-type-mapscircle": "Círculo xeográfico",
+ "validator-type-mapscircle-list": "Lista de círculos",
+ "validator-type-mapsimageoverlay": "Superposición de imaxe",
+ "validator-type-mapsimageoverlay-list": "Lista de superposicións de imaxe",
+ "validator-type-mapsline": "Liña xeográfica",
+ "validator-type-mapsline-list": "Lista de liñas",
+ "validator-type-mapslocation": "Localización xeográfica",
+ "validator-type-mapslocation-list": "Lista de localizacións",
+ "validator-type-mapsrectangle": "Rectángulo xeográfico",
+ "validator-type-mapsrectangle-list": "Lista de rectángulos",
+ "validator-type-mapspolygon": "Polígono xeográfico",
+ "validator-type-mapspolygon-list": "Lista dos polígonos xeográficos",
+ "validator-type-wmsoverlay": "Transparencia de Servizo de Mapa web",
+ "validator-type-jsonfile": "texto"
+}
diff --git a/www/wiki/extensions/Maps/i18n/grc.json b/www/wiki/extensions/Maps/i18n/grc.json
new file mode 100644
index 00000000..ba3592a8
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/grc.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": [],
+ "maps-abb-north": "Β",
+ "maps-abb-east": "Α",
+ "maps-abb-south": "Ν",
+ "maps-abb-west": "Δ",
+ "maps-latitude": "Πλάτος γεωγραφικόν:",
+ "maps-longitude": "Μῆκος γεωγραφικόν:"
+}
diff --git a/www/wiki/extensions/Maps/i18n/gsw.json b/www/wiki/extensions/Maps/i18n/gsw.json
new file mode 100644
index 00000000..95bb7c9c
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/gsw.json
@@ -0,0 +1,49 @@
+{
+ "@metadata": {
+ "authors": [
+ "Als-Holder"
+ ]
+ },
+ "maps-desc": "Ergänzt d Megligkeit Koordinatedate in Charte un Geocodeadrässe aazzeige. Verfiegbari Chartedienscht: $1. [http://www.mediawiki.org/wiki/Extension:Maps Dokumäntation]. [http://mapping.referata.com/wiki/Maps_examples Demo]",
+ "maps_map": "Charte",
+ "maps-loading-map": "Am Lade vu dr Charte ...",
+ "maps-markers": "Markierige",
+ "maps-others": "anderi",
+ "maps-ns-layer": "Ebeni",
+ "maps-ns-layer-talk": "Ebeni Diskussion",
+ "maps-layer-property": "Eigeschaft",
+ "maps-layer-value": "Wärt",
+ "maps-layer-errors": "Fähler",
+ "maps-error-invalid-layertype": "S git kei Ebene vum Typ „$1“. Nume {{PLURAL:$3|dää Typ wird|die Type wäre}} unterstitzt: $2",
+ "maps-error-no-layertype": "Dr Ebenetyp mueß aagee wäre. Nume {{PLURAL:$2|dää Typ wird|die Typen wäre}} unterstitzt: $1",
+ "validation-error-invalid-layer": "Parameter $1 mueß e giltigi Ebeni syy.",
+ "validation-error-invalid-layers": "Parameter $1 mueß ei oder meh giltigi Ebene syy.",
+ "maps-layer-of-type": "Ebeni vum Typ $1",
+ "maps-layer-type-supported-by": "Dää Ebenetyp cha {{PLURAL:$2|nume bim Kartografidienscht $1 brucht wäre|bi däne Kartografidienscht brucht wäre: $1}}.",
+ "validation-error-invalid-location": "Parameter $1 mueß e giltige Standort syy.",
+ "validation-error-invalid-locations": "Parameter $1 mueß ei oder meh giltigi Standort syy.",
+ "validation-error-invalid-width": "Parameter $1 mueß e giltigi Breiti syy.",
+ "validation-error-invalid-height": "Parameter $1 mueß e giltigi Lengi syy.",
+ "validation-error-invalid-distance": "Parameter $1 mueß e giltigi Entfärnig syy.",
+ "validation-error-invalid-distances": "Parameter $1 mueß ei oder meh giltigi Entfärnige syy.",
+ "validation-error-invalid-image": "Parameter $1 mueß e giltig Bild syy.",
+ "validation-error-invalid-images": "Parameter $1 mueß ei oder meh giltigi Bilder syy.",
+ "validation-error-invalid-goverlay": "Parameter $1 mueß e giltigi Iberlagerig syy.",
+ "validation-error-invalid-goverlays": "Parameter $1 mueß ei oder meh giltigi Iberlagerige syy.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "O",
+ "maps-abb-south": "S",
+ "maps-abb-west": "W",
+ "maps-latitude": "Breiti:",
+ "maps-longitude": "Lengi:",
+ "maps-invalid-coordinates": "Dr Wärt $1 isch nit erkännt wore as giltige Satz vu Koordinate.",
+ "maps_coordinates_missing": "S git kei Koordinate fir die Charte.",
+ "maps_geocoding_failed": "Die {{PLURAL:$2|Adräss het|Adräss hän}} nit chenne georeferänziert wäre: $1. D Charte cha nit aazeigt wäre.",
+ "maps_geocoding_failed_for": "Die {{PLURAL:$2|Adräss het|Adrässe hän}} nit chenne georeferänziert wäre un {{PLURAL:$2|isch|sin}} us dr Charte uusegnuu wore: $1",
+ "maps_unrecognized_coords": "Die {{PLURAL:$2|Koordinate isch|Koordinate sin}} nit erkannt wore: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|Die Koordinate isch nit erkannt wore un isch|Die Koordinate sin nit erkannt wore un sin}} wäge däm uusegnuu wore us dr Charte:\n$1",
+ "maps_map_cannot_be_displayed": "D Charte cha nit aazeigt wäre.",
+ "maps-geocoder-not-available": "S Geokodierigs-Feature vu däre Charte isch nit verfiegbar, Dyy Ort cha nit geokodiert wäre.",
+ "maps_click_to_activate": "Klick go d Charte aktiviere",
+ "maps_centred_on": "Charte zäntriert uf $1, $2."
+}
diff --git a/www/wiki/extensions/Maps/i18n/he.json b/www/wiki/extensions/Maps/i18n/he.json
new file mode 100644
index 00000000..5f99e108
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/he.json
@@ -0,0 +1,217 @@
+{
+ "@metadata": {
+ "authors": [
+ "Amire80",
+ "Ex-Diktator",
+ "Rotemliss",
+ "YaronSh",
+ "Yona b",
+ "Yonidebest",
+ "ערן",
+ "תומר ט",
+ "Inkbug",
+ "Macofe",
+ "Guycn2",
+ "המקיסט",
+ "Erelon"
+ ]
+ },
+ "maps-desc": "הוספת האפשרות להטמעת מפות דינמיות אל תוך דפי ויקי, קידוד גאוגרפי של כתובות ופעולות גאוגרפיות אחרות",
+ "right-geocode": "ביצוע קידודים גאוגרפיים",
+ "action-geocode": "לבצע קידודים גאוגרפיים באתר הוויקי הזה",
+ "maps_map": "מפה",
+ "maps-tracking-category": "דפים עם מפה שהוכנה באמצעות הרחבת המפות",
+ "maps-loading-map": "המפה נטענת...",
+ "maps-load-failed": "טעינת המפה לא הצליחה!",
+ "maps-markers": "סמנים",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "סמני מסנן",
+ "maps-others": "אחרים",
+ "maps-kml-parsing-failed": "פענוח קובץ KML אחד או יותר נכשל. בדרך כלל בגלל בעיה באחזור או בגלל XML בלתי־תקין.",
+ "maps-ns-layer": "שכבה",
+ "maps-ns-layer-talk": "שיחת שכבה",
+ "maps-layer-property": "מאפיין",
+ "maps-layer-value": "ערך",
+ "maps-layer-errors": "שגיאות",
+ "maps-layerdef-invalid": "{{PLURAL:$1|הגדרה בלתי־תקינה|הגדרות בלתי־תקינות}}",
+ "maps-layerdef-invalid-fatal": "הגדרה בלתי־תקינה בעליל",
+ "maps-layerdef-wrong-namespace": "הגדרות שכבות תקינות רק בדפים במרחב השם \"$1\"",
+ "maps-layerdef-equal-layer-name": "שמות השכבות צריכים להיות ייחודיים באותו דף שכבה. \"$1\" כבר משמש בשכבה אחרת.",
+ "maps-layerpage-usage": "דפים עם מפות שמשתמשות בשכבה \"$1\".",
+ "maps-layerpage-nousage": "אין דפים שמשתמשות עכשיו בשכבה הזאת.",
+ "maps-error-invalid-layertype": "אין שכבות מסוג \"$1\". רק {{PLURAL:$3|הסוג הזה נתמך|הסוגים האלה נתמכים}}: $2.",
+ "maps-error-no-layertype": "יש להקליד את סוג השכבה. {{PLURAL:$2|רק הסוג הזה נתמך|הסוגים הנתמכים}}: $1",
+ "validation-error-invalid-layer": "הפרמטר $1 צריך להיות שכבה תקינה.",
+ "validation-error-invalid-layers": "הפרמטר $1 צריך להיות שכבה תקינה אחת או יותר.",
+ "validation-error-no-non-numeric": "הפרמטר \"$1\" חייב להיות מחרוזת שאינה מספרית.",
+ "validation-error-no-non-numerics": "הפרמטר \"$1\" צריך להיות מחרוזת בלתי־מספרית אחת או יותר.",
+ "maps-layer-of-type": "שכבה מסוג $1",
+ "maps-layer-of-type-and-name": "שכבה \"$2\" מסוג \"$1\"",
+ "maps-layer-type-supported-by": "השכבה הזאת יכולה לעבוד רק עם {{PLURAL:$2|שירות המפות הבא|שירותי המפות הבאים}}: $1.",
+ "maps-coordinates-description": "מילת הפעלה לעיצוב נקודות ציון, מכל תסדיר נתמך לכל תסדיר נתמך.",
+ "maps-displaymap-description": "להציג מפות גאוגרפיות ללא שום סמנים מוגדרים בוויקי עליהן.",
+ "maps-distance-description": "להמיר את המרחק ביחידות נתמכות מסוימות לערך מתאים ביחידות אחרות.",
+ "maps-finddestination-description": "למצוא את היעד בהינתן נקודת התחלה (שיכולה להיות בכל פורמט), הכיוון הראשוני והמרחק.",
+ "maps-geocode-description": "מפעיל קידוד גאוגרפי של כתובות, במילים אחרות, הופך מיקומים עם שמות שאנשים יכולים לקרוא לערכות של נקודות ציון. יש תמיכה במספר שירותי קידוד גאוגרפי, ואין להתבלבל בינם לבין שירותי מיפוי.",
+ "maps-geodistance-description": "לחשב את המרחק הגאוגרפי בין שתי נקודת, מכל תסדיר נתמך ולכל תסדיר נתמך.",
+ "maps-mapsdoc-description": "להציג עם פרמטרים עבור שירות המיפוי שצוין יחד עם הערכים והתיאורים ההתחלתיים שלהם.",
+ "maps-layerdefinition-description": "מתאר שכבה מותאמת שאפשר להציג באמצעות פונקציות מפה אחרות.",
+ "maps-mapsdoc-par-service": "שירות המיפוי שעבורו יוצג תיעוד פרמטרים.",
+ "maps-mapsdoc-par-language": "השפה שבה יוצג התיעוד. אם אין תרגום זמין, התיעוד יוצג באנגלית.",
+ "maps-coordinates-par-location": "נקודות הציון שברצונך לעצב.",
+ "maps-coordinates-par-format": "תסדיר היעד לנקודות הציון.",
+ "maps-coordinates-par-directional": "מציין האם לפלוט את נקודות הציון באופן כיווני או לא.",
+ "maps-distance-par-distance": "מרחק שיומר לערך המתאים ביחידות המבוקשות.",
+ "maps-distance-par-decimals": "המספר המקסימלי של ספרות אחרי הנקודה בערך של התוצאה.",
+ "maps-distance-par-unit": "באילו יחידות לפלוט את המרחק.",
+ "maps-finddestination-par-location": "המיקום ההתחלתי.",
+ "maps-finddestination-par-bearing": "הכיוון הראשוני.",
+ "maps-finddestination-par-distance": "מרחק הנסיעה.",
+ "maps-finddestination-par-format": "באיזה תסדיר לפלוט את היעד.",
+ "maps-finddestination-par-directional": "האם תבנית היעד צריכה להיות כיוונית או לא.",
+ "maps-geocode-par-location": "הכתובת שאתם רוצים לעשות לה קידוד גאוגרפי.",
+ "maps-geocode-par-format": "תסדיר נקודות הציון הסופיות.",
+ "maps-geocode-par-directional": "האם לפלוט את נקודות הציון באופן כיווני או לא.",
+ "maps-geodistance-par-location1": "הנקודה הראשונה בקבוצה לחישוב מרחק.",
+ "maps-geodistance-par-location2": "הנקודה השנייה בקבוצה לחישוב מרחק.",
+ "maps-geodistance-par-unit": "באילו יחידות לפלוט את המרחק.",
+ "maps-geodistance-par-decimals": "המספר המרבי של ספרות בשבר בתוצאה.",
+ "maps-displaymap-par-mappingservice": "לאפשר להגדיר שירות מיפוי שישמש ליצרת המפה.",
+ "maps-displaymap-par-coordinates": "באיזה מיקום המפה תתמרכז בתחילה.",
+ "maps-displaymap-par-visitedicon": "שם הקובץ של תמונה שתשתמש לסמלי סימון אחרי לחיצה על הסמנים המקוריים",
+ "maps-displaymap-par-zoom": "מאפשר קביעת קמת התקריב של המפה.\nכאשר נתון זה לא מסופק וסמנים שונים נמצאים על המפה, יילקח התקריב המתאים ביותר, לא ברירת המחדל הניתנת להגדרה.",
+ "maps-displaymap-par-centre": "מאפשר קביעת נקודות ציון של מרכז המפה עבור מופעי display_point.\nמקבל כתובות ונקודות ציון.\nכאשר המאפיין הזה לא מצוין, המפה תמרכז את עצמה על הסמן שצוין או בין סמנים שצוינו.",
+ "maps-displaymap-par-title": "מאפשר קביעת טקסט שיוצג בחלונות קופצים על כל הסמנים שאין להם כותרת מוגדרת.\nכאשר נעשה שימוש בזה יחד עם תווית, הכותרת תהיה בולטת ועם קו תחתי.",
+ "maps-displaymap-par-label": "מאפשר להציב מלל שיוצג בכל החלונות הקופצים של כל הסמנים שאין להם תווית מסוימת.",
+ "maps-displaymap-par-icon": "לאפשר להציב סמל לכל הסמנים.",
+ "maps-displaymap-par-circles": "אילו עיגולים להציג",
+ "maps-displaymap-par-copycoords": "להציג תיבת דו־שיח בעת לחיצה על מיקום שממנו אפשר להעתיק את נקודות הציון שלו",
+ "maps-displaymap-par-lines": "כמה קווים להציג",
+ "maps-displaymap-par-maxzoom": "רמת התקריב המרבית",
+ "maps-displaymap-par-minzoom": "רמת התקריב המזערית",
+ "maps-displaymap-par-polygons": "אילו מצולעים להציג",
+ "maps-displaymap-par-rectangles": "אילו מלבנים להציג",
+ "maps-displaymap-par-static": "להפוך את המפה לסטטית",
+ "maps-displaymap-par-wmsoverlay": "להשתמש בשכבת כיסוי WMS",
+ "maps-displaymap-par-geojson": "כתובת URL של קובץ GeoJSON",
+ "maps-fullscreen-button": "להפעיל מסך מלא",
+ "maps-fullscreen-button-tooltip": "להציג את המפה במסך מלא או מוטבעת",
+ "validation-error-invalid-location": "הערך $1 צריך להיות מיקום תקין.",
+ "validation-error-invalid-locations": "הערך $1 צריך להיות מיקום תקין אחד או יותר.",
+ "validation-error-invalid-width": "הערך $1 צריך להיות רוחב תקין.",
+ "validation-error-invalid-height": "הערך $1 צריך להיות גובה תקין.",
+ "validation-error-invalid-distance": "הערך $1 צריך להיות מרחק תקין.",
+ "validation-error-invalid-distances": "הערך $1 צריך להיות מרחק תקין אחד או יותר.",
+ "validation-error-invalid-image": "הערך $1 צריך להיות קובץ תמונה תקין.",
+ "validation-error-invalid-images": "הערך $1 צריך להיות קובץ תמונה תקין אחד או יותר.",
+ "validation-error-invalid-goverlay": "הערך $1 צריך להיות שכבת כיסוי תקינה.",
+ "validation-error-invalid-goverlays": "הערך $1 צריך להיות שכבת כיסוי תקינה אחת או יותר.",
+ "maps-abb-north": "צפ׳",
+ "maps-abb-east": "מז׳",
+ "maps-abb-south": "דר׳",
+ "maps-abb-west": "מע׳",
+ "maps-latitude": "קו רוחב:",
+ "maps-longitude": "קו אורך:",
+ "maps-invalid-coordinates": "הערך $1 לא זוהה כסדרת קואורדינטות תקינה.",
+ "maps_coordinates_missing": "לא סופקו קואורדינטות למפה.",
+ "maps_geocoding_failed": "לא ניתן לייצר geocode עבור {{PLURAL:$2|הכתובת הבאה|הכתובות הבאות}}: $1.\nלא ניתן להציג את המפה.",
+ "maps_geocoding_failed_for": "לא ניתן לייצר geocode עבור {{PLURAL:$2|הכתובת הבאה|הכתובות הבאות}}, ולכן {{PLURAL:$2|היא הושמטה|הן הושמטו}} מהמפה:\n$1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|הקואורדינטה הבאה לא זוהתה|הקואורדינטות הבאות לא זוהו}}: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|נקודת הציון הבאה לא זוהתה והושמטה|נקודות הציון הבאות לא זוהו והושמטו}} מהמפה: $1",
+ "maps_map_cannot_be_displayed": "לא ניתן להציג את המפה.",
+ "maps-geocoder-not-available": "הקידוד הגאוגרפי של מפות אינו זמין. לא ניתן לקודד את המיקום שנבחר.",
+ "maps_leaflet": "עלון",
+ "maps-leaflet-par-defzoom": "מאפשר להגדיר את רמת המרחק ההתחלתית במפה.",
+ "maps_click_to_activate": "יש ללחוץ כדי להפעיל את המפה",
+ "maps_centred_on": "המפה ממורכזת סביב $1,$2",
+ "maps-par-mappingservice": "מאפשר הגדרת שירות המיפוי שישמש לחילול המפה.",
+ "maps-par-resizable": "מאפשר שינוי גודל מפה באמצעות גרירת הפינה שלה.",
+ "maps-par-searchmarkers": "מאפשר חיפוש סמנים מסוימים דרך שדה שמוטמע במפה",
+ "maps-par-zoom": "רמת התקריב של המפה. למפות עם סמנים ברירת המחדל תהיה רמת תקריב גבוהה ביותר שמציגה את כל הסמנים.",
+ "maps-par-width": "לאפשר הגדרת רוחב המפה. ברירת המחדל היא שהיחידה תהיה פיקסל, אבל אפשר להגדיר במפורש אחת מהיחידות הבאות: px, ex, em, %.",
+ "maps-par-height": "לאפשר הגדרת גובה המפה. ברירת המחדל היא שהיחידה תהיה פיקסל, אבל אפשר להגדיר במפורש אחת מהיחידות הבאות: px, ex, em, %.",
+ "maps-par-centre": "המיקום שבו המפה צריכה להיות ממורכזת",
+ "maps-par-enable-fullscreen": "להפעיל כתפור מסך מלא",
+ "maps-par-kml": "קבצי KML שייטענו אל המפה.",
+ "maps-par-markercluster": "מאפשר מיזוג מספר סמנים קרובים לסמן אחד",
+ "maps-googlemaps3-incompatbrowser": "הדפדפן שלך לא תומך בגוגל מפות גרסה 3.",
+ "maps-googlemaps3-par-imageoverlays": "מאפשר הוספת תמונה שתוצג במיקום מסוים על המפה.",
+ "maps-googlemaps3-par-type": "סוג המפה שיוצג תחילה.",
+ "maps-googlemaps3-par-types": "סוגי המפה שיהיו זמינים דרך בקר הסוג.",
+ "maps-googlemaps3-par-layers": "שכבות מיוחדות שייטענו אל המפה.",
+ "maps-googlemaps3-par-controls": "אילו בקרים להציב על המפה.",
+ "maps-googlemaps3-par-zoomstyle": "סגנון בקר התקריב.",
+ "maps-googlemaps3-par-typestyle": "סגנון בקר הסוג.",
+ "maps-googlemaps3-par-autoinfowindows": "לפתוח את כל חלונות המידע אחרי שהדף נטען.",
+ "maps-googlemaps3-par-gkml": "קבצי KML שמתארחים בגוגל וייטענו אל המפה.",
+ "maps-googlemaps3-par-kmlrezoom": "לקרב מחדש את המפה אחרי ששכבות KML נטענו.",
+ "maps-googlemaps3-par-poi": "הצגת נקודות עניין.",
+ "maps-googlemaps3-par-clustergridsize": "גודל הרשת של האשכול בפיקסלים.",
+ "maps-par-clusterzoomonclick": "האם ההתנהגות הראשונית ללחיצה על אשכולית היא להתקרב לתוכו.",
+ "maps-googlemaps3-par-clusteraveragecenter": "האם המרכז של כל אשכול אמור להיות הממוצע של כל הסמנים באשכול.",
+ "maps-googlemaps3-par-clusterminsize": "המספר המזערי של הסמנים שאמורים להיות באשכול לפני שהסמנים מוסתרים ומוצג מונה.",
+ "mapeditor": "עורך מפות",
+ "specialpages-group-maps": "מפות",
+ "mapeditor-parser-error": "אירעה שגיאה בעת עיבוד מטא־נתונים. קלט ממשתמשים לא יעובד.",
+ "mapeditor-none-text": "אין",
+ "mapeditor-done-button": "בוצע",
+ "mapeditor-remove-button": "הסרה",
+ "mapeditor-import-button2": "ייבוא",
+ "mapeditor-export-button": "ייצוא לקוד ויקי",
+ "mapeditor-import-button": "ייבוא מקוד ויקי",
+ "mapeditor-select-button": "לבחור את המצולע הזה",
+ "mapeditor-mapparam-button": "עריכת הפרמטרים של המפה",
+ "mapeditor-clear-button": "ניקוי מפה",
+ "mapeditor-code-title": "קוד ויקי",
+ "mapeditor-import-title": "ייבוא קוד ויקי",
+ "mapeditor-import-note": "נא לשים לב לכך שהמפענח מצפה לקוד ויקי בתסדיר נוקשה מאוד. הקוד שמוזן כאן צריך להתאים לקוד המיוצא.",
+ "mapeditor-form-title": "עריכת פרטים",
+ "mapeditor-link-title-switcher-popup-text": "חלון קופץ עם טקסט",
+ "mapeditor-link-title-switcher-link-text": "קישור",
+ "mapeditor-form-field-title": "כותרת",
+ "mapeditor-form-field-text": "טקסט",
+ "mapeditor-form-field-link": "קישור",
+ "mapeditor-form-field-icon": "אייקון",
+ "mapeditor-form-field-group": "קבוצה",
+ "mapeditor-form-field-inlinelabel": "תווית פנימית",
+ "mapeditor-form-field-strokecolor": "צבע קו",
+ "mapeditor-form-field-strokeopacity": "אטימות קו",
+ "mapeditor-form-field-strokeweight": "עובי קו",
+ "mapeditor-form-field-fillcolor": "צבע מילוי",
+ "mapeditor-form-field-fillopcaity": "שקיפות מילוי:",
+ "mapeditor-form-field-showonhover": "להציג רק במעבר סמן",
+ "mapeditor-mapparam-title": "עריכת הפרמטרים של המפה",
+ "mapeditor-mapparam-defoption": "-בחירת פרמטר-",
+ "mapeditor-imageoverlay-button": "הוספת שכבת תמונה",
+ "mapeditor-form-field-image": "תמונה",
+ "mapeditor-imageoverlay-title": "פרטי שכבת תמונה",
+ "mapeditor-form-field-visitedicon": "סמל לסימון מקומות שביקרת בהם",
+ "semanticmaps-unrecognizeddistance": "הערך $1 אינו מרחק תקין.",
+ "semanticmaps-kml-link": "הצגת קובץ KML",
+ "semanticmaps-default-kml-pagelink": "הצגת הדף $1",
+ "semanticmaps-latitude": "קו־רוחב: $1",
+ "semanticmaps-longitude": "קו־אורך: $1",
+ "semanticmaps-altitude": "גובה: $1",
+ "semanticmaps-forminput-locations": "מיקומים",
+ "semanticmaps-par-staticlocations": "רשימת מיקומים להוסיף למפה יחד עם הנתונים המבוקשים בשאילתה. כמו עם display_points, אפשר להוסיף כאן כותרת, תיאור וסמל לכל מיקום עם טילדה (~) בתור תו מפריד.",
+ "semanticmaps-par-showtitle": "להציג את הכותרת בחלון המידע על הסמן או לא. הכיבוי של זה שימושי לעתים קרובות כאשר נעשה שימוש בתבנית לעיצוב חלון המידע.",
+ "semanticmaps-par-hidenamespace": "האם להציג את שם המרחב בחלון המידע על סמן.",
+ "semanticmaps-par-centre": "מרכז המפה. אם לא ניתן, המפה תבחר בעצמה את המרכז המיטבי להצגת כל הסמנים על המפה.",
+ "semanticmaps-par-template": "תבנית לעיצוב תוכן חלון המידע.",
+ "semanticmaps-par-geocodecontrol": "הצגת בקר קידוד גאוגרפי.",
+ "semanticmaps-par-activeicon": "סמל שיוצג במקום הסמן הרגיל כאשר הדף הפעיל זהה לתוצאת השאילתה",
+ "semanticmaps-par-pagelabel": "כשזה מכון ל־\"yes\", לכל הסמנים יהיה \"inlineLabel\" עם קישור לדף שמכיל את נקודות הציון עבור הסמן",
+ "semanticmaps-par-ajaxcoordproperty": "שם המאפיין של נקודות הציון שמשמש לבניית שאילתת ה־ajax.",
+ "semanticmaps-par-ajaxquery": "שאילתה שנייה שנשלחת ב־ajax כדי לאחזר נקודות ציון נוספות.",
+ "semanticmaps-par-userparam": "ערך שמועבר לכל קריאה לתבנית, אם משמשת תבנית",
+ "semanticmaps-kml-text": "הטקסט משויך לכל עמוד ועמוד. נדרס במאפיינים אחרים שנעשית עליהם שאילתה, אם יש כאלה.",
+ "semanticmaps-kml-title": "כותרת לתוצאות לפי בררת המחדל.",
+ "semanticmaps-kml-linkabsolute": "האם הקישורים צריכים להיות מוחלטים (או יחסיים)",
+ "semanticmaps-kml-pagelinktext": "הטקסט שישמש לקישורים לדף, כאשר $1 יוחלף בכותרת הדף",
+ "semanticmaps-shapes-improperformat": "עיצוב לא מתאים עבור $1. נא לראות את התיעוד על עיצוב",
+ "semanticmaps-shapes-missingshape": "לא נמצאו צורות עבור $1. נא לראות את התיעוד עבור צורות",
+ "validator-type-mapscircle-list": "רשימת מעגלים",
+ "validator-type-mapsline-list": "רשימת קווים",
+ "validator-type-mapsrectangle-list": "רשימה של משולשים",
+ "validator-type-jsonfile": "טקסט"
+}
diff --git a/www/wiki/extensions/Maps/i18n/hi.json b/www/wiki/extensions/Maps/i18n/hi.json
new file mode 100644
index 00000000..e87d5c30
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/hi.json
@@ -0,0 +1,26 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ansumang",
+ "Siddhartha Ghai",
+ "Sfic"
+ ]
+ },
+ "maps_map": "मानचित्र",
+ "maps-ns-layer": "परत",
+ "maps-layer-errors": "त्रुटि",
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "W",
+ "maps-latitude": "अक्षांश:",
+ "maps-longitude": "रेखांश:",
+ "specialpages-group-maps": "मानचित्र",
+ "mapeditor-none-text": "कोई नहीं",
+ "mapeditor-remove-button": "निकालें",
+ "mapeditor-import-button2": "आयात करें",
+ "mapeditor-form-field-title": "शीर्षक",
+ "mapeditor-form-field-text": "पाठ",
+ "mapeditor-form-field-icon": "आइकन",
+ "mapeditor-form-field-image": "छवि"
+}
diff --git a/www/wiki/extensions/Maps/i18n/hr.json b/www/wiki/extensions/Maps/i18n/hr.json
new file mode 100644
index 00000000..7b10792c
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/hr.json
@@ -0,0 +1,92 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ex13",
+ "Bugoslav",
+ "Tivek"
+ ]
+ },
+ "maps-desc": "Omogućava uključivanje dinamičkih zemljovida u wikistranice, geokodiranje adresa i druge zemljopisne operacije",
+ "right-geocode": "Geokodiranje",
+ "action-geocode": "izvršavanje geokodiranja na ovom wikiju",
+ "maps_map": "Zemljovid",
+ "maps-tracking-category": "Stranica sa zemljovidom iscrtanim pomoću proširenja Zemljovidi (''Maps'')",
+ "maps-loading-map": "Učitavam zemljovid...",
+ "maps-load-failed": "Zemljovid nije bilo moguće učitati!",
+ "maps-markers": "Označivači",
+ "maps-copycoords-prompt": "Pritisnite CTRL+C, pa ENTER",
+ "maps-searchmarkers-text": "Filtriranje označivača",
+ "maps-others": "ostali",
+ "maps-ns-layer": "Sloj",
+ "maps-ns-layer-talk": "Razgovor o sloju",
+ "maps-layer-property": "Svojstvo",
+ "maps-layer-value": "Vrijednost",
+ "maps-layer-errors": "Pogrješke",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Nevrijedeća definicija|Nevrijedeće definicije}}",
+ "maps-layerdef-invalid-fatal": "Kobna nevrijedeća definicija",
+ "maps-layerdef-wrong-namespace": "Definicije sloja valjane su samo na stranicama u imenskom prostoru »$1«",
+ "maps-layerpage-usage": "Stranice sa zemljovidima koji koriste sloj »$1«",
+ "maps-layerpage-nousage": "Trenutačno nema stranica koje rabe ovaj sloj.",
+ "maps-layer-type-supported-by": "Ovaj tip sloja može se rabiti {{PLURAL:$2|samo s kartografskom uslugom $1|s ovim kartografskim uslugama: $1}}.",
+ "maps-geodistance-description": "Izračunava zemljopisnu udaljenost između dvije točke, iz i na neki od podržanih formata.",
+ "maps-layerdefinition-description": "Opisuje prilagođeni sloj koji je moguće prikazati s drugim mogućnostima zemljovoda.",
+ "maps-displaymap-par-polygons": "Mnogokuti za prikaz",
+ "maps-displaymap-par-rectangles": "Pravokutnici za prikaz",
+ "maps-displaymap-par-static": "Postavi zemljovid statičnim",
+ "maps-fullscreen-button-tooltip": "Vidi zemljovid preko cijeloga zaslona ili kao umetak.",
+ "maps-abb-north": "S",
+ "maps-abb-east": "I",
+ "maps-abb-south": "J",
+ "maps-abb-west": "Z",
+ "maps-latitude": "Zemljopisna širina:",
+ "maps-longitude": "Zemljopisna dužina:",
+ "maps_coordinates_missing": "Za kartu nisu dostupne koordinate.",
+ "maps_geocoding_failed": "{{PLURAL:$2|Sljedeća adresa ne može biti geokodirana|Sljedeće adrese ne mogu biti geokodirane}}: $1.\nKarta ne može biti prikazana.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|Sljedeća adresa ne može biti geokodirana|Sljedeće adrese ne mogu biti geokodirane}} i {{PLURAL:$2|izostavljena je|izostavljene su}} iz karte:\n$1",
+ "maps_map_cannot_be_displayed": "Zemljovid nije moguće prikazati.",
+ "maps-leaflet-par-defzoom": "Dopuštanje postavljanja predodređenoga stupnja povećanja prikaza zemljovida.",
+ "maps_click_to_activate": "Škljocnite za aktivaciju zemljovida.",
+ "maps_centred_on": "Zemljovid je centriran na $1, $2.",
+ "maps-par-centre": "Lokacija na kojoj zemljovid treba centrirati",
+ "maps-par-kml": "KML datoteke za učitavanje na zemljovid.",
+ "maps-googlemaps3-par-type": "Tip zemljovida koji će prvobitno biti pokazan.",
+ "maps-googlemaps3-par-layers": "Posebni slojevi za učitavanje na zemljovid.",
+ "maps-googlemaps3-par-poi": "Pokazuje točke koje Vas zanimaju.",
+ "mapeditor": "Uređivač zemljovida",
+ "specialpages-group-maps": "Zemljovidi",
+ "mapeditor-none-text": "Nema",
+ "mapeditor-done-button": "Gotovo",
+ "mapeditor-remove-button": "Ukloni",
+ "mapeditor-import-button2": "Uvezi",
+ "mapeditor-export-button": "Izvezi u vikikôdu",
+ "mapeditor-import-button": "Uvezi iz wikikôda",
+ "mapeditor-select-button": "Odaberi mnogokut",
+ "mapeditor-mapparam-button": "Uredi parametre zemljovida",
+ "mapeditor-clear-button": "Očisti zemljovid",
+ "mapeditor-code-title": "Wikikôd",
+ "mapeditor-import-title": "Uvoz wikikôda",
+ "mapeditor-form-title": "Uredi pojedinosti",
+ "mapeditor-link-title-switcher-popup-text": "Prozorčić s tekstom",
+ "mapeditor-link-title-switcher-link-text": "Poveznica",
+ "mapeditor-form-field-title": "Naslov",
+ "mapeditor-form-field-text": "Tekst",
+ "mapeditor-form-field-link": "Poveznica",
+ "mapeditor-form-field-icon": "Ikona",
+ "mapeditor-form-field-group": "Skupina",
+ "mapeditor-mapparam-title": "Uredi parametre zemljovida",
+ "semanticmaps-unrecognizeddistance": "Vrijednost $1 nije valjana udaljenost.",
+ "semanticmaps-latitude": "Zemljopisna širina: $1",
+ "semanticmaps-longitude": "Zemljopisna dužina: $1",
+ "semanticmaps-altitude": "Nadmorska visina: $1",
+ "semanticmaps-forminput-locations": "Lokacije",
+ "semanticmaps-par-centre": "Središte zemljovida. Ako nije zadano, bit će automatski odabrano optimalno središte za prikaz svih oznaka na zemljovidu.",
+ "validator-type-mapscircle": "Zemljopisna kružnica",
+ "validator-type-mapscircle-list": "Popis kružnica",
+ "validator-type-mapsline": "Zemljopisna linija",
+ "validator-type-mapslocation": "Zemljopisna lokacija",
+ "validator-type-mapsrectangle": "Zemljopisni pravokutnik",
+ "validator-type-mapsrectangle-list": "Popis pravokutnika",
+ "validator-type-mapspolygon": "Zemljopisni mnogokut",
+ "validator-type-mapspolygon-list": "Popis zemljopisnih mnogokuta",
+ "validator-type-jsonfile": "tekst"
+}
diff --git a/www/wiki/extensions/Maps/i18n/hsb.json b/www/wiki/extensions/Maps/i18n/hsb.json
new file mode 100644
index 00000000..943696c7
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/hsb.json
@@ -0,0 +1,172 @@
+{
+ "@metadata": {
+ "authors": [
+ "J budissin",
+ "Michawiki",
+ "Macofe"
+ ]
+ },
+ "maps-desc": "Zmóžnja zasadźenje dynamiskich kartow do wikistronow, geokodowanje adresow a druhe geografiske podawki",
+ "right-geocode": "Geokodowanje",
+ "maps_map": "Karta",
+ "maps-tracking-category": "Strony z kartu rysowanej přez rozšěrjenje Maps",
+ "maps-loading-map": "Karta so začituje...",
+ "maps-load-failed": "Karta njeda so začitać!",
+ "maps-markers": "Marki",
+ "maps-copycoords-prompt": "STRG+C, ENTER",
+ "maps-searchmarkers-text": "Filtrowy marki",
+ "maps-others": "druhe",
+ "maps-kml-parsing-failed": "Parsowanje jedneje KML-dataje abo wjacorych KML-datajow je so naprašowanskeho zmylka abo zmylneho XML njeporadźiło.",
+ "maps-ns-layer": "Runina",
+ "maps-ns-layer-talk": "Diskusija runiny",
+ "maps-layer-property": "Kajkosć",
+ "maps-layer-value": "Hódnota",
+ "maps-layer-errors": "Zmylki",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Njepłaćiwa definicija|Njepłaćiwej definiciji|Njepłaćiwe definicije}}",
+ "maps-layerdef-invalid-fatal": "Chutna njepłaćiwa definicija",
+ "maps-layerdef-wrong-namespace": "Runinowe definicije su jenož płaćiwe na stronach z mjenowym rumom \"$1\"",
+ "maps-layerpage-usage": "Strony z kartami, kotrež runinu \"$1\" wužiwaja",
+ "maps-layerpage-nousage": "We wokomiku žane strony tutu runinu njewužiwaja.",
+ "maps-error-invalid-layertype": "Njejsu žane runiny typa \"$1\". Jenož {{PLURAL:$3|tutón typ so podpěruje|tutej typaj so podpěrujetej|tute typy so podpěruja|tute typy so podpěruja}}: $2",
+ "maps-error-no-layertype": "Dyrbiš typ runiny podać: {{PLURAL:$2|Jenož tutón typ so podpěruje|Tutej typaj so podpěrujetej|Tute typy so podpěruja|Tute typy so podpěruja}}: $1",
+ "validation-error-invalid-layer": "Parameter $1 dyrbi płaćiwa runina być.",
+ "validation-error-invalid-layers": "Parameter $1 dyrbi jedna runina abo wjacore runiny być.",
+ "maps-layer-of-type": "Runina typa $1",
+ "maps-layer-of-type-and-name": "Runina \"$2\" typa \"$1\"",
+ "maps-layer-type-supported-by": "Tutón typ runiny móže so {{PLURAL:$2|jenož z kartografiskej słužbu $1|z tutej kartografiskimaj słužbomaj: $1|z tutymi kartografiskimi słužbami: $1|z tutymi kartografiskimi słužbami: $1}}.wužiwać.",
+ "maps-coordinates-description": "Parserowa hóčka za formatowanje koordinatow z a do podpěranych formatow.",
+ "maps-displaymap-description": "Geografiske karty bjez we wikiju definowanych markow na nich zwobraznić.",
+ "maps-distance-description": "Konwertuj distancu z pomocu wěsteje podpěraneje jednotki do jeje ekwiwalenta z pomocu druheje jednotki.",
+ "maps-finddestination-description": "Cil z pomocu podateho startoweho dypka (kotryž móže w někajkim z podpěranych formatow być), spočatneho nasměrjenja a zdalenosće pytać.",
+ "maps-geocode-description": "Zmóžnja geokodowanje adresow, hinak prajene, přetworja wot čłowjeka čitajomne městna do sadźbow koordinatow. Je podpěra za wjacore geokodowanske słužby, kotrež njeměli so z kartowymi słužbami zaměnić.",
+ "maps-geodistance-description": "Wulič geografisku zdalenosć mjez dwěmaj dypkomaj, z a do podpěranych formatow.",
+ "maps-mapsdoc-description": "Tabelu z parametrami za podatu kartěrowansku słužbu hromadźe z jich standardnymi hódnotami a wopisanjemi zwobraznić.",
+ "maps-mapsdoc-par-service": "Kartografiska słužba, za kotrejež parametery dokumentacija ma so zwobraznić.",
+ "maps-mapsdoc-par-language": "Rěč, w kotrejž dokumentacija ma so zwobraznić. Jeli přełožk k dispoziciji njesteji, budźe so jendźelšćina wužiwać.",
+ "maps-coordinates-par-location": "Koordinaty, kotrež chceš formatować.",
+ "maps-coordinates-par-format": "Cilowy format za koordinaty.",
+ "maps-coordinates-par-directional": "Podawa, hač koordinaty maja směrowe podaće měć abo nic.",
+ "maps-distance-par-distance": "Zdalenosć, kotraž ma so do jeje ekwiwalenta z podatej jednotku konwertować.",
+ "maps-distance-par-decimals": "Maksimalna ličba městnow zady komy, kotraž ma so we wuslědku wužiwać.",
+ "maps-distance-par-unit": "Jednotka za wudawanje zdalenosće.",
+ "maps-finddestination-par-location": "Wuchadźišćo.",
+ "maps-finddestination-par-bearing": "Wuchadne nasměrjenje.",
+ "maps-finddestination-par-distance": "Pućowanska zdalenosć.",
+ "maps-finddestination-par-format": "Wudawanski format za cil.",
+ "maps-finddestination-par-directional": "Podawa, hač format cila ma směrowe podaće měć abo nic.",
+ "maps-geocode-par-location": "Adresa, kotruž chceš geokodować.",
+ "maps-geocode-par-format": "Format za wudate koordinaty.",
+ "maps-geocode-par-directional": "Podawa, hač koordinaty maja směrowe podaće měć abo nic.",
+ "maps-geodistance-par-location1": "Prěni dypk z dweju dypkow, mjez kotrymajž ma so zdalenosć wuličić.",
+ "maps-geodistance-par-location2": "Druhi dypk z dweju dypkow, mjez kotrymajž ma so zdalenosć wuličić.",
+ "maps-geodistance-par-unit": "Jednotka, w kotrejž so zdalenosć podawa.",
+ "maps-geodistance-par-decimals": "Maksimalna ličba městnow zady komy, kotraž ma so we wuslědku wužiwać.",
+ "maps-displaymap-par-mappingservice": "Zmóžnja nastajenje kartografiskeje słužby, kotraž ma so za generowanje karty wužiwać,",
+ "maps-displaymap-par-coordinates": "Městno, na kotrež ma so karta spočatnje centrować.",
+ "maps-displaymap-par-visitedicon": "Mjeno wobrazoweje dataje, kotrež ma so za markowe symbole wužiwać, po tym zo je so na prěnjotne marki kliknyło.",
+ "maps-displaymap-par-icon": "Zmóžnja postajenje symbola, kotryž ma so za wšě marki wužiwać.",
+ "maps-displaymap-par-circles": "Kruhi, kotrejž maja so zwobraznić",
+ "maps-displaymap-par-lines": "Linije, kotrež maja so zwobraznić",
+ "maps-displaymap-par-maxzoom": "Maksimalny skalowanski schodźenk",
+ "maps-displaymap-par-minzoom": "Minimalny skalowanski schodźenk",
+ "maps-displaymap-par-polygons": "Wjeleróžki, kotrež maja so zwobraznić",
+ "maps-displaymap-par-rectangles": "Praworóžki, kotrež maja so zwobraznić",
+ "maps-displaymap-par-static": "Jeli karta ma statiska być",
+ "maps-displaymap-par-wmsoverlay": "WMS-naworštowanje wužiwać",
+ "maps-fullscreen-button": "Połnu wobrazowku přepinać",
+ "maps-fullscreen-button-tooltip": "Kartu jako połnu wobrazowku abo zasadźenu pokazać",
+ "validation-error-invalid-location": "Parameter $1 dyrbi płaćiwe městno być.",
+ "validation-error-invalid-locations": "Parameter $1 dyrbi jedne městno abo wjacore městna być.",
+ "validation-error-invalid-width": "Parameter $1 dyrbi płaćiwa šěrokosć być.",
+ "validation-error-invalid-height": "Parameter $1 dyrbi płaćiwa wysokosć być.",
+ "validation-error-invalid-distance": "Parameter $1 dyrbi płaćiwa distanca być.",
+ "validation-error-invalid-distances": "Parameter $1 dyrbi jedna distanca abo wjacore distancy być.",
+ "validation-error-invalid-image": "Parameter $1 dyrbi płaćiwy wobraz być.",
+ "validation-error-invalid-images": "Parameter $1 dyrbi jedyn wobraz abo wjacore wobrazy być.",
+ "validation-error-invalid-goverlay": "Parameter $1 dyrbi płaćiwa woršta być.",
+ "validation-error-invalid-goverlays": "Parameter $1 dyrbi jedna woršta abo wjacore woršty być.",
+ "maps-abb-north": "S",
+ "maps-abb-east": "W",
+ "maps-abb-south": "J",
+ "maps-abb-west": "Z",
+ "maps-latitude": "Šěrina:",
+ "maps-longitude": "Dołhosć:",
+ "maps-invalid-coordinates": "Hódnota $1 njebu jako płaćiwu sadźbu koordinatow spóznata.",
+ "maps_coordinates_missing": "Za kartu njejsu koordinaty podate.",
+ "maps_geocoding_failed": "Geokodowanje {{PLURAL:$2|slědowaceje adresy|slědowaceju adresow|slědowacych adresow|slědowacych adresow}} njebě móžno: $1. Karta njeda so zwobraznić.",
+ "maps_geocoding_failed_for": "Geokodowanje {{PLURAL:$2|slědowaceje adresy|slědowaceju adresow|slědowacych adresow|slědowacych adresow}} njebě móžno a {{PLURAL:$2|tuta adresa|tutej adresy|tute adresy|tute adresy}} so na karće {{PLURAL:$2|wuwostaja|wuwostajetej|wuwostajeja|wuwostajeja}}: $1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|Slědowaca koordinata njebu spóznana|Slědowacej koordinaće njebuštej spóznanej|Slědowace koordinaty njebuchu spóznane|Slědowace koordinaty njebuchu spóznane}}: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|Slědowaca koordinata njebu spóznana|Slědowacej koordinaće njebuštej spóznanej|Slědowace koordinaty njebuchu spóznane|Slědowace koordinaty njebuchu spóznane}} a {{PLURAL:$2|bu na karće wuwostajena|buštej na karće wuwostajenej|buchu na karće wuwostajene|buchu na karće wuwostajene}}: $1",
+ "maps_map_cannot_be_displayed": "Karta njeda so zwobraznić.",
+ "maps-geocoder-not-available": "Funkcija geokodowanja Kartow k dispoziciji njesteji, twoje městno njehodźi so geokodować.",
+ "maps_click_to_activate": "Klikń, zo by kartu aktiwizował",
+ "maps_centred_on": "Karta na $1, $2 centrowana.",
+ "maps-par-mappingservice": "Zmóžnja nastajenje kartografiskeje słužby, kotraž ma so za wutworjenje karty wužiwać,",
+ "maps-par-centre": "Městno, hdźež karta ma so centrować",
+ "maps-par-enable-fullscreen": "Tłóčatko połneje wobrazowki zmóžnić",
+ "maps-par-kml": "KML-dataje, kotrež maja so na kartu začitać.",
+ "maps-googlemaps3-incompatbrowser": "Twój wobhladowak njeje kompatibelny z Google Maps v3.",
+ "maps-googlemaps3-par-type": "Kartowy typ, kotryž ma so na spočatku pokazać.",
+ "maps-googlemaps3-par-layers": "Wosebite runiny, kotrež maja so na kartu začitać.",
+ "maps-googlemaps3-par-controls": "Wodźenske elementy, kotrež maja so na karće zwobraznić.",
+ "maps-googlemaps3-par-zoomstyle": "Stil skalowanskeho wodźenja.",
+ "maps-googlemaps3-par-typestyle": "Stil wodźenja za kartowe typy.",
+ "maps-googlemaps3-par-autoinfowindows": "Wšě informaciske wokna awtomatisce wočinić, po tym zo strona je so začitała.",
+ "maps-googlemaps3-par-poi": "Zajimawosće pokazać.",
+ "mapeditor": "Kartowy editor",
+ "specialpages-group-maps": "Karty",
+ "mapeditor-parser-error": "Při parsowanju metadatow je so zmylk wustupił. Zapodaća wužiwarja so ignoruja.",
+ "mapeditor-none-text": "Žana",
+ "mapeditor-done-button": "Dokónčeny",
+ "mapeditor-remove-button": "Wotstronić",
+ "mapeditor-import-button2": "Importować",
+ "mapeditor-export-button": "Do wikikoda eksportować",
+ "mapeditor-import-button": "Z wikikoda importować",
+ "mapeditor-select-button": "Tutón wjeleróžk wubrać",
+ "mapeditor-mapparam-button": "Kartowe parametry wobdźěłać",
+ "mapeditor-clear-button": "Kartu wuprózdnić",
+ "mapeditor-code-title": "Wikikod",
+ "mapeditor-import-title": "Wikikod importować",
+ "mapeditor-form-title": "Podrobnosće wobdźěłać",
+ "mapeditor-link-title-switcher-popup-text": "Wuskakowanski meni z tekstom",
+ "mapeditor-link-title-switcher-link-text": "Wotkaz",
+ "mapeditor-form-field-title": "Titul",
+ "mapeditor-form-field-text": "Tekst",
+ "mapeditor-form-field-link": "Wotkaz",
+ "mapeditor-form-field-icon": "Symbol",
+ "mapeditor-form-field-group": "Skupina",
+ "mapeditor-form-field-inlinelabel": "Zasadźene pomjenjowanje",
+ "mapeditor-form-field-strokecolor": "Barba smužkow",
+ "mapeditor-form-field-strokeopacity": "Opaknosć smužkow",
+ "mapeditor-form-field-strokeweight": "Smužkowa tołstosć",
+ "mapeditor-form-field-fillcolor": "Pjelnjenska barba",
+ "mapeditor-form-field-fillopcaity": "Opaknosć pjelnjenja",
+ "mapeditor-form-field-showonhover": "Jenož pod znošowacym kursorom pokazać",
+ "mapeditor-mapparam-title": "Kartowe parametry wobdźěłać",
+ "mapeditor-mapparam-defoption": "-Parameter wubrać-",
+ "mapeditor-imageoverlay-button": "Wobrazowe zablendowanje přidać",
+ "mapeditor-form-field-image": "Wobraz",
+ "mapeditor-imageoverlay-title": "Podrobnosće wo wobrazowym zablendowanju",
+ "mapeditor-form-field-visitedicon": "Symbol za wopytany",
+ "semanticmaps-unrecognizeddistance": "Hódnota $1 płaćiwa distanca njeje.",
+ "semanticmaps-kml-link": "KML-dataju sej wobhladać",
+ "semanticmaps-default-kml-pagelink": "Nastawk $1 sej wobhladać",
+ "semanticmaps-latitude": "Šěrokostnik: $1",
+ "semanticmaps-longitude": "Dołhostnik: $1",
+ "semanticmaps-altitude": "Wysokosć: $1",
+ "semanticmaps-forminput-locations": "Městna",
+ "semanticmaps-par-staticlocations": "Lisćina městnow, kotrež maja so zhromadnje z naprašowanymi datami karće přidać. Kaž pola zwobraznjenskich dypkow móžeš titul. wopisanje a symbol na městno z pomocu tildy \"~\" jako dźělatko přidać.",
+ "semanticmaps-par-showtitle": "Titul w informaciskim woknje woznamjenjenja pokazać abo nic. Je husto wužitne, tutu opciju znjemóžnić, hdyž so předłoha wužiwa, zo by so wobsah informaciskeho wokna formatował.",
+ "semanticmaps-par-hidenamespace": "Mjeno mjenoweho ruma w informaciskim woknje woznamjenjenja pokazać.",
+ "semanticmaps-par-centre": "Srjedźišćo karty. Jeli je njepodate, budźe so karta awtomatisce optimalne srjedźišćo wuběrać, zo bychu so wšě woznamjenjenja na karće pokazali.",
+ "semanticmaps-par-template": "Předłoha, kotraž ma so za formatowanje wobsaha infowokna wužiwać,",
+ "semanticmaps-par-geocodecontrol": "Geokodowanske wodźenje pokazać",
+ "semanticmaps-par-activeicon": "Symbol, kotryž ma so město standardneho woznamjenjenja zwobraznić, hdyž aktiwna strona naprašowanskemu wuslědkej wotpowěduje.",
+ "semanticmaps-par-pagelabel": "Jeli na \"haj\" stajene, změju wšě marki \"inlineLabel\" z wotkazom k stronje, kotraž koordinaty marki wobsahuje.",
+ "semanticmaps-kml-text": "Tekst, kotryž je z kóždej stronu zwjazany. Naruna so přez přidatne naprašowane kajkosće, jeli tajke su.",
+ "semanticmaps-kml-title": "Standardny titul za wuslědki",
+ "semanticmaps-kml-linkabsolute": "Wotkazy měli absolutne być (nic relatiwne)",
+ "semanticmaps-kml-pagelinktext": "Tekst, kotryž ma so za wotkazy k tej stronje wužiwać, w kotrejž $1 so přez titul strony naruna",
+ "semanticmaps-shapes-improperformat": "$1 je so wopak formatował. Prošu hlej dokumentaciju za formatowanje",
+ "semanticmaps-shapes-missingshape": "Formy za $1 njejsu so namakali. Prošu hlej dokumentaciju za k dispoziciji stejace formy."
+}
diff --git a/www/wiki/extensions/Maps/i18n/ht.json b/www/wiki/extensions/Maps/i18n/ht.json
new file mode 100644
index 00000000..f2531413
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ht.json
@@ -0,0 +1,7 @@
+{
+ "@metadata": [],
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "W"
+}
diff --git a/www/wiki/extensions/Maps/i18n/hu.json b/www/wiki/extensions/Maps/i18n/hu.json
new file mode 100644
index 00000000..fb1ec7e3
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/hu.json
@@ -0,0 +1,89 @@
+{
+ "@metadata": {
+ "authors": [
+ "Dani",
+ "Dj",
+ "Glanthor Reviol",
+ "Misibacsi",
+ "Máté",
+ "Macofe",
+ "TK-999"
+ ]
+ },
+ "maps-desc": "Lehetővé teszi a dinamikus térképek beágyazását a wiki lapokba, címek geográfiai kódolását és más geográfiai műveleteket",
+ "right-geocode": "geokód",
+ "maps_map": "Térkép",
+ "maps-loading-map": "Térkép betöltése…",
+ "maps-load-failed": "Nem sikerült betölteni a térképet!",
+ "maps-markers": "Markerek",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-others": "egyebek",
+ "maps-ns-layer": "Réteg",
+ "maps-ns-layer-talk": "Rétegvita",
+ "maps-layer-property": "Tulajdonság",
+ "maps-layer-value": "Érték",
+ "maps-layer-errors": "Hibák",
+ "maps-error-invalid-layertype": "Nincs „$1” típusú réteg. Csak {{PLURAL:$3|ez a réteg támogatott|ezek a rétegek támogatottak}}: $2",
+ "maps-error-no-layertype": "Meg kell adnod egy réteg típust. {{PLURAL:$2|Csak ez a réteg támogatott|Ezek a rétegek támogatottak}}: $1",
+ "validation-error-invalid-layer": "$1 paraméternek egy érvényes réteg típusnak kell lennie.",
+ "validation-error-invalid-layers": "$1 paraméternek egy vagy több érvényes réted típusnak kell lennie.",
+ "maps-layer-of-type": "$1 típusú réteg",
+ "validation-error-invalid-location": "A $1 paraméternek valós helynek kell lennie.",
+ "validation-error-invalid-width": "A $1 paraméternek valós szélességnek kell lennie.",
+ "validation-error-invalid-height": "A $1 paraméternek valós magasságnak kell lennie.",
+ "validation-error-invalid-distance": "$1 paraméter érvényes távolságnak kell lennie.",
+ "validation-error-invalid-image": "A(z) $1 paraméter csak érvényes kép lehet.",
+ "validation-error-invalid-images": "A(z) $1 paraméter csak egy vagy több érvényes kép lehet.",
+ "validation-error-invalid-goverlay": "A(z) $1 paraméter csak érvényes réteg lehet.",
+ "validation-error-invalid-goverlays": "A(z) $1 paraméter csak egy vagy több érvényes réteg lehet.",
+ "maps-abb-north": "É",
+ "maps-abb-east": "K",
+ "maps-abb-south": "D",
+ "maps-abb-west": "Ny",
+ "maps-latitude": "Földrajzi szélesség:",
+ "maps-longitude": "Földrajzi hosszúság:",
+ "maps-invalid-coordinates": "A(z) „$1” érték nem érvényes koordinátacsoport.",
+ "maps_coordinates_missing": "Nincsenek megadva koordináták a térképhez.",
+ "maps_geocoding_failed": "A következő {{PLURAL:$2|cím|címek}} nem geokódolhatók: $1.\nA térképet nem lehet megjeleníteni.",
+ "maps_geocoding_failed_for": "A következő {{PLURAL:$2|cím nem geokódolható|címek nem geokódolhatóak}}, és nem {{PLURAL:$2|szerepel|szerepelnek}} a térképen:\n$1",
+ "maps_unrecognized_coords": "A következő {{PLURAL:$2|koordinátát|koordinátákat}} nem sikerült felismerni: $1.",
+ "maps_unrecognized_coords_for": "A következő {{PLURAL:$2|koordinátát|koordinátákat}} nem sikerült felismerni, és el {{PLURAL:$2|lett|lettek}} távolítva a térképről: $1",
+ "maps_map_cannot_be_displayed": "A térképet nem sikerült megjeleníteni.",
+ "maps-geocoder-not-available": "A térképek kiterjesztés geokódoló funkciója nem elérhető. A tartózkodási helyed nem geokódolható.",
+ "maps_click_to_activate": "Kattints a térkép aktiválásához",
+ "maps_centred_on": "Térkép középre igazítva a következő koordináták alapján: $1, $2.",
+ "maps-par-kml": "Betöltendő KML fájlok.",
+ "maps-googlemaps3-incompatbrowser": "A böngésződ nem kompatibilis a Google Maps v3-mal.",
+ "maps-googlemaps3-par-type": "Elsőre megjelenítendő térkép típusa.",
+ "maps-googlemaps3-par-zoomstyle": "A nagyítás irányításának stílusa.",
+ "mapeditor": "Térkép szerkesztő",
+ "specialpages-group-maps": "Térképek",
+ "mapeditor-none-text": "Nincs",
+ "mapeditor-done-button": "Kész",
+ "mapeditor-remove-button": "Eltávolítás",
+ "mapeditor-import-button2": "Importálás",
+ "mapeditor-export-button": "Exportálás wikikódba",
+ "mapeditor-import-button": "Importálás wikikódból",
+ "mapeditor-select-button": "Ezen sokszög kijelelölése",
+ "mapeditor-mapparam-button": "Térkép paraméterek szerkesztése",
+ "mapeditor-clear-button": "Térkép törlése",
+ "mapeditor-code-title": "Wikikód",
+ "mapeditor-form-title": "Részletek szerkesztése",
+ "mapeditor-link-title-switcher-link-text": "Hivatkozás",
+ "mapeditor-form-field-title": "Cím",
+ "mapeditor-form-field-text": "Szöveg",
+ "mapeditor-form-field-link": "Hivatkozás",
+ "mapeditor-form-field-icon": "Ikon",
+ "mapeditor-form-field-group": "Csoport",
+ "mapeditor-form-field-inlinelabel": "Beágyazott címke",
+ "mapeditor-form-field-image": "Kép",
+ "semanticmaps-unrecognizeddistance": "A(z) $1 érték nem egy érvényes távolság.",
+ "semanticmaps-kml-link": "KML fájl megtekintése",
+ "semanticmaps-default-kml-pagelink": "A(z) $1 lap megtekintése",
+ "semanticmaps-latitude": "Szélesség: $1",
+ "semanticmaps-longitude": "Hosszúság: $1",
+ "semanticmaps-altitude": "Tengerszint feletti magasság: $1",
+ "semanticmaps-forminput-locations": "Helyszínek",
+ "semanticmaps-par-staticlocations": "A térképre a lekérdezett adatok mellett felveendő helyek listája. Akárcsak a megjelenítési pontokkal, minden helyhez megadhatsz címet, leírást és ikont hullámvonal (\"~\") elválasztóval.",
+ "semanticmaps-par-showtitle": "Megjelenítse a címet a jelző információs ablakában, vagy ne? Ennek kikapcsolás hasznos lehet, ha sablonnal formázod az információs ablak tartalmát."
+}
diff --git a/www/wiki/extensions/Maps/i18n/ia.json b/www/wiki/extensions/Maps/i18n/ia.json
new file mode 100644
index 00000000..8f57ca02
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ia.json
@@ -0,0 +1,161 @@
+{
+ "@metadata": {
+ "authors": [
+ "McDutchie",
+ "Macofe"
+ ]
+ },
+ "maps-desc": "Permitte le incorporation de cartas dynamic in paginas wiki, le geocodification de adresses e altere operationes geographic",
+ "right-geocode": "Geocodificar",
+ "action-geocode": "facer geocodification sur iste wiki",
+ "maps_map": "Carta",
+ "maps-tracking-category": "Paginas con un carta generate per le extension Maps",
+ "maps-loading-map": "Cargamento del carta…",
+ "maps-load-failed": "Non poteva cargar le carta!",
+ "maps-markers": "Marcatores",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "Filtrar marcatores",
+ "maps-others": "alteres",
+ "maps-kml-parsing-failed": "Error durante le processamento de un o plus files XML. Isto es generalmente debite a un fallimento de recuperation o a codice XML mal formate.",
+ "maps-ns-layer": "Strato",
+ "maps-ns-layer-talk": "Discussion Strato",
+ "maps-layer-property": "Proprietate",
+ "maps-layer-value": "Valor",
+ "maps-layer-errors": "Errores",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Definition|Definitiones}} non valide",
+ "maps-layerdef-invalid-fatal": "Definition non valide fatal",
+ "maps-error-invalid-layertype": "Il non ha stratos del typo \"$1\". Solmente iste {{PLURAL:$3|typo|typos}} es supportate: $2",
+ "maps-error-no-layertype": "Tu debe specificar le typo de strato. {{PLURAL:$2|Solmente iste typo|Iste typos}} es supportate: $1",
+ "validation-error-invalid-layer": "Le parametro \"$1\" debe esser un strato valide.",
+ "validation-error-invalid-layers": "Le parametro \"$1\" debe esser un o plus stratos valide.",
+ "maps-layer-of-type": "Strato del typo $1",
+ "maps-layer-type-supported-by": "Iste typo de strato pote {{PLURAL:$2|solmente esser usate con le servicio cartographic|esser usate con le sequente servicios cartographic:}} $1.",
+ "maps-coordinates-description": "Uncino analysator pro formatar coordinatas, ex e in tote le formatos supportate.",
+ "maps-displaymap-description": "Monstra cartas geographic sin marcatores definite in wiki.",
+ "maps-distance-description": "Converte un distantia in un del unitates supportate a su equivalente in un altere unitate.",
+ "maps-finddestination-description": "Cercar un destination, date un puncto de initio (que pote esser in omne formato supportate), un direction initial e un distantia.",
+ "maps-geocode-description": "Permitte le geocodification de adresses, in altere parolas, converte adresses conventional de locos in gruppos de coordinatas. Existe supporto pro plure servicios de geocodification, le quales non debe esser confundite con servicios cartographic.",
+ "maps-geodistance-description": "Calcula le distantia geographic inter duo punctos, ex e in omne formato supportate.",
+ "maps-mapsdoc-description": "Presenta un tabella con le parametros de un servicio cartographic specificate, insimul con lor valores predefinite e lor descriptiones.",
+ "maps-mapsdoc-par-service": "Le servicio cartographic pro le qual presentar le documentation de parametros.",
+ "maps-mapsdoc-par-language": "Le lingua in le qual presentar le documentation. Si tal traduction non es disponibile, anglese essera usate in su loco.",
+ "maps-coordinates-par-location": "Le coordinatas que tu vole formatar.",
+ "maps-coordinates-par-format": "Le formato in le qual presentar le coordinatas.",
+ "maps-coordinates-par-directional": "Indica si le coordinatas debe esser presentate directional o non.",
+ "maps-distance-par-distance": "Le distantia a converter a su equivalente in un unitate specificate.",
+ "maps-distance-par-decimals": "Le numero maxime de digitos fractional a usar in le valor resultante.",
+ "maps-distance-par-unit": "Le unitate in le qual presentar le distantia.",
+ "maps-finddestination-par-location": "Le position initial.",
+ "maps-finddestination-par-bearing": "Le direction initial.",
+ "maps-finddestination-par-distance": "Le distantia a viagiar.",
+ "maps-finddestination-par-format": "Le formato in le qual producer le destination.",
+ "maps-finddestination-par-directional": "Indica si le formato del destination debe esser directional o non.",
+ "maps-geocode-par-location": "Le adresse que tu vole geocodificar.",
+ "maps-geocode-par-format": "Le formato pro le coordinatas resultante.",
+ "maps-geocode-par-directional": "Indica si le coordinatas debe esser presentate directional o non.",
+ "maps-geodistance-par-location1": "Le prime puncto del gruppo inter le quales calcular le distantia.",
+ "maps-geodistance-par-location2": "Le secunde puncto del gruppo inter le quales calcular le distantia.",
+ "maps-geodistance-par-unit": "Le unitate in le qual presentar le distantia.",
+ "maps-geodistance-par-decimals": "Le numero maxime de digitos fractional a usar in le valor resultante.",
+ "maps-displaymap-par-mappingservice": "Permitte definir le servicio cartographic a usar pro generar le carta.",
+ "maps-displaymap-par-coordinates": "Le loco in le qual le carta essera initialmente centrate.",
+ "maps-displaymap-par-zoom": "Permitte definir le nivello de zoom del carta.\nSi isto non es fornite, e multiple marcatores es presente in le cata, le zoom que los arrangia melio essera prendite, non le predefinition configurabile.",
+ "maps-displaymap-par-centre": "Permitte definir le coordinatas del centro del carta pro display_point(s).\nAccepta e adresses e coordinatas.\nSi iste proprietate non es fornite, le carta se centrara in le marcator fornite, o inter le marcatores fornite.",
+ "maps-displaymap-par-title": "Permitte definir texto que essera monstrate in le pop-ups de tote le marcatores que non ha un titulo specific.\nSi usate con un etiquetta, le titulo essera monstrate in litteras grasse e sublineate.",
+ "maps-displaymap-par-label": "Permitte definir texto que essera monstrate in le pop-ups de tote le marcatores que non ha un etiquetta specific.",
+ "maps-displaymap-par-icon": "Permitte definir le icone usate pro tote le marcatores.",
+ "validation-error-invalid-location": "Le parametro $1 debe esser un loco valide.",
+ "validation-error-invalid-locations": "Le parametro $1 debe esser un o plus locos valide.",
+ "validation-error-invalid-width": "Le parametro $1 debe esser un latitude valide.",
+ "validation-error-invalid-height": "Le parametro $1 debe esser un altitude valide.",
+ "validation-error-invalid-distance": "Le parametro $1 debe esser un distantia valide.",
+ "validation-error-invalid-distances": "Le parametro $1 debe esser un o plus distantias valide.",
+ "validation-error-invalid-image": "Le parametro $1 debe esser un imagine valide.",
+ "validation-error-invalid-images": "Le parametro $1 debe esser un o plus imagines valide.",
+ "validation-error-invalid-goverlay": "Le parametro $1 debe esser un superposition valide.",
+ "validation-error-invalid-goverlays": "Le parametro $1 debe esser un o plus superpositiones valide.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "W",
+ "maps-latitude": "Latitude:",
+ "maps-longitude": "Longitude:",
+ "maps-invalid-coordinates": "Le valor $1 non es recognoscite qua coordinatas valide.",
+ "maps_coordinates_missing": "Nulle coordinata providite pro le mappa.",
+ "maps_geocoding_failed": "Le sequente {{PLURAL:$2|adresse|adresses}} non poteva esser geocodificate: $1.\nLe mappa non pote esser monstrate.",
+ "maps_geocoding_failed_for": "Le sequente {{PLURAL:$2|adresse|adresses}} non poteva esser geocodificate e ha essite omittite del mappa:\n$1",
+ "maps_unrecognized_coords": "Le sequente {{PLURAL:$2|coordinata|coordinatas}} non esseva recognoscite: $1.",
+ "maps_unrecognized_coords_for": "Le sequente {{PLURAL:$2|coordinata|coordinatas}} non esseva recognoscite e ha essite omittite del carta:\n$1",
+ "maps_map_cannot_be_displayed": "Le carta on pote esser monstrate.",
+ "maps-geocoder-not-available": "Le function de geocodification de Maps non es disponibile; tu loco non pote esser geocodificate.",
+ "maps_click_to_activate": "Clicca pro activar le carta",
+ "maps_centred_on": "Carta centrate super $1, $2.",
+ "maps-par-resizable": "Rende le carta redimensionabile per traher lo per su angulo dextre inferior.",
+ "maps-par-zoom": "Le nivello de zoom pro le carta. Pro cartas con marcatores isto es predefinite como le nivello de zoom le plus alte que ancora monstra tote le marcatores.",
+ "maps-par-width": "Permitte fixar le latitude del carta. Le unitate assumite es pixels, ma tu pote specificar un de iste unitates: px, ex, em, %.",
+ "maps-par-height": "Permitte fixar le altitude del carta. Le unitate assumite es pixels, ma tu pote specificar un de iste unitates: px, ex, em, %.",
+ "maps-par-kml": "Files KML pro cargar in le carta.",
+ "maps-googlemaps3-incompatbrowser": "Tu navigator de web non es compatibile con Google Maps version 3.",
+ "maps-googlemaps3-par-type": "Le typo de carta a monstrar initialmente.",
+ "maps-googlemaps3-par-types": "Le typos de carta que essera disponibile via le controlo de typo.",
+ "maps-googlemaps3-par-layers": "Stratos special pro cargar in le carta.",
+ "maps-googlemaps3-par-controls": "Le controlos a placiar super le carta.",
+ "maps-googlemaps3-par-zoomstyle": "Le stilo del controlo de zoom.",
+ "maps-googlemaps3-par-typestyle": "Le stilo del controlo de typo.",
+ "maps-googlemaps3-par-autoinfowindows": "Aperir automaticamente tote le fenestras informative post le cargamento del pagina.",
+ "maps-googlemaps3-par-gkml": "KML files albergate per Google a cargar sur le carta.",
+ "maps-googlemaps3-par-kmlrezoom": "Rezoomar le carta post le cargamento del stratos KML.",
+ "maps-googlemaps3-par-poi": "Monstrar punctos de interesse.",
+ "mapeditor": "Editor cartographic",
+ "specialpages-group-maps": "Cartas",
+ "mapeditor-parser-error": "Un error occurreva durante le processamento del metadatos. Le entrata del usator es ignorate.",
+ "mapeditor-none-text": "Nulle",
+ "mapeditor-done-button": "Facite",
+ "mapeditor-remove-button": "Remover",
+ "mapeditor-import-button2": "Importar",
+ "mapeditor-export-button": "Exportar in codice wiki",
+ "mapeditor-import-button": "Importar ex codice wiki",
+ "mapeditor-mapparam-button": "Modificar le parametros del carta",
+ "mapeditor-clear-button": "Rader carta",
+ "mapeditor-code-title": "Codice wiki",
+ "mapeditor-import-title": "Importar codice wiki",
+ "mapeditor-import-note": "Nota ben que le analysator syntactic expecta un formato multo stricte in le codice wiki. Le codice entrate hic debe corresponder al codice producite per le functionalitate de exportation.",
+ "mapeditor-form-title": "Modificar detalios",
+ "mapeditor-link-title-switcher-popup-text": "Pop-up con texto",
+ "mapeditor-link-title-switcher-link-text": "Ligamine",
+ "mapeditor-form-field-title": "Titulo",
+ "mapeditor-form-field-text": "Texto",
+ "mapeditor-form-field-link": "Ligamine",
+ "mapeditor-form-field-icon": "Icone",
+ "mapeditor-form-field-group": "Gruppo",
+ "mapeditor-form-field-inlinelabel": "Etiquetta in linea",
+ "mapeditor-form-field-strokecolor": "Color de tracto",
+ "mapeditor-form-field-strokeopacity": "Opacitate del tracto",
+ "mapeditor-form-field-strokeweight": "Peso del tracto",
+ "mapeditor-form-field-fillcolor": "Color de impletion",
+ "mapeditor-form-field-fillopcaity": "Opacitate de impletion",
+ "mapeditor-form-field-showonhover": "Monstrar solmente quando le cursor passa supra",
+ "mapeditor-mapparam-title": "Modificar le parametros del carta",
+ "mapeditor-mapparam-defoption": "-Selige parametro-",
+ "mapeditor-imageoverlay-button": "Adder un imagine superponite",
+ "mapeditor-form-field-image": "Imagine",
+ "mapeditor-imageoverlay-title": "Detalios del imagine superponite",
+ "mapeditor-form-field-visitedicon": "Icone visitate",
+ "semanticmaps-unrecognizeddistance": "Le valor $1 non es un distantia valide.",
+ "semanticmaps-kml-link": "Vider le file KML",
+ "semanticmaps-default-kml-pagelink": "Vider articulo $1",
+ "semanticmaps-latitude": "Latitude: $1",
+ "semanticmaps-longitude": "Longitude: $1",
+ "semanticmaps-altitude": "Altitude: $1",
+ "semanticmaps-forminput-locations": "Locos",
+ "semanticmaps-par-staticlocations": "Un lista de locos a adder al carta con le datos resultante del consulta. Como con display_points, tu pote adder un titulo, description e icone per loco usante le tilde \"~\" como separator.",
+ "semanticmaps-par-showtitle": "Monstrar un titulo in le fenestra de information de marcator o non. Disactivar isto es sovente utile si un patrono es usate pro formatar le contento del fenestra de information.",
+ "semanticmaps-par-hidenamespace": "Monstrar le titulo del spatio de nomines in le fenestra de information del marcator.",
+ "semanticmaps-par-centre": "Le centro del carta. Si non specificate, le systema selige automaticamente le centro optimal pro monstrar tote le marcatores in le carta.",
+ "semanticmaps-par-template": "Un patrono a usar pro formatar le contento del fenestra de information.",
+ "semanticmaps-par-geocodecontrol": "Monstrar le controlo de geocodification.",
+ "semanticmaps-kml-text": "Le texto associate con cata pagina. Es supplantate per le additional proprietates consultate, si existe.",
+ "semanticmaps-kml-title": "Le titulo predefinite pro resultatos",
+ "semanticmaps-kml-linkabsolute": "Debe ligamines esser absolute (in vice de relative)",
+ "semanticmaps-kml-pagelinktext": "Le texto a usar pro le ligamines al pagina, in le quales $1 essera reimplaciate per le titulo de pagina"
+}
diff --git a/www/wiki/extensions/Maps/i18n/id.json b/www/wiki/extensions/Maps/i18n/id.json
new file mode 100644
index 00000000..afa25d13
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/id.json
@@ -0,0 +1,139 @@
+{
+ "@metadata": {
+ "authors": [
+ "Bennylin",
+ "Farras",
+ "Irwangatot",
+ "IvanLanin",
+ "පසිඳු කාවින්ද",
+ "Macofe",
+ "C5st4wr6ch"
+ ]
+ },
+ "maps-desc": "Mengaktifkan penyertaan peta dinamis dalam halaman wiki, pembuatan geokode alamat, dan operasi geografi lain",
+ "maps_map": "Peta",
+ "maps-loading-map": "Memuat peta...",
+ "maps-load-failed": "Peta tidak dapat dimuat!",
+ "maps-markers": "Tanda",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-others": "lainnya",
+ "maps-ns-layer": "Lapisan",
+ "maps-ns-layer-talk": "Pembicaraan lapisan",
+ "maps-layer-property": "Properti",
+ "maps-layer-value": "Nilai",
+ "maps-layer-errors": "Kesalahan",
+ "maps-error-invalid-layertype": "Tidak ada lapisan jenis \"$1\". {{PLURAL:$3|Jenis|Jenis}} yang didukung: $2",
+ "maps-error-no-layertype": "Anda perlu menentukan jenis lapisan. {{PLURAL:$2|Jenis|Jenis}} yang didukung: $1",
+ "validation-error-invalid-layer": "Parameter $1 harus merupakan lapisan yang sah.",
+ "validation-error-invalid-layers": "Parameter $1 harus berupa satu atau lebih lapisan yang sah.",
+ "maps-layer-of-type": "Lapisan jenis $1",
+ "maps-layer-type-supported-by": "Lapisan ini hanya dapat digunakan oleh layanan pemetaan {{PLURAL:$2|$1|$1}}.",
+ "maps-coordinates-description": "Pengait parser untuk memformat koordinat, dari dan ke salah satu format yang didukung.",
+ "maps-displaymap-description": "Menampilkan peta geografis tanpa mencantumkan penanda wiki terdefinisi.",
+ "maps-distance-description": "Mengubah jarak dengan menggunakan unit terdukung tertentu menjadi setaranya dengan menggunakan unit lain.",
+ "maps-finddestination-description": "Menemukan tujuan dari suatu titik awal (dalam salah satu format yang didukung), arah, dan jarak.",
+ "maps-geocode-description": "Memberikan geokode alamat, atau dengan kata lain, mengubah lokasi yang dapat dibaca manusia ke set koordinat. Tersedia dukungan untuk beberapa layanan geokode, yang berbeda dengan layanan pemetaan.",
+ "maps-geodistance-description": "Menghitung jarak geografis antara dua titik, dari dan ke salah satu format yang didukung.",
+ "maps-coordinates-par-location": "Koordinat yang ingin diformat.",
+ "maps-coordinates-par-format": "Format sasaran untuk koordinat.",
+ "maps-coordinates-par-directional": "Menunjukkan apakah keluaran koordinat perlu disertai arah atau tidak.",
+ "maps-distance-par-distance": "Jarak yang ingin diubah ke setaranya dengan suatu unit tertentu.",
+ "maps-distance-par-decimals": "Jumlah maksimum digit pecahan yang digunakan untuk nilai hasil.",
+ "maps-distance-par-unit": "Unit keluaran jarak.",
+ "maps-finddestination-par-location": "Lokasi awal.",
+ "maps-finddestination-par-bearing": "Arah awal.",
+ "maps-finddestination-par-distance": "Jarak yang ditempuh.",
+ "maps-finddestination-par-format": "Format keluaran tujuan.",
+ "maps-finddestination-par-directional": "Menunjukkan apakah format tujuan disertai arah atau tidak.",
+ "maps-geocode-par-location": "Alamat yang ingin diubah menjadi geokode.",
+ "maps-geocode-par-format": "Format koordinat hasil.",
+ "maps-geocode-par-directional": "Menunjukkan apakah keluaran koordinat perlu disertai arah atau tidak.",
+ "maps-geodistance-par-location1": "Titik pertama dalam himpunan untuk menghitung jarak antara.",
+ "maps-geodistance-par-location2": "Titik kedua dalam himpunan untuk menghitung jarak antara.",
+ "maps-geodistance-par-unit": "Unit keluaran jarak.",
+ "maps-geodistance-par-decimals": "Jumlah maksimum digit pecahan yang digunakan untuk nilai hasil.",
+ "maps-displaymap-par-mappingservice": "Memungkinkan pengaturan layanan pemetaan yang akan digunakan untuk menghasilkan peta.",
+ "maps-displaymap-par-coordinates": "Lokasi pusat awal dari peta.",
+ "maps-displaymap-par-zoom": "Memungkinkan pengaturan tingkat pembesaran peta.\nJika tidak diberikan dan beberapa penanda ada pada peta, akan dipilih pembesaran yang paling muat, bukan bawaan yang disetel.",
+ "maps-displaymap-par-centre": "Memungkinkan pengaturan koordinat pusat peta untuk display_point.\nDapat menerima alamat maupun koordinat.\nJika tidak diberikan, peta akan menentukan pusatnya pada penanda yang ada atau di antara penanda yang diberikan.",
+ "maps-displaymap-par-title": "Memungkinkan pengaturan teks yang akan ditampilkan pada munculan dari semua penanda yang tidak memiliki judul tertentu.\nJika digunakan bersama dengan label, judul akan ditebalkan dan memiliki garis bawah.",
+ "maps-displaymap-par-label": "Memungkinkan pengaturan teks yang akan ditampilkan pada munculan dari semua tanda yang tidak memiliki label tertentu.",
+ "maps-displaymap-par-icon": "Memungkinkan pengaturan ikon yang digunakan semua penanda.",
+ "validation-error-invalid-location": "Parameter $1 harus merupakan lokasi yang sah.",
+ "validation-error-invalid-locations": "Parameter $1 harus berupa satu atau lebih lokasi yang sah.",
+ "validation-error-invalid-width": "Parameter $1 harus merupakan lebar yang sah.",
+ "validation-error-invalid-height": "Parameter $1 harus merupakan tinggi yang sah.",
+ "validation-error-invalid-distance": "Parameter $1 harus merupakan nilai jarak yang sah.",
+ "validation-error-invalid-distances": "Parameter $1 harus berupa satu atau lebih jarak yang sah.",
+ "validation-error-invalid-image": "Parameter $1 harus merupakan berkas yang sah.",
+ "validation-error-invalid-images": "Parameter $1 harus berupa satu atau lebih berkas yang sah.",
+ "validation-error-invalid-goverlay": "Parameter $1 harus merupakan hamparan yang sah.",
+ "validation-error-invalid-goverlays": "Parameter $1 harus berupa satu atau lebih hamparan yang sah.",
+ "maps-abb-north": "U",
+ "maps-abb-east": "T",
+ "maps-abb-south": "S",
+ "maps-abb-west": "B",
+ "maps-latitude": "Lintang:",
+ "maps-longitude": "Bujur:",
+ "maps-invalid-coordinates": "Nilai $1 tidak dikenali sebagai rangkaian koordinat yang sah.",
+ "maps_coordinates_missing": "Tidak koordinat yang disediakan bagi peta.",
+ "maps_geocoding_failed": "{{PLURAL:$2|alamat|alamat}} berikut tidak dapat di Geocode: $1.\nPeta tidak dapat ditampilkan.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|alamat|alamat}} berikut tidak dapat di Geocode dan {{PLURAL:$2|telah|telah}} dihilangkan dari peta: $1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|Koordinat|Koordinat}} berikut tidak dikenali: $1.",
+ "maps_unrecognized_coords_for": "Koordinat berikut tidak dikenali dan {{PLURAL:$2|telah|telah}} diabaikan dari peta:\n$1",
+ "maps_map_cannot_be_displayed": "Peta tak dapat ditampilkan.",
+ "maps-geocoder-not-available": "Fitur kodegeo Peta tidak tersedia. Lokasi Anda tidak dapat dikodegeokan",
+ "maps_click_to_activate": "Klik untuk mengaktifkan peta",
+ "maps_centred_on": "Peta dipusatkan di $1, $2.",
+ "maps-par-resizable": "Membuat peta berubah ukuran dengan menyeret pojok kanan bawahnya.",
+ "maps-par-zoom": "Tingkat pembesaran peta. Secara bawaan, peta bertanda akan mengambil tingkat pembesaran yang masih menunjukkan semua tanda.",
+ "maps-par-width": "Memungkinkan pengaturan lebar peta. Secara bawaan, piksel akan dianggap sebagai unit, tetapi Anda dapat menentukan secara eksplisit salah satu unit berikut: px, ex, em, %.",
+ "maps-par-height": "Memungkinkan pengaturan tinggi peta. Secara bawaan, piksel akan dianggap sebagai unit, tetapi Anda dapat menentukan secara eksplisit salah satu unit berikut: px, ex, em, %.",
+ "maps-par-kml": "Berkas KML yang akan dimuat ke dalam peta.",
+ "maps-googlemaps3-incompatbrowser": "Peramban Anda tidak kompatibel dengan Google Maps v3.",
+ "maps-googlemaps3-par-type": "Jenis peta yang ditampilkan saat awal.",
+ "maps-googlemaps3-par-types": "Jenis peta yang akan tersedia melalui kontrol jenis.",
+ "maps-googlemaps3-par-layers": "Lapisan khusus yang akan dimuat ke dalam peta.",
+ "maps-googlemaps3-par-controls": "Kontrol yang akan ditempatkan pada peta.",
+ "maps-googlemaps3-par-zoomstyle": "Gaya kontrol pembesaran.",
+ "maps-googlemaps3-par-typestyle": "Gaya jenis kontrol.",
+ "maps-googlemaps3-par-autoinfowindows": "Otomatis membuka semua jendela info setelah halaman dimuat.",
+ "mapeditor": "Penyunting peta",
+ "specialpages-group-maps": "Peta",
+ "mapeditor-none-text": "Tidak ada",
+ "mapeditor-done-button": "Selesai",
+ "mapeditor-remove-button": "Hapus",
+ "mapeditor-import-button2": "Impor",
+ "mapeditor-export-button": "Ekspor ke kode wiki",
+ "mapeditor-import-button": "Impor dari kode wiki",
+ "mapeditor-select-button": "Pilih poligon ini",
+ "mapeditor-mapparam-button": "Sunting parameter peta",
+ "mapeditor-clear-button": "Hapus peta",
+ "mapeditor-code-title": "Kode wiki",
+ "mapeditor-import-title": "Impor kode wiki",
+ "mapeditor-form-title": "Sunting rincian",
+ "mapeditor-link-title-switcher-link-text": "Tautan",
+ "mapeditor-form-field-title": "Judul",
+ "mapeditor-form-field-text": "Teks",
+ "mapeditor-form-field-link": "Tautan",
+ "mapeditor-form-field-icon": "Ikon",
+ "mapeditor-form-field-group": "Grup",
+ "mapeditor-form-field-strokecolor": "Warna pinggiran",
+ "mapeditor-form-field-strokeopacity": "Transparansi pinggiran",
+ "mapeditor-form-field-strokeweight": "Tebal pinggiran",
+ "mapeditor-form-field-fillcolor": "Warna isi",
+ "mapeditor-form-field-fillopcaity": "Transparansi isi",
+ "mapeditor-mapparam-title": "Sunting parameter peta",
+ "mapeditor-mapparam-defoption": "-Pilih parameter-",
+ "mapeditor-form-field-image": "Gambar",
+ "semanticmaps-unrecognizeddistance": "Nilai $1 bukan jarak yang sah.",
+ "semanticmaps-kml-link": "Lihat berkas KML",
+ "semanticmaps-default-kml-pagelink": "Lihat halaman $1",
+ "semanticmaps-forminput-locations": "Lokasi",
+ "semanticmaps-par-staticlocations": "Daftar lokasi yang akan ditambahkan ke dalam peta, berikut data kueri. Seperti halnya display_points, Anda dapat menambahkan judul, deskripsi, dan ikon per lokasi dengan menggunakan tanda tilde \"~\" sebagai pemisah.",
+ "semanticmaps-par-showtitle": "Tampilkan judul di jendela info penanda. Penonaktifan judul sering berguna ketika menggunakan templat untuk memformat isi jendela info.",
+ "semanticmaps-par-centre": "Pusat peta. Jika tidak disediakan, peta secara otomatis akan memilih pusat optimal untuk menampilkan semua penanda di peta.",
+ "semanticmaps-par-template": "Ttemplat yang digunakan untuk memformat isi jendela info.",
+ "semanticmaps-kml-linkabsolute": "Pranala sebaiknya adalah mutlak (lawan dari relatif)",
+ "semanticmaps-shapes-improperformat": "Format yang kurang tepat untuk $1. Silakan lihat dokumentasi untuk format."
+}
diff --git a/www/wiki/extensions/Maps/i18n/ig.json b/www/wiki/extensions/Maps/i18n/ig.json
new file mode 100644
index 00000000..72ec5033
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ig.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ukabia"
+ ]
+ },
+ "maps_map": "Otú Uzọr"
+}
diff --git a/www/wiki/extensions/Maps/i18n/it.json b/www/wiki/extensions/Maps/i18n/it.json
new file mode 100644
index 00000000..9eecfe15
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/it.json
@@ -0,0 +1,163 @@
+{
+ "@metadata": {
+ "authors": [
+ "Beta16",
+ "Civvì",
+ "Darth Kule",
+ "F. Cosoleto",
+ "Gianfranco",
+ "HalphaZ",
+ "Rosh",
+ "Viscontino",
+ "Matteocng",
+ "Kaspo",
+ "S4b1nuz E.656"
+ ]
+ },
+ "maps-desc": "Consente di includere mappe dinamiche nelle pagine wiki, la geocodifica degli indirizzi ed altre operazioni geografiche",
+ "right-geocode": "Effettua la geocodifica",
+ "maps_map": "Mappa",
+ "maps-loading-map": "Caricamento mappa ...",
+ "maps-load-failed": "Impossibile caricare la mappa!",
+ "maps-markers": "Marcatori",
+ "maps-copycoords-prompt": "CTRL+C, INVIO",
+ "maps-searchmarkers-text": "Filtra marcatori",
+ "maps-others": "altri",
+ "maps-ns-layer": "Livello",
+ "maps-ns-layer-talk": "Discussioni livello",
+ "maps-layer-property": "Proprietà",
+ "maps-layer-value": "Valore",
+ "maps-layer-errors": "Errori",
+ "maps-error-invalid-layertype": "Non c'è alcun livello di tipo \"$1\". Solo {{PLURAL:$3|questo tipo è supportato|questi tipi sono supportati}}: $2",
+ "maps-error-no-layertype": "È necessario specificare il tipo di livello. Solo {{PLURAL:$2|questo tipo è supportato|questi tipi sono supportati}}: $1",
+ "validation-error-invalid-layer": "Il parametro $1 deve essere un livello valido.",
+ "validation-error-invalid-layers": "Il parametro $1 deve essere uno o più livelli validi.",
+ "maps-layer-of-type": "Livello di tipo $1",
+ "maps-layer-type-supported-by": "Questo tipo di livello può essere usato solo con {{PLURAL:$2|il servizio di mappe $1|questi servizi di mappe: $1}}.",
+ "maps-coordinates-description": "Hook del parser per formattare le coordinate, da e verso i formati supportati.",
+ "maps-distance-description": "Convertire una distanza utilizzando una certa unità supportata nel suo equivalente utilizzando un'altra unità.",
+ "maps-coordinates-par-location": "Le coordinate che si desidera formattare.",
+ "maps-coordinates-par-format": "Il formato di destinazione per le coordinate.",
+ "maps-distance-par-unit": "L'unità di misura in cui sarà visualizzata la distanza.",
+ "maps-finddestination-par-location": "La posizione iniziale.",
+ "maps-finddestination-par-bearing": "La direzione iniziale.",
+ "maps-finddestination-par-distance": "La distanza da percorrere.",
+ "maps-geocode-par-format": "Il formato per le coordinate risultanti.",
+ "maps-displaymap-par-coordinates": "Uno o più luoghi da mostrare sulla mappa. Verranno indicati con un marcatore.",
+ "maps-displaymap-par-visitedicon": "Il nome del file di un'immagine da utilizzare come icone per i marcatori dopo che quelli originali sono stati selezionati",
+ "maps-displaymap-par-lines": "Linee da visualizzare",
+ "maps-displaymap-par-maxzoom": "Il livello massimo di zoom",
+ "maps-displaymap-par-minzoom": "Il livello minimo di zoom",
+ "maps-displaymap-par-polygons": "Poligoni da visualizzare",
+ "maps-displaymap-par-rectangles": "Rettangoli da visualizzare",
+ "maps-displaymap-par-static": "Rendi la mappa statica",
+ "validation-error-invalid-location": "Il parametro $1 deve essere una posizione valida.",
+ "validation-error-invalid-locations": "Il parametro $1 deve essere una o più posizioni valide.",
+ "validation-error-invalid-width": "Il parametro $1 deve essere una larghezza valida.",
+ "validation-error-invalid-height": "Il parametro $1 deve essere un'altezza valida.",
+ "validation-error-invalid-distance": "Il parametro $1 deve essere una distanza valida.",
+ "validation-error-invalid-distances": "Il parametro $1 deve essere una o più distanze valide.",
+ "validation-error-invalid-image": "Il parametro $1 deve essere un'immagine valida.",
+ "validation-error-invalid-images": "Il parametro $1 deve essere una o più immagini valide.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "O",
+ "maps-latitude": "Latitudine:",
+ "maps-longitude": "Longitudine:",
+ "maps-invalid-coordinates": "Il valore $1 non è stato riconosciuto come un set di coordinate valido.",
+ "maps_coordinates_missing": "Non sono state fornite coordinate per la mappa",
+ "maps_geocoding_failed": "Non è stato possibile effettuare la geocodifica per {{PLURAL:$2|il seguente indirizzo|i seguenti indirizzi}}: $1.",
+ "maps_geocoding_failed_for": "Non è stato possibile effettuare la geocodifica {{PLURAL:$2|del seguente indirizzo|dei seguenti indirizzi}} che {{PLURAL:$2|è stato omesso|sono stati omessi}} dalla mappa: $1.",
+ "maps_unrecognized_coords": "{{PLURAL:$2|La seguente coordinata|Le seguenti coordinate}} non sono state riconosciute: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|La seguente coordinata|Le seguenti coordinate}} {{PLURAL:$2|non è stata riconosciuta ed è stata omessa|non sono state riconosciute e sono state omesse}} dalla mappa: $1.",
+ "maps_map_cannot_be_displayed": "La mappa non può essere visualizzata.",
+ "maps-geocoder-not-available": "La funzionalità di geocodifica dell'estensione Maps non è disponibile. La tua posizione non può essere geocodificata.",
+ "maps_leaflet": "Volantino",
+ "maps-leaflet-par-defzoom": "Permette l'impostazione del livello di zoom predefinito della mappa.",
+ "maps_click_to_activate": "Clicca per attivare la mappa.",
+ "maps_centred_on": "Mappa centrata su $1, $2.",
+ "maps-par-enable-fullscreen": "Attiva bottone a tutto schermo",
+ "maps-par-kml": "File KML da caricare sulla mappa.",
+ "maps-googlemaps3-incompatbrowser": "Si sta utilizzando un browser non compatibile con Google Maps v3.",
+ "maps-googlemaps3-par-type": "Il tipo di mappa da mostrare inizialmente.",
+ "maps-googlemaps3-par-layers": "Livelli speciali da caricare sulla mappa.",
+ "maps-googlemaps3-par-controls": "I controlli da posizionare sulla mappa.",
+ "maps-googlemaps3-par-zoomstyle": "Lo stile del controllo dello zoom.",
+ "maps-googlemaps3-par-gkml": "File KML ospitati da Google da caricare sulla mappa.",
+ "maps-googlemaps3-par-poi": "Mostra punti di interesse.",
+ "maps-googlemaps3-par-clustergridsize": "La dimensione della griglia di un cluster in pixels.",
+ "maps-par-clustermaxzoom": "Il massimo livello di zoom in cui i cluster possono esistere.",
+ "maps-par-clusterzoomonclick": "Se il comportamento predefinito quando si clicca su un cluster è di fare lo zoom su di esso.",
+ "maps-par-maxclusterradius": "Il raggio massimo coperto da un cluster.",
+ "maps-googlemaps3-par-clusteraveragecenter": "Se il centro di ogni cluster dovrebbe essere la media di tutti i marcatori del cluster.",
+ "maps-googlemaps3-par-clusterminsize": "Il numero minimo di marcatori presenti in un cluster dopo il quale tutti i marcatori saranno nascosti e sarà mostrato il loro numero.",
+ "mapeditor": "Editor della mappa",
+ "specialpages-group-maps": "Mappe",
+ "mapeditor-parser-error": "Si verificato un errore durante l'analisi dei metadati. Ignorato l'input dell'utente.",
+ "mapeditor-none-text": "Nessuno",
+ "mapeditor-done-button": "Fatto",
+ "mapeditor-remove-button": "Rimuovi",
+ "mapeditor-import-button2": "Importa",
+ "mapeditor-export-button": "Esporta in codice wiki",
+ "mapeditor-import-button": "Importa dal codice wiki",
+ "mapeditor-select-button": "Seleziona questo poligono",
+ "mapeditor-mapparam-button": "Modifica i parametri della mappa",
+ "mapeditor-clear-button": "Pulisci mappa",
+ "mapeditor-code-title": "Codice wiki",
+ "mapeditor-import-title": "Importa codice wiki",
+ "mapeditor-import-note": "Da notare che il parser si aspetta un formato molto rigoroso per il codice wiki. Il codice immesso qui dovrebbe corrispondere al codice emesso dalla funzionalità di esportazione.",
+ "mapeditor-form-title": "Modifica dettagli",
+ "mapeditor-link-title-switcher-popup-text": "Popup con testo",
+ "mapeditor-link-title-switcher-link-text": "Collegamento",
+ "mapeditor-form-field-title": "Titolo",
+ "mapeditor-form-field-text": "Testo",
+ "mapeditor-form-field-link": "Collegamento",
+ "mapeditor-form-field-icon": "Icona",
+ "mapeditor-form-field-group": "Gruppo",
+ "mapeditor-form-field-strokecolor": "Colore del tratto",
+ "mapeditor-form-field-fillcolor": "Colore di riempimento",
+ "mapeditor-form-field-fillopcaity": "Opacità riempimento",
+ "mapeditor-form-field-showonhover": "Visualizza solo al passaggio del mouse",
+ "mapeditor-mapparam-title": "Modifica i parametri della mappa",
+ "mapeditor-mapparam-defoption": "-Selezionare parametro-",
+ "mapeditor-imageoverlay-button": "Aggiungi sovrapposizione dell'immagine",
+ "mapeditor-form-field-image": "Immagine",
+ "mapeditor-imageoverlay-title": "Dettagli di sovrapposizione di immagini",
+ "mapeditor-form-field-visitedicon": "Icona visitato",
+ "semanticmaps-unrecognizeddistance": "Il valore $1 non è una distanza valida.",
+ "semanticmaps-kml-link": "Visualizza il file KML",
+ "semanticmaps-default-kml-pagelink": "Visualizza la pagina $1",
+ "semanticmaps-latitude": "Latitudine: $1",
+ "semanticmaps-longitude": "Longitudine: $1",
+ "semanticmaps-altitude": "Altitudine: $1",
+ "semanticmaps-forminput-locations": "Luoghi",
+ "semanticmaps-par-staticlocations": "Un elenco di luoghi da aggiungere alla mappa unitamente ai dati interrogati. Come con display_points, si può aggiungere un titolo, la descrizione e l'icona per ogni posizione utilizzando la tilde \"~\" come separatore.",
+ "semanticmaps-par-showtitle": "Mostrare oppure no un titolo nella finestra informazioni per l'indicatore. Disattivarlo è spesso utile quando si utilizza un template per la formattazione del contenuto della finestra informazioni.",
+ "semanticmaps-par-hidenamespace": "Mostrare il namespace del titolo nella finestra informazioni per l'indicatore",
+ "semanticmaps-par-centre": "Il centro della mappa. Quando non indicato, la mappa sceglierà automaticamente il centro ottimale per visualizzare tutti gli indicatori sulla mappa.",
+ "semanticmaps-par-template": "Un template da utilizzare per formattare il contenuto della finestra informazioni.",
+ "semanticmaps-par-geocodecontrol": "Mostra il controllo per geocodifica.",
+ "semanticmaps-par-activeicon": "Icona da mostrare al posto dell'indicatore predefinito, quando la pagina attiva è uguale al risultato dell'interrogazione",
+ "semanticmaps-par-pagelabel": "Quando impostato su \"yes\", tutti gli indicatori avranno un \"inlineLabel\" con un collegamento alla pagina che contiene le coordinate per l'indicatore",
+ "semanticmaps-par-userparam": "Un valore passato in ogni chiamata a template, se un template è utilizzato",
+ "semanticmaps-kml-text": "Il testo associato per ogni pagina. Sovrascritto dall'eventuale proprietà dell'interrogazione aggiuntiva.",
+ "semanticmaps-kml-title": "Il titolo predefinito per i risultati",
+ "semanticmaps-kml-linkabsolute": "I collegamenti dovranno essere assoluti (anziché relativi)",
+ "semanticmaps-kml-pagelinktext": "Il testo da utilizzare per i collegamenti alla pagina, in cui $1 verrà sostituito dal titolo della pagina",
+ "semanticmaps-shapes-improperformat": "Formattazione impropria di $1. Per favore controlla la documentazione sulla formattazione",
+ "semanticmaps-shapes-missingshape": "Nessuna forma trovata per $1. Per favore controlla la documentazione sulle forme disponibili",
+ "validator-type-mapscircle": "Cerchio geografico",
+ "validator-type-mapscircle-list": "Elenco di cerchi",
+ "validator-type-mapsimageoverlay": "Sovrapposizione di immagini",
+ "validator-type-mapsimageoverlay-list": "Elenco delle sovrapposizioni di immagini",
+ "validator-type-mapsline": "Linea geografica",
+ "validator-type-mapsline-list": "Elenco di linee",
+ "validator-type-mapslocation": "Posizione geografica",
+ "validator-type-mapslocation-list": "Elenco delle località",
+ "validator-type-mapsrectangle": "Rettangolo geografico",
+ "validator-type-mapsrectangle-list": "Elenco di rettangoli",
+ "validator-type-mapspolygon": "Poligono geografico",
+ "validator-type-mapspolygon-list": "Elenco dei poligoni geografici",
+ "validator-type-jsonfile": "testo"
+}
diff --git a/www/wiki/extensions/Maps/i18n/ja.json b/www/wiki/extensions/Maps/i18n/ja.json
new file mode 100644
index 00000000..7fa54348
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ja.json
@@ -0,0 +1,176 @@
+{
+ "@metadata": {
+ "authors": [
+ "Aotake",
+ "Fryed-peach",
+ "Shirayuki",
+ "Yanajin66",
+ "青子守歌",
+ "Schu",
+ "Suyama",
+ "Omotecho"
+ ]
+ },
+ "maps-desc": "ウィキページへの動的な地図の埋め込み、住所の座標データへの変換、その他の地図関連の操作ができるようにする。",
+ "right-geocode": "ジオコーディング",
+ "action-geocode": "このウィキでのジオコーディングの実行",
+ "maps_map": "地図",
+ "maps-tracking-category": "Maps拡張機能がレンダリングした地図があるページ",
+ "maps-loading-map": "地図を読み込み中...",
+ "maps-load-failed": "地図を読み込めませんでした。",
+ "maps-markers": "マーカー",
+ "maps-copycoords-prompt": "Ctrl+C、Enter",
+ "maps-searchmarkers-text": "マーカーを絞り込む",
+ "maps-others": "その他",
+ "maps-kml-parsing-failed": "1 件以上の KML ファイルを構文解析できませんでした。これは通常、取得失敗または XML の破損で発生します。",
+ "maps-ns-layer": "レイヤー",
+ "maps-ns-layer-talk": "レイヤー・トーク",
+ "maps-layer-property": "プロパティ",
+ "maps-layer-value": "値",
+ "maps-layer-errors": "エラー",
+ "maps-layerdef-invalid": "{{PLURAL:$1|definition|定義}}が無効です",
+ "maps-layerdef-wrong-namespace": "レイヤー設定は「$1」名前空間のページ限定で有効です",
+ "maps-layerdef-equal-layer-name": "同じレイヤーページ内の複数のレイヤー名はそれぞれ違う必要があります。「$1」は既に他のレイヤー名に使用中です。",
+ "maps-layerpage-usage": "「$1」レイヤーを使った地図のあるページ",
+ "maps-layerpage-nousage": "現在、このレイヤーを使用中のページはありません。",
+ "maps-error-invalid-layertype": "種類「$1」のレイヤーはありません。{{PLURAL:$3|以下の}}種類のみに対応しています: $2",
+ "maps-error-no-layertype": "レイヤーの種類を指定する必要があります。{{PLURAL:$2|この種類のみ|これらの種類}}に対応しています: $1",
+ "validation-error-invalid-layer": "引数「$1」には有効なレイヤーを指定してください。",
+ "validation-error-invalid-layers": "引数「$1」には有効なレイヤーを 1 つ以上指定してください。",
+ "validation-error-no-non-numeric": "引数「$1」には数字以外の文字列を指定してください。",
+ "validation-error-no-non-numerics": "引数「$1」には最低1文字の数字以外の文字列を指定してください。",
+ "maps-layer-of-type": "種類「$1」のレイヤー",
+ "maps-layer-of-type-and-name": "「$1」タイプの「$2」レイヤー",
+ "maps-layer-type-supported-by": "このレイヤーの種類は、{{PLURAL:$2|$1地図サービスでのみ使用できます。|以下の地図サービスで使用できます: $1}}",
+ "maps-coordinates-description": "対応している座標形式間で相互に変換するパーサーフック",
+ "maps-displaymap-description": "ウィキ定義をしたマーカーを使わない地図を表示します。",
+ "maps-distance-description": "サポートする特定の単位で表示した距離を、別の単位に換算します。",
+ "maps-finddestination-description": "出発地点から方位と距離の条件に当てはまる目的地を探します(サポートする選択肢のどのフォーマットでも可能)。",
+ "maps-geodistance-description": "サポートする単位の選択肢の組み合わせに従って、地図上の2地点間の距離を換算します。",
+ "maps-layerdefinition-description": "地図の他の機能と同時に表示できる特別なレイヤーを説明します。",
+ "maps-distance-par-distance": "指定した単位に換算する距離を指定します。",
+ "maps-distance-par-unit": "距離を表示する単位です。",
+ "maps-finddestination-par-location": "最初に表示される場所です。",
+ "maps-finddestination-par-distance": "移動距離です。",
+ "maps-geodistance-par-location1": "距離を計測する2点のうちの第1地点",
+ "maps-geodistance-par-unit": "距離を出力する単位です。",
+ "maps-displaymap-par-coordinates": "地図に表示する 1 か所以上の場所です。これらの場所にはマーカーが付きます。",
+ "maps-displaymap-par-visitedicon": "元のマーカー アイコンがクリックされた後にマーカー アイコンに使用される画像のファイル名",
+ "maps-displaymap-par-label": "ラベルを設定していないマーカーに対して必ずポップアップ表示する、文字列を設定できます。",
+ "maps-displaymap-par-circles": "表示する円形",
+ "maps-displaymap-par-lines": "表示する線",
+ "maps-displaymap-par-maxzoom": "最大ズーム レベル",
+ "maps-displaymap-par-minzoom": "最小ズーム レベル",
+ "maps-displaymap-par-polygons": "表示する多角形",
+ "maps-displaymap-par-rectangles": "表示する矩形",
+ "maps-displaymap-par-wmsoverlay": "WMS オーバーレイを使用",
+ "maps-displaymap-par-geojson": "GeoJSONデータがあるファイルのURLまたはページ名",
+ "maps-fullscreen-button": "全画面表示を切り替え",
+ "maps-fullscreen-button-tooltip": "地図の表示を全画面または埋め込みに切り替えます。",
+ "validation-error-invalid-location": "引数「$1」には有効な場所を指定してください。",
+ "validation-error-invalid-locations": "引数「$1」には有効な場所を 1 つ以上指定してください。",
+ "validation-error-invalid-width": "引数「$1」には有効な幅を指定してください。",
+ "validation-error-invalid-height": "引数「$1」には有効な高さを指定してください。",
+ "validation-error-invalid-distance": "引数「$1」には有効な距離を指定してください。",
+ "validation-error-invalid-distances": "引数「$1」には有効な距離を 1 つ以上指定してください。",
+ "validation-error-invalid-image": "引数「$1」には有効な画像を指定してください。",
+ "validation-error-invalid-images": "引数「$1」には有効な画像を 1 つ以上指定してください。",
+ "validation-error-invalid-goverlay": "引数「$1」には有効なオーバーレイを指定してください。",
+ "validation-error-invalid-goverlays": "引数「$1」には有効なオーバーレイを 1 つ以上指定してください。",
+ "maps-abb-north": "北",
+ "maps-abb-east": "東",
+ "maps-abb-south": "南",
+ "maps-abb-west": "西",
+ "maps-latitude": "緯度:",
+ "maps-longitude": "経度:",
+ "maps-invalid-coordinates": "値 $1 は座標の有効な組み合わせとして認識されませんでした。",
+ "maps_coordinates_missing": "地図に座標が指定されていません。",
+ "maps_geocoding_failed": "以下の{{PLURAL:$2|住所}}を座標に変換できませんでした: $1",
+ "maps_geocoding_failed_for": "指定された{{PLURAL:$2|住所|複数の住所}}の座標への変換に失敗したため、それらを地図から除外して表示します。$1",
+ "maps_unrecognized_coords": "以下の{{PLURAL:$2|座標}}を認識できませんでした: $1",
+ "maps_unrecognized_coords_for": "以下の{{PLURAL:$2|座標}}を認識できなかったため、地図に表示して{{PLURAL:$2|いません}}:\n$1",
+ "maps_map_cannot_be_displayed": "地図を表示できません。",
+ "maps-geocoder-not-available": "地図のジオコーディング機能は利用できません。指定した位置をジオコーディングできません。",
+ "maps_googlemaps3": "Google マップ v3",
+ "maps-leaflet-par-layers": "レイヤー選択機能で利用できるレイヤー。地図を読み込んだとき1番目のレイヤーを表示します。",
+ "maps-leaflet-par-overlaylayers": "地図を読み込むと表示されるオーバーレイのレイヤー。",
+ "maps_click_to_activate": "クリックして地図をアクティブに",
+ "maps_centred_on": "地図の中心は $1、$2。",
+ "maps-par-searchmarkers": "地図の埋め込みフィールドを使い、特定のマーカーの検索を行います。",
+ "maps-par-zoom": "地図の拡大率。マーカーを設定した地図の場合、すべてのマーカーを表示できる最大の拡大率を既定で選びます。",
+ "maps-par-centre": "地図の中心にしたい場所",
+ "maps-par-enable-fullscreen": "全画面表示のボタンを有効にする",
+ "maps-par-kml": "地図に読み込む KML ファイルです。",
+ "maps-par-markercluster": "近接する複数のマーカーを1点に統合できます",
+ "maps-googlemaps3-incompatbrowser": "あなたのブラウザーは Google マップ v3 と互換性がありません。",
+ "maps-googlemaps3-par-type": "最初に表示する地図の種類です。",
+ "maps-googlemaps3-par-types": "種類コントロールで利用できる、地図の種類です。",
+ "maps-googlemaps3-par-layers": "地図に読み込む特別なレイヤーです。",
+ "maps-googlemaps3-par-controls": "地図上に配置するコントロールです。",
+ "maps-googlemaps3-par-zoomstyle": "ズーム コントロールのスタイルです。",
+ "maps-googlemaps3-par-typestyle": "種類コントロールのスタイルです。",
+ "maps-googlemaps3-par-kmlrezoom": "KMLレイヤーを読み込むとマップを再度ズームし直します。",
+ "mapeditor": "地図の編集",
+ "specialpages-group-maps": "地図",
+ "mapeditor-parser-error": "メタデータの構文解析でエラーが発生しました。利用者の入力を無視しています。",
+ "mapeditor-none-text": "なし",
+ "mapeditor-done-button": "完了",
+ "mapeditor-remove-button": "除去",
+ "mapeditor-import-button2": "取り込み",
+ "mapeditor-export-button": "ウィキコードに書き出し",
+ "mapeditor-import-button": "ウィキコードから取り込み",
+ "mapeditor-select-button": "この多角形を選択",
+ "mapeditor-mapparam-button": "地図のパラメーターを編集",
+ "mapeditor-clear-button": "地図を消去",
+ "mapeditor-code-title": "ウィキコード",
+ "mapeditor-import-title": "ウィキコードの取り込み",
+ "mapeditor-form-title": "詳細を編集",
+ "mapeditor-link-title-switcher-popup-text": "テキスト付きポップアップ",
+ "mapeditor-link-title-switcher-link-text": "リンク",
+ "mapeditor-form-field-title": "タイトル",
+ "mapeditor-form-field-text": "テキスト",
+ "mapeditor-form-field-link": "リンク",
+ "mapeditor-form-field-icon": "アイコン",
+ "mapeditor-form-field-group": "グループ",
+ "mapeditor-form-field-inlinelabel": "インライン ラベル",
+ "mapeditor-form-field-strokecolor": "線の色",
+ "mapeditor-form-field-strokeopacity": "線の不透明度",
+ "mapeditor-form-field-strokeweight": "線幅",
+ "mapeditor-form-field-fillcolor": "塗りつぶしの色",
+ "mapeditor-form-field-fillopcaity": "塗りつぶしの不透明度",
+ "mapeditor-form-field-showonhover": "ホバー時のみ表示",
+ "mapeditor-mapparam-title": "地図のパラメーターの編集",
+ "mapeditor-mapparam-defoption": "-パラメーターを選択-",
+ "mapeditor-imageoverlay-button": "画像オーバーレイを追加",
+ "mapeditor-form-field-image": "画像",
+ "mapeditor-imageoverlay-title": "画像オーバーレイの詳細",
+ "mapeditor-form-field-visitedicon": "訪問済みアイコン",
+ "semanticmaps-unrecognizeddistance": "値$1は有効な距離ではありません。",
+ "semanticmaps-kml-link": "KMLファイルを閲覧",
+ "semanticmaps-default-kml-pagelink": "ページ$1を表示",
+ "semanticmaps-latitude": "緯度: $1",
+ "semanticmaps-longitude": "経度: $1",
+ "semanticmaps-altitude": "高度: $1",
+ "semanticmaps-forminput-locations": "場所",
+ "semanticmaps-par-staticlocations": "問い合わせたデータと共に地図に追加する場所の列挙です。display_points と同様に、区切り文字としてチルダ「~」を使用して、場所ごとにタイトル、説明、アイコンを追加できます。",
+ "semanticmaps-par-showtitle": "マーカーの情報ウィンドウのタイトルを表示するかどうか。情報ウィンドウのコンテンツをフォーマットするためにテンプレートを使用するとき、これを無効にすると便利です。",
+ "semanticmaps-par-hidenamespace": "マーカー情報ウィンドウに名前空間名を表示する",
+ "semanticmaps-par-centre": "地図の中心。提供されていないときは、自動的に地図上のすべてのマーカーを表示するための最適な中心が選択されます。",
+ "semanticmaps-par-template": "情報ウィンドウのコンテンツの整形に使用するテンプレートです。",
+ "semanticmaps-par-geocodecontrol": "ジオコーディングコントロールを表示します。",
+ "semanticmaps-par-activeicon": "アクティブなページがクエリ結果と等しい場合に、デフォルトのアイコンの代わりに表示されるアイコン",
+ "semanticmaps-par-pagelabel": "「はい」に設定すると、マーカーの緯度経度を含むページにリンクされた「インライン ラベル」がすべてのマーカーに付きます。",
+ "semanticmaps-par-userparam": "テンプレートを使用している場合に、各テンプレート呼出しに渡す値",
+ "semanticmaps-kml-text": "各ページに関連付けられたテキストです。クエリに追加的なプロパティがある場合は上書きされます。",
+ "semanticmaps-kml-title": "結果の既定のタイトル",
+ "semanticmaps-kml-linkabsolute": "リンクは絶対表記 (= 相対表記の対義語) にしてください。",
+ "semanticmaps-kml-pagelinktext": "ページへのリンクに使用するテキスト ($1 はページ名に置換される)",
+ "semanticmaps-shapes-improperformat": "$1 を不適切な形式に整形しようとしました。整形の説明文書を参照してください。",
+ "semanticmaps-shapes-missingshape": "$1 の図形が見つかりません。利用できる図形について説明文書を参照してください。",
+ "validator-type-mapsimageoverlay": "画像オーバーレイ",
+ "validator-type-mapsimageoverlay-list": "画像オーバーレイの一覧",
+ "validator-type-mapslocation-list": "位置の一覧",
+ "validator-type-mapspolygon": "地図ポリゴン",
+ "validator-type-mapspolygon-list": "地図ポリゴンの一覧",
+ "validator-type-jsonfile": "テキスト"
+}
diff --git a/www/wiki/extensions/Maps/i18n/jv.json b/www/wiki/extensions/Maps/i18n/jv.json
new file mode 100644
index 00000000..6581003b
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/jv.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": [],
+ "maps-abb-north": "L",
+ "maps-abb-east": "W",
+ "maps-abb-south": "Kdl",
+ "maps-abb-west": "Kln",
+ "maps-latitude": "Latituda:",
+ "maps-longitude": "Longituda:"
+}
diff --git a/www/wiki/extensions/Maps/i18n/ka.json b/www/wiki/extensions/Maps/i18n/ka.json
new file mode 100644
index 00000000..13a82651
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ka.json
@@ -0,0 +1,75 @@
+{
+ "@metadata": {
+ "authors": [
+ "David1010",
+ "Temuri rajavi"
+ ]
+ },
+ "right-geocode": "გეოკოდირება",
+ "maps_map": "რუკა",
+ "maps-loading-map": "იტვირთება რუკა...",
+ "maps-load-failed": "რუკის ჩატვირთვა შეუძლებელია!",
+ "maps-markers": "ნიშნები",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "ნიშნების ფილტრი",
+ "maps-others": "სხვები",
+ "maps-ns-layer": "ფენა",
+ "maps-ns-layer-talk": "ფენის განხილვა",
+ "maps-layer-property": "თვისება",
+ "maps-layer-value": "მნიშვნელობა",
+ "maps-layer-errors": "შეცდომები",
+ "validation-error-invalid-layer": "პარამეტრი $1 უნდა იყოს კორექტული ფენა.",
+ "validation-error-invalid-layers": "პარამეტრი $1 უნდა იყოს ერთი ან მეტი კორექტული ფენა.",
+ "maps-layer-of-type": "$1 ტიპის ფენა",
+ "maps-finddestination-par-location": "საწყისი მდებარეობა.",
+ "maps-finddestination-par-bearing": "საწყისი მიმართულება.",
+ "maps-finddestination-par-distance": "სამოგზაურო მანძილი.",
+ "maps-geocode-par-location": "მისამართი რომლის გეოკოდირებაც გსურთ.",
+ "maps-displaymap-par-maxzoom": "გადიდების მაქსიმალური დონე",
+ "maps-displaymap-par-minzoom": "შემცირების მაქსიმალური დონე",
+ "validation-error-invalid-location": "პარამეტრი $1 უნდა იყოს ნამდვილი მდებარეობის.",
+ "validation-error-invalid-locations": "პარამეტრი $1 უნდა შეიცავდეს ერთ ან რამდენიმე ნამდვილ მდებარეობას.",
+ "validation-error-invalid-width": "პარამეტრი $1 უნდა იყოს ნამდვილი სიგანის.",
+ "validation-error-invalid-height": "პარამეტრი $1 უნდა იყოს ნამდვილი სიმაღლის.",
+ "validation-error-invalid-distance": "პარამეტრი $1 უნდა იყოს ნამდვილი მანძილი.",
+ "validation-error-invalid-distances": "პარამეტრი $1 უნდა შეიცავდეს ერთ ან რამდენიმე ნამდვილ მანძილს.",
+ "validation-error-invalid-image": "პარამეტრი $1 უნდა იყოს ნამდვილი სურათი.",
+ "validation-error-invalid-images": "პარამეტრი $1 უნდა იყოს ერთი ან მეტი ნამდვილი სურათი.",
+ "maps-abb-north": "ჩ",
+ "maps-abb-east": "ა",
+ "maps-abb-south": "ს",
+ "maps-abb-west": "დ",
+ "maps-latitude": "განედი:",
+ "maps-longitude": "გრძედი:",
+ "maps_coordinates_missing": "რუკის კოორდინატები არ არის მითითებული.",
+ "maps_click_to_activate": "დააჭირეთ რუკის გასააქტიურებლად",
+ "maps_centred_on": "რუკის ცენტრი — $1, $2.",
+ "maps-googlemaps3-incompatbrowser": "თქვენი ბრაუზერი არ არის თავსებადი Google Maps v3-თან.",
+ "maps-googlemaps3-par-poi": "ღირსშესანიშნაობების ჩვენება.",
+ "mapeditor": "რუკის რედაქტორი",
+ "specialpages-group-maps": "რუკები",
+ "mapeditor-none-text": "არა",
+ "mapeditor-done-button": "გაკეთდა",
+ "mapeditor-remove-button": "წაშლა",
+ "mapeditor-import-button2": "იმპორტი",
+ "mapeditor-clear-button": "რუკის გასუფთავება",
+ "mapeditor-code-title": "ვიკი კოდი",
+ "mapeditor-form-title": "დეტალების რედაქტირება",
+ "mapeditor-link-title-switcher-link-text": "ბმული",
+ "mapeditor-form-field-title": "სათაური",
+ "mapeditor-form-field-text": "ტექსტი",
+ "mapeditor-form-field-link": "ბმული",
+ "mapeditor-form-field-icon": "ხატულა",
+ "mapeditor-form-field-group": "ჯგუფი",
+ "mapeditor-form-field-showonhover": "მხოლოდ მიტანისას ჩვენება",
+ "mapeditor-mapparam-title": "რუკის პარამეტრების რედაქტირება",
+ "mapeditor-mapparam-defoption": "-აირჩიეთ პარამეტრი-",
+ "mapeditor-form-field-image": "სურათი",
+ "semanticmaps-kml-link": "KML ფაილის ხილვა",
+ "semanticmaps-default-kml-pagelink": "გვერდი $1 ხილვა",
+ "semanticmaps-latitude": "განედი: $1",
+ "semanticmaps-longitude": "გრძედი: $1",
+ "semanticmaps-altitude": "სიმაღლე: $1",
+ "semanticmaps-forminput-locations": "მდებარეობები",
+ "semanticmaps-kml-title": "საწყისი სათაური შედეგებისათვის"
+}
diff --git a/www/wiki/extensions/Maps/i18n/km.json b/www/wiki/extensions/Maps/i18n/km.json
new file mode 100644
index 00000000..c5eb0860
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/km.json
@@ -0,0 +1,18 @@
+{
+ "@metadata": {
+ "authors": [
+ "គីមស៊្រុន"
+ ]
+ },
+ "maps_map": "ផែនទី",
+ "maps-loading-map": "កំពុងផ្ទុកផែនទី...",
+ "maps-load-failed": "ផ្ទុកផែនទីមិនបានសម្រេចទេ!",
+ "maps-markers": "សញ្ញាសម្គាល់",
+ "maps-others": "ផ្សេងទៀត",
+ "maps-abb-north": "ជ",
+ "maps-abb-east": "ក",
+ "maps-abb-south": "ត",
+ "maps-abb-west": "ល",
+ "maps-latitude": "រយៈទទឹង៖",
+ "maps-longitude": "រយៈបណ្តោយ៖"
+}
diff --git a/www/wiki/extensions/Maps/i18n/ko.json b/www/wiki/extensions/Maps/i18n/ko.json
new file mode 100644
index 00000000..b687427b
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ko.json
@@ -0,0 +1,208 @@
+{
+ "@metadata": {
+ "authors": [
+ "Hym411",
+ "Jskang",
+ "Priviet",
+ "아라",
+ "Revi",
+ "Macofe",
+ "Ykhwong",
+ "Garam"
+ ]
+ },
+ "maps-desc": "위키 문서에 동적 지도를 넣고, 주소의 좌표화와 다른 지리적 작업을 할 수 있습니다",
+ "right-geocode": "좌표화",
+ "maps_map": "지도",
+ "maps-tracking-category": "지도를 지도 확장 기능으로 표시하는 문서",
+ "maps-loading-map": "지도를 불러오는 중...",
+ "maps-load-failed": "지도를 불러올 수 없습니다!",
+ "maps-markers": "표시",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "표시를 필터",
+ "maps-others": "기타",
+ "maps-kml-parsing-failed": "하나 이상의 KML 파일을 구문 분석하는 데 실패했습니다. 일반적으로 이것은 얻기 실패나 잘못된 형식의 XML이기 때문에 일어납니다.",
+ "maps-ns-layer": "레이어",
+ "maps-ns-layer-talk": "레이어 토론",
+ "maps-layer-property": "속성",
+ "maps-layer-value": "값",
+ "maps-layer-errors": "오류",
+ "maps-layerdef-invalid": "유효하지 않은 정의{{PLURAL:$1|}}",
+ "maps-layerdef-invalid-fatal": "치명적으로 잘못된 정의",
+ "maps-layerdef-wrong-namespace": "레이어 정의가 \"$1\" 이름공간의 문서에만 유효하지 않습니다",
+ "maps-layerdef-equal-layer-name": "같은 레이어 문서 안에서 레이어 이름은 고유해야 합니다. \"$1\"은 이미 다른 레이어의 이름으로 사용되고 있습니다.",
+ "maps-layerpage-usage": "\"$1\" 레이어를 사용하는 지도와 문서",
+ "maps-layerpage-nousage": "현재 이 레이어를 사용하는 문서가 없습니다.",
+ "maps-error-invalid-layertype": "\"$1\" 레이어 종류가 없습니다. {{PLURAL:$3|다음 종류}}만 지원합니다: $2",
+ "maps-error-no-layertype": "레이어 종류를 지정해야 합니다. {{PLURAL:$2|다음 종류만|다음 종류를}} 지원합니다: $1",
+ "validation-error-invalid-layer": "$1 변수는 올바른 레이어여야 합니다.",
+ "validation-error-invalid-layers": "$1 변수는 하나 이상의 올바른 레이어여야 합니다.",
+ "validation-error-no-non-numeric": "\"$1\" 매개변수는 숫자를 포함하지 않는 문자열이어야 합니다.",
+ "validation-error-no-non-numerics": "\"$1\" 매개변수는 하나 또는 그 이상의 숫자가 아닌 문자열이어야 합니다.",
+ "maps-layer-of-type": "$1 종류의 레이어",
+ "maps-layer-of-type-and-name": "\"$1\" 형식의 \"$2\" 레이어",
+ "maps-layer-type-supported-by": "이 레이어 유형은 {{PLURAL:$2|$1 매핑 서비스에서만 사용할 수 있습니다.|다음 매핑 서비스에서 사용할 수 있습니다: $1}}",
+ "maps-coordinates-description": "지원되는 좌표 형식간에 서로 변환하는 파서 훅입니다.",
+ "maps-displaymap-description": "지도에 위키 정의한 표시 없이 지리적 지도를 표시합니다.",
+ "maps-distance-description": "다른 단위를 사용하여 해당하는 특정 지원되는 단위를 사용하여 거리를 변환합니다.",
+ "maps-finddestination-description": "주어진 시작점과 (지원되는 형식을 사용할 수 있습니다) 초기 방위, 거리로 목적지를 찾습니다.",
+ "maps-geocode-description": "주소, 즉 좌표 집합으로 인간이 읽을 수 있는 위치를 전환하는 좌표화를 활성화합니다. 매핑 서비스와 혼동하지 않아야 하는 여러 좌표화 서비스에 대한 지원이 있습니다.",
+ "maps-geodistance-description": "지원하는 형식으로 두 점 사이의 지리적 거리를 계산합니다.",
+ "maps-mapsdoc-description": "기본 값과 설명과 함께 지정된 좌표화 서비스에 대한 변수로 테이블을 표시합니다.",
+ "maps-layerdefinition-description": "지도기능으로 표시할 수 있는 사용자 정의 레이어를 설명하세요.",
+ "maps-mapsdoc-par-service": "변수 설명문서를 표시할 좌표화 서비스입니다.",
+ "maps-mapsdoc-par-language": "설명문을 표시할 언어입니다. 이러한 번역을 사용할 수 없으면 영어가 대신 사용됩니다.",
+ "maps-coordinates-par-location": "형식을 지정할 좌표입니다.",
+ "maps-coordinates-par-format": "죄표에 대한 대상 형식입니다.",
+ "maps-coordinates-par-directional": "좌표가 방향이 출력되어야 할 것인지를 나타냅니다.",
+ "maps-par-scrollwheelzoom": "마우스 스크롤링을 활성화하는 것이 좋은지의 여부를 표시합니다.",
+ "maps-distance-par-distance": "지정된 단위로 해당하는 변환할 거리입니다.",
+ "maps-distance-par-decimals": "결과 값에 사용할 최대 소수 자릿수입니다.",
+ "maps-distance-par-unit": "거리를 출력할 단위입니다.",
+ "maps-finddestination-par-location": "처음 위치입니다.",
+ "maps-finddestination-par-bearing": "초기 방위입니다.",
+ "maps-finddestination-par-distance": "이동 거리입니다.",
+ "maps-finddestination-par-format": "목적지를 출력할 형식입니다.",
+ "maps-finddestination-par-directional": "대상의 형식이 방향성이 있어야 할 것인지를 나타냅니다.",
+ "maps-geocode-par-location": "좌표화를 원하는 주소입니다.",
+ "maps-geocode-par-format": "결과 좌표에 대한 형식입니다.",
+ "maps-geocode-par-directional": "좌표가 방향이 출력되어야 할 것인지를 나타냅니다.",
+ "maps-geodistance-par-location1": "집합에서 사이의 거리를 계산하는 첫 번째 지점입니다.",
+ "maps-geodistance-par-location2": "집합에서 사이의 거리를 계산하는 두 번째 지점입니다.",
+ "maps-geodistance-par-unit": "거리를 출력할 단위입니다.",
+ "maps-geodistance-par-decimals": "결과 값에 사용할 최대 소수 자릿수입니다.",
+ "maps-displaymap-par-mappingservice": "지도를 생성하는데 사용될 매핑 서비스를 설정할 수 있습니다.",
+ "maps-displaymap-par-coordinates": "지도에 표시할 하나 이상의 위치입니다. 표시로 표시됩니다.",
+ "maps-displaymap-par-visitedicon": "원래의 마커가 클릭된 후 마커 아이콘에 사용될 영상 파일 이름입니다",
+ "maps-displaymap-par-zoom": "지도의 확대 수준을 설정할 수 있습니다.\n제공하지 않고 여러 표시가 지도에 존재하지 않으면 설정한 기본값이 아닌 최고의 맞춤 확대로 설정됩니다.",
+ "maps-displaymap-par-centre": "display_point에 대한 지도의 중심 좌표를 설정할 수 있습니다.\n주소와 좌표를 모두 사용할 수 있습니다.\n이 속성이 제공되어 있지 않은 경우 지도는 제공한 표시에 대한 자체 중심에 있거나 제공한 표시 사이에 있습니다.",
+ "maps-displaymap-par-title": "특정 제목이 없는 모든 표시의 팝업에 보여줄 텍스트를 설정할 수 있습니다.\n레이블과 함께 사용하면 제목은 굵고 밑줄이 그어집니다.",
+ "maps-displaymap-par-label": "특정 레이블이 없는 마든 표시의 팝업에 보여줄 텍스트를 설정할 수 있습니다.",
+ "maps-displaymap-par-icon": "모든 표시에 사용하는 아이콘을 설정할 수 있게 합니다.",
+ "maps-displaymap-par-circles": "표시할 동그라미",
+ "maps-displaymap-par-copycoords": "좌표를 복사한 위치를 클릭할 때 대화상자를 표시합니다.",
+ "maps-displaymap-par-lines": "표시할 선",
+ "maps-displaymap-par-maxzoom": "최대 확대 수준",
+ "maps-displaymap-par-minzoom": "최소 확대 수준",
+ "maps-displaymap-par-polygons": "표시할 다각형",
+ "maps-displaymap-par-rectangles": "표시할 직사각형",
+ "maps-displaymap-par-static": "지도를 정적으로 만듭니다.",
+ "maps-displaymap-par-wmsoverlay": "WMS 중첩 사용",
+ "maps-displaymap-par-geojson": "GeoJSON 데이터가 포함된 파일의 URL 또는 문서 이름",
+ "maps-fullscreen-button": "전체화면으로 전환",
+ "maps-fullscreen-button-tooltip": "지도를 전체 화면 또는 삽입된 형태로 보기",
+ "validation-error-invalid-location": "$1 변수는 올바른 위치여야 합니다.",
+ "validation-error-invalid-locations": "$1 변수는 하나 이상의 올바른 위치여야 합니다.",
+ "validation-error-invalid-width": "$1 변수는 올바른 너비여야 합니다.",
+ "validation-error-invalid-height": "$1 변수는 올바른 높이여야 합니다.",
+ "validation-error-invalid-distance": "$1 변수는 올바른 거리여야 합니다.",
+ "validation-error-invalid-distances": "$1 변수는 하나 이상의 올바른 거리여야 합니다.",
+ "validation-error-invalid-image": "$1 변수는 올바른 그림이어야 합니다.",
+ "validation-error-invalid-images": "$1 변수는 하나 이상의 올바른 그림이어야 합니다.",
+ "validation-error-invalid-goverlay": "$1 변수는 올바른 오버레이여야 합니다.",
+ "validation-error-invalid-goverlays": "$1 변수는 하나 이상의 올바른 오버레이여야 합니다.",
+ "maps-abb-north": "북",
+ "maps-abb-east": "동",
+ "maps-abb-south": "남",
+ "maps-abb-west": "서",
+ "maps-latitude": "위도:",
+ "maps-longitude": "경도:",
+ "maps-invalid-coordinates": "$1 값은 죄표의 올바른 값으로 인식되지 않았습니다.",
+ "maps_coordinates_missing": "지도에 제공한 좌표가 없습니다.",
+ "maps_geocoding_failed": "다음 {{PLURAL:$2|주소}}는 좌표화할 수 없습니다: $1.",
+ "maps_geocoding_failed_for": "다음 {{PLURAL:$2|주소}}는 좌표화할 수 없고 지도에서 생략{{PLURAL:$2|되었습니다}}:\n$1",
+ "maps_unrecognized_coords": "다음 {{PLURAL:$2|좌표}}는 인식할 수 없습니다: $1.",
+ "maps_unrecognized_coords_for": "다음 {{PLURAL:$2|좌표}}는 인식할 수 없고 지도에서 생략{{PLURAL:$2|되었습니다}}:\n$1",
+ "maps_map_cannot_be_displayed": "지도를 보여줄 수 없습니다.",
+ "maps-geocoder-not-available": "지도의 좌표화 기능을 사용할 수 없습니다. 당신의 위치는 좌표화할 수 없습니다.",
+ "maps_googlemaps3": "Google 지도 v3",
+ "maps-leaflet-par-layers": "레이서 선택기에서 사용할 수 있는 레이어입니다. 첫 레이어는 지도를 불러올 때 보여줍니다.",
+ "maps_click_to_activate": "지도를 활성화하려면 클릭하세요",
+ "maps_centred_on": "$1, $2 지도 중심",
+ "maps-par-mappingservice": "지도를 생성하는데 사용될 매핑 서비스를 설정할 수 있습니다.",
+ "maps-par-resizable": "지도의 오른쪽 아래 모서리에 드래그하여 지도 크기를 조절할 수 있도록 합니다.",
+ "maps-par-zoom": "지도에 대한 확대 수준입니다. 표시로 된 지도에 대해서는 여전히 모든 표시를 보여주는 수준으로 대부분 확대되도록 기본 설정됩니다.",
+ "maps-par-width": "지도의 너비을 설정할 수 있습니다. 기본적으로 픽셀은 단위로 간주되지만, 명시적으로 이러한 단위 중 하나를 지정할 수 있습니다: px, ex, em, %.",
+ "maps-par-height": "지도의 높이를 설정할 수 있습니다. 기본적으로 픽셀은 단위로 간주되지만, 명시적으로 이러한 단위 중 하나를 지정할 수 있습니다: px, ex, em, %.",
+ "maps-par-centre": "지도의 중심으로 할 위치",
+ "maps-par-enable-fullscreen": "전체 화면 버튼 활성화",
+ "maps-par-kml": "지도에 불러올 KML 파일입니다.",
+ "maps-googlemaps3-incompatbrowser": "브라우저는 Google 지도 v3와 호환되지 않습니다.",
+ "maps-googlemaps3-par-type": "처음 보여줄 지도 종류입니다.",
+ "maps-googlemaps3-par-types": "종류 컨트롤을 통해 사용할 수 있는 지도 종류입니다.",
+ "maps-googlemaps3-par-layers": "지도에 불러오는 특수 레이어입니다.",
+ "maps-googlemaps3-par-controls": "지도에 놓을 컨트롤입니다.",
+ "maps-googlemaps3-par-zoomstyle": "줌 컨트롤의 스타일입니다.",
+ "maps-googlemaps3-par-typestyle": "종류 컨트롤의 스타일입니다.",
+ "maps-googlemaps3-par-autoinfowindows": "문서를 불러오고 나서 모든 정보 창을 자동으로 엽니다.",
+ "maps-googlemaps3-par-gkml": "지도에 불러올 Google이 호스트하는 KML 파일입니다.",
+ "maps-googlemaps3-par-kmlrezoom": "KML 레이어를 불러오고 나서 지도를 다시 확대합니다.",
+ "maps-googlemaps3-par-poi": "관심 지점을 보여줍니다.",
+ "mapeditor": "지도 편집기",
+ "specialpages-group-maps": "지도",
+ "mapeditor-parser-error": "메타데이터를 구문 분석하는 동안 오류가 발생했습니다. 사용자 입력을 무시합니다.",
+ "mapeditor-none-text": "없음",
+ "mapeditor-done-button": "완료",
+ "mapeditor-remove-button": "제거",
+ "mapeditor-import-button2": "가져오기",
+ "mapeditor-export-button": "위키 코드로 내보내기",
+ "mapeditor-import-button": "위키 코드에서 가져오기",
+ "mapeditor-select-button": "이 다각형 선택",
+ "mapeditor-mapparam-button": "지도 변수 편집",
+ "mapeditor-clear-button": "지도 지우기",
+ "mapeditor-code-title": "위키 코드",
+ "mapeditor-import-title": "위키 코드 가져오기",
+ "mapeditor-import-note": "파서는 위키 코드에 매우 엄격한 형식을 예상하는 것에 주의하세요. 여기에 입력한 코드는 내보내기 기능으로 출력되는 코드와 일치해야 합니다.",
+ "mapeditor-form-title": "자세한 사항 편집",
+ "mapeditor-link-title-switcher-popup-text": "텍스트 팝업",
+ "mapeditor-link-title-switcher-link-text": "링크",
+ "mapeditor-form-field-title": "제목",
+ "mapeditor-form-field-text": "텍스트",
+ "mapeditor-form-field-link": "링크",
+ "mapeditor-form-field-icon": "아이콘",
+ "mapeditor-form-field-group": "그룹",
+ "mapeditor-form-field-inlinelabel": "레이블 안쪽",
+ "mapeditor-form-field-strokecolor": "선 색",
+ "mapeditor-form-field-strokeopacity": "선 투명토",
+ "mapeditor-form-field-strokeweight": "선 굵기",
+ "mapeditor-form-field-fillcolor": "채우기 색",
+ "mapeditor-form-field-fillopcaity": "채우기 투명도",
+ "mapeditor-form-field-showonhover": "가리켰을 때에만 보이기",
+ "mapeditor-mapparam-title": "지도 변수 편집",
+ "mapeditor-mapparam-defoption": "-변수 선택-",
+ "mapeditor-imageoverlay-button": "그림 배치 추가",
+ "mapeditor-form-field-image": "그림",
+ "mapeditor-imageoverlay-title": "그림 배치 자세한 사항",
+ "mapeditor-form-field-visitedicon": "방문한 아이콘",
+ "semanticmaps-unrecognizeddistance": "$1 값은 올바른 거리가 아닙니다.",
+ "semanticmaps-kml-link": "KML 파일 보기",
+ "semanticmaps-default-kml-pagelink": "$1 문서 보기",
+ "semanticmaps-latitude": "위도: $1",
+ "semanticmaps-longitude": "경도: $1",
+ "semanticmaps-altitude": "고도: $1",
+ "semanticmaps-forminput-locations": "위치",
+ "semanticmaps-par-staticlocations": "쿼리된 데이터와 함께 지도를 추가하는 위치의 목록입니다. display_points와 같이, 구분자로 물결표 \"~\"를 사용하여 위치마다 제목과 설명, 아이콘을 추가할 수 있습니다.",
+ "semanticmaps-par-showtitle": "표시 정보 창에서 제목에 보여주거나 보여주지 않습니다. 정보 창 내용의 형식에 틀을 사용할 때 이를 비활성화하면 편리합니다.",
+ "semanticmaps-par-hidenamespace": "표시 정보 창의 이름공간 제목 보이기",
+ "semanticmaps-par-centre": "지도의 가운데입니다. 제공하지 않으면, 지도는 자동으로 지도에서 모든 표시를 표시할 최적의 가운데를 선택합니다.",
+ "semanticmaps-par-template": "정보 창 내용을 형식에 사용하는 틀입니다.",
+ "semanticmaps-par-geocodecontrol": "좌표화 컨트롤을 보여줍니다.",
+ "semanticmaps-par-activeicon": "활성화된 문서는 쿼리 결과와 같을 때 아이콘이 기본값 표시기 대신에 표시해야 합니다",
+ "semanticmaps-par-pagelabel": "\"yes\"로 설정하면, 모든 마커가 마커의 좌표를 포함하는 문서로 연결된 링크와 \"inlineLabel\"을 가집니다.",
+ "semanticmaps-par-userparam": "틀이 사용되는 경우 각 틀 호출에 전달되는 값입니다",
+ "semanticmaps-kml-text": "각 문서와 연관된 텍스트입니다. 추가적인 쿼리된 속성이 있으면 덮어씁니다.",
+ "semanticmaps-kml-title": "결과에 대한 기본 제목",
+ "semanticmaps-kml-linkabsolute": "링크는 (상대적와 반대인) 절대적이어야 합니다",
+ "semanticmaps-kml-pagelinktext": "$1 문서는 문서 제목으로 바뀔 문서의 링크에 사용하는 텍스트",
+ "semanticmaps-shapes-improperformat": "$1의 부적절한 형식입니다. 형식에 대해서는 설명서를 참조하세요.",
+ "semanticmaps-shapes-missingshape": "$1에 대한 모양을 찾을 수 없습니다. 사용할 수 있는 모양에 대해서는 설명서를 참조하세요",
+ "validator-type-mapscircle-list": "원 목록",
+ "validator-type-mapsimageoverlay": "영상 중첩",
+ "validator-type-mapsimageoverlay-list": "영상 중첩 목록",
+ "validator-type-mapsline-list": "선 목록",
+ "validator-type-mapslocation": "지리적 위치",
+ "validator-type-mapslocation-list": "위치 목록",
+ "validator-type-mapsrectangle-list": "직사각형 목록",
+ "validator-type-wmsoverlay": "웹 지도 서비스 오버레이",
+ "validator-type-jsonfile": "텍스트"
+}
diff --git a/www/wiki/extensions/Maps/i18n/ksh.json b/www/wiki/extensions/Maps/i18n/ksh.json
new file mode 100644
index 00000000..943d2e78
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ksh.json
@@ -0,0 +1,69 @@
+{
+ "@metadata": {
+ "authors": [
+ "Purodha",
+ "Macofe"
+ ]
+ },
+ "maps-desc": "Määt et müjjelesch, bewääschlesche Landkaate en Sigge em Wiki aanzezeije, Addräße en Koodinaate op de Ääd ömzerääschne, un ander esu en Funxjuhne",
+ "maps_map": "Kaat",
+ "maps-loading-map": "Mer sen en Kaat aam lahde&nbsp;…",
+ "maps-markers": "Makeeronge",
+ "maps-searchmarkers-text": "Makkehronge ußsöhke",
+ "maps-others": "sönß wää",
+ "maps-layer-property": "Eijeschaff",
+ "maps-layer-value": "Wäät",
+ "maps-layer-errors": "Fähler",
+ "maps-distance-par-unit": "De Einheit för der Afschtand dren ußzejävve.",
+ "maps-displaymap-par-coordinates": "Dä Plaz, dä aanfänglesch en de Medde vun dä Kaat lijje sull.",
+ "maps-displaymap-par-visitedicon": "Dä Nahme vun dä Dattei med enem Belldsche, wat jezeisch weed, wann obb et orschprönglesche Belldsche jeklek woode es.",
+ "maps-displaymap-par-icon": "Määd et müjjelesch, e Minni_Belldsche faßzelääje, waoh met all de Makeerunge jezeish wääde.",
+ "maps-displaymap-par-copycoords": "Zeisch e Finstersche, öm de Ko'odenahte om Johbus druß ze koppehre, wam_mer obb ene Plaz klek.",
+ "validation-error-invalid-location": "Dä Parameeter $1 mos ene jölteje Plaz sin.",
+ "validation-error-invalid-locations": "Dä Parameeter $1 moß eine jölteje Plaz udder an Aanzahl jöltijje Pläz sin.",
+ "validation-error-invalid-width": "Dä Parameeter $1 mos en jölteje Breedt sin.",
+ "validation-error-invalid-height": "Dä Parameeter $1 mos en jölteje Hühde sin.",
+ "validation-error-invalid-distance": "Dä Parameeter $1 mos ene jölteje Affshtand sin.",
+ "validation-error-invalid-distances": "Dä Parameeter $1 moß eine jölteje Affshtand udder an Aanzahl jöltijje Affshtänd sin.",
+ "validation-error-invalid-image": "Dä Parameeter $1 mos e jöltesch Beld sin.",
+ "validation-error-invalid-images": "Dä Parameeter $1 moß ei jöltesch Beld udder an Aanzahl jöltijje Belder sin.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "O",
+ "maps-abb-south": "S",
+ "maps-abb-west": "W",
+ "maps-latitude": "Breedt om Jlobus:",
+ "maps-longitude": "Längde om Jlobus:",
+ "maps-invalid-coordinates": "Dä Wäät $1 künne mer nit als ene Saz vun Koodinahte vershtonn.",
+ "maps_coordinates_missing": "Mer han kein Koodinaate för di Kaat.",
+ "maps_geocoding_failed": "{{PLURAL:$2|Di Koodinaat|De Koodinaate|Kein Koodinaat}} om Jlobus för di {{PLURAL:$2|aanjejovve Adräß wohr|aanjejovve Adräße wohre|kein aanjejovve Adräß wohr}} Kappes: $1. Di Kaat künne mer su nit aanzeije.",
+ "maps_geocoding_failed_for": "De Koodinaate om Jlobus för {{PLURAL:$2|ein|paa|kein}} vun dä aanjejovve Adräße {{PLURAL:$2|es|wohre|Fähler!}} Kappes. Di {{PLURAL:$2|es|sin|Fähler!}} dröm nit op dä Kaat. De fottjelohße {{PLURAL:$2|es|sin|Fähler!}}: $1",
+ "maps_unrecognized_coords": "He di Koordinate kunnte mer nit verschtonn: $1.",
+ "maps_unrecognized_coords_for": "He di {{PLURAL:$2|Koordinat kunnt|Koordinate kunnte}} mer nit verschtonn un dröm {{PLURAL:$2|es|sin}} se nit en de Kaat opjenumme woode:\n$1",
+ "maps_map_cannot_be_displayed": "Di Kaat künne mer nit aanzeije.",
+ "maps_googlemaps3": "<i lang=\"en\">Google-Maps-v3</i>-Kaat",
+ "maps_click_to_activate": "Donn klecke, öm op di Kaat ze jonn",
+ "maps_centred_on": "De Kaat met $1, $2 en de Medde.",
+ "maps-par-markercluster": "Määd et müjjelesch, benaachbaate Makehronge zesamme ze faße.",
+ "maps-googlemaps3-par-imageoverlays": "Määd et müjjelesch, e Beld obb em aanjejovve Plaz op dä Kaat aanzeije ze lohße.",
+ "maps-googlemaps3-par-kmlrezoom": "Donn di Kaat norr_ens zoome, nohdäm de layers vum KLM jelaade sin.",
+ "semanticmaps-unrecognizeddistance": "Dä Wäät „$1“ es keine jölteje Afschtand.",
+ "semanticmaps-kml-link": "De KML-Dattei belooere",
+ "semanticmaps-kml": "Äxpoot als KML",
+ "semanticmaps-default-kml-pagelink": "De Sigg „$1“ belooere",
+ "semanticmaps-latitude": "Dä Breedejrad om Jloobos: $1",
+ "semanticmaps-longitude": "Dä Längejraad om Jloobos: $1",
+ "semanticmaps-altitude": "De Hühde: $1",
+ "semanticmaps-forminput-locations": "Oote",
+ "semanticmaps-par-staticlocations": "En Leß met Pläz, di zosamme met dä nohjefrochte Aanjabe op di Kaat sulle. Wi bei <code lang=\"en\">display_points</code> kam_mer ene Tittel, ene Täx doh zoh, un e Minni_Belldsche\nför jeede Plaz aanjävve, med enem Schlängelsche (~) doh zwesche.",
+ "semanticmaps-par-showtitle": "Donn en Övverschreff en däm Finster met Infomazjuhne övver de Makeerong aanzeije udder nit. De Övverschreff afzeschallde es öff joot, wam_mer en Schabloon nemmp för dä Enhallt vum Finster zerääsch ze possumenteere.",
+ "semanticmaps-par-hidenamespace": "Zeisch dä Name vum Appachtemang em marker info Finster udder nit.",
+ "semanticmaps-par-centre": "Der Meddelpunk vun dä Kaat. Wann keine aanjejovve_n_es jeiht dä automattesch op der optesche Meddelpunk vun all dä Makeerunge en dä Kaat.",
+ "semanticmaps-par-template": "En Schablohn för der Enhalt vum Finster met de Enfommazjuhne ze jeschtallte",
+ "semanticmaps-par-geocodecontrol": "Donn dat Bedeenelemänt aanzeije för de Ko'odinaate op de Ääd ze beärbeide",
+ "semanticmaps-kml-text": "Dä Täx, dä met jeder Sigg aanjezeisch weed. Wann extra Eijeschaffte afjefroocht wääde, kumme di schtatt däm Täx.",
+ "semanticmaps-kml-title": "Dä Schtandatttittel för wad eruß küt.",
+ "semanticmaps-kml-linkabsolute": "Sulle Lenks absoluud udder relatief sin?",
+ "semanticmaps-kml-pagelinktext": "Ene Täx, dä för Lenks op di Sigg jebuch weed. $1 schteiht dobei för dä Sigg ier Övverschreff.",
+ "semanticmaps-shapes-improperformat": "dat Fommaat för $1 paß nit. Loor em Handbooch noh de Fommaate.",
+ "semanticmaps-shapes-missingshape": "Mer han kein Forme för $1 jefonge. Loor en et Handbohch, doh schtonn de Forme dren."
+}
diff --git a/www/wiki/extensions/Maps/i18n/ku-latn.json b/www/wiki/extensions/Maps/i18n/ku-latn.json
new file mode 100644
index 00000000..bf1af05d
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ku-latn.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "George Animal"
+ ]
+ },
+ "maps_map": "Nexşe",
+ "mapeditor-link-title-switcher-link-text": "Girêdan",
+ "mapeditor-form-field-title": "Sernav",
+ "mapeditor-form-field-link": "Girêdan",
+ "mapeditor-form-field-group": "Kom",
+ "mapeditor-form-field-image": "Wêne"
+}
diff --git a/www/wiki/extensions/Maps/i18n/lad.json b/www/wiki/extensions/Maps/i18n/lad.json
new file mode 100644
index 00000000..8113e980
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/lad.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Universal Life"
+ ]
+ },
+ "maps_map": "Mapa",
+ "maps-loading-map": "Cargando la mapa...",
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "O"
+}
diff --git a/www/wiki/extensions/Maps/i18n/lb.json b/www/wiki/extensions/Maps/i18n/lb.json
new file mode 100644
index 00000000..387ab13a
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/lb.json
@@ -0,0 +1,123 @@
+{
+ "@metadata": {
+ "authors": [
+ "Robby",
+ "Soued031",
+ "Macofe"
+ ]
+ },
+ "maps-desc": "Gëtt d'Méiglechkeet fir d'Date vun de Koordinaten op Kaarten a Geocode Adressen an aner geographesch Operatiounen ze weisen",
+ "right-geocode": "Geocode",
+ "action-geocode": "Geocoding op dëser Wiki maachen",
+ "maps_map": "Kaart",
+ "maps-loading-map": "D'Kaart gëtt gelueden…",
+ "maps-load-failed": "D'Kaart konnt net geluede ginn!",
+ "maps-markers": "Markéierungen",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "Markéierunge fir Filteren",
+ "maps-others": "anerer",
+ "maps-layer-property": "Eegenschaft",
+ "maps-layer-value": "Wäert",
+ "maps-layer-errors": "Feeler",
+ "maps-layerdef-invalid": "Net valabel {{PLURAL:$1|Definitioun|Definitiounen}}",
+ "maps-layerdef-invalid-fatal": "Fatal net valabel Definitioun",
+ "maps-layer-of-type": "Layer vum Typ \"$1\"",
+ "maps-geodistance-description": "Déi geographesch Distanz tëscht zwéi Punkte berechnen, vun an an iergendeent vun den ënnerstëtzte Formater.",
+ "maps-mapsdoc-par-language": "D'Sprooch an där d'Dokumentatioun gewise gi soll. Wann déi Iwwersetzung net disponibel ass, da gëtt Englesch an där Plaz benotzt.",
+ "maps-coordinates-par-location": "D'Koordinaten déi Dir formatéiere wëllt.",
+ "maps-coordinates-par-format": "D'Zilformat vun de Koordinaten.",
+ "maps-distance-par-decimals": "Déi maximal Zuel vu Kommastellen déi am Resultat benotzt gi soll.",
+ "maps-distance-par-unit": "D'Eenheet fir d'Distanz.",
+ "maps-finddestination-par-location": "Déi initial Plaz.",
+ "maps-finddestination-par-distance": "D'Distanz vun der Rees.",
+ "maps-finddestination-par-format": "D'Format an deem d'Zil gewise soll ginn.",
+ "maps-geocode-par-location": "D'Adress déi Dir geocodéiere wëllt.",
+ "maps-geocode-par-format": "De Format fir d'Koordinaten.",
+ "maps-geodistance-par-location1": "Den éischte Punkt vun deem aus eng Distanz berechent gi soll.",
+ "maps-geodistance-par-unit": "D'Eenheet fir d'Distanz.",
+ "maps-geodistance-par-decimals": "Déi maximal Zuel vu Kommastellen déi am Resultat benotzt gi soll.",
+ "maps-displaymap-par-coordinates": "Déi Plaz op déi d'Kaart am Ufank zentréiert gëtt.",
+ "maps-displaymap-par-circles": "Kreesser fir ze weisen",
+ "maps-displaymap-par-lines": "Linne fir ze weisen",
+ "maps-displaymap-par-maxzoom": "De maximalen Niveau vum Zoom",
+ "maps-displaymap-par-minzoom": "De minimalen Niveau vum Zoom",
+ "maps-displaymap-par-polygons": "Polygonen fir ze weisen",
+ "maps-displaymap-par-rectangles": "Rechtecker fir ze weisen",
+ "maps-displaymap-par-static": "D'Kaart statesch maachen",
+ "validation-error-invalid-location": "Parameter $1 muss eng valabel Plaz sinn.",
+ "validation-error-invalid-locations": "Parameter $1 muss eng oder méi valabel Plaze sinn.",
+ "validation-error-invalid-width": "Parameter $1 muss eng valabel Breet sinn.",
+ "validation-error-invalid-height": "Parameter $1 muss eng valabel Héicht sinn.",
+ "validation-error-invalid-distance": "Parameter $1 muss eng valabel Distanz sinn.",
+ "validation-error-invalid-distances": "Parameter $1 muss eng oder méi valabel Distanze sinn.",
+ "validation-error-invalid-image": "Parameter $1 muss e valabelt Bild sinn.",
+ "validation-error-invalid-images": "Parameter $1 muss eent oder méi valabel Biller sinn.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "O",
+ "maps-abb-south": "S",
+ "maps-abb-west": "W",
+ "maps-latitude": "Breedegrad:",
+ "maps-longitude": "Längtegrad:",
+ "maps-invalid-coordinates": "De Wäert $1 gouf net als valabel Set vu Koordinaten erkannt.",
+ "maps_coordinates_missing": "Et goufe keng Koordinate fir d'Kaart uginn.",
+ "maps_geocoding_failed": "Dës {{PLURAL:$2|Adress konnt|Adresse konnten}} net geocodéiert ginn: $1",
+ "maps_geocoding_failed_for": "Dës {{PLURAL:$2|Adress|Adresse}} konnten net geocodéiert ginn an {{PLURAL:$2|huet|hu}} missen op der Kaart ewechgelooss ginn:\n$1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|Dëse Koordinate gouf|Dës Koordinate goufen}} net erkannt: $1",
+ "maps_unrecognized_coords_for": "Dës {{PLURAL:$2|Koordinate|Koordinate}} goufen net erkannt a vun der Kaart ignoréiert:\n$1",
+ "maps_map_cannot_be_displayed": "D'Kaart kann net gewise ginn.",
+ "maps-geocoder-not-available": "D'Funktioun vun der Geocodéierung vu Kaarten ass net disponibel. Äre Standuert konnt net geocodéiert ginn.",
+ "maps_leaflet": "Depliant",
+ "maps-leaflet-par-layers": "De Layer dee gewise gëtt wann d'Kaart geluede gëtt.",
+ "maps_click_to_activate": "Klickt fir d'kaart z'aktivéieren",
+ "maps_centred_on": "D'Kaart ass zentréiert op $1, $2",
+ "maps-par-enable-fullscreen": "'Fullscreen'-Knäppchen aschalten",
+ "maps-par-kml": "KML-Fichieren déi op d'Kaart musse geluede ginn.",
+ "maps-googlemaps3-incompatbrowser": "Äre Browser ass net mat Google Maps v3 kompatibel.",
+ "maps-googlemaps3-par-type": "Den Typ vu Kaart den am Ufank gewise gëtt.",
+ "maps-googlemaps3-par-autoinfowindows": "Automatesch all Fënstere mat Informatiounen opmaachen nodeem d'Säit geluede gouf.",
+ "maps-googlemaps3-par-poi": "Kuckeswäertes weisen",
+ "specialpages-group-maps": "Kaarten",
+ "mapeditor-none-text": "Keen",
+ "mapeditor-done-button": "Fäerdeg",
+ "mapeditor-remove-button": "Ewechhuelen",
+ "mapeditor-import-button2": "Importéieren",
+ "mapeditor-export-button": "A Wikicode exportéieren",
+ "mapeditor-mapparam-button": "Parametere vun der Kaart änneren",
+ "mapeditor-clear-button": "Kaart eidelmaachen",
+ "mapeditor-code-title": "Wikicode",
+ "mapeditor-import-title": "Wikicode importéieren",
+ "mapeditor-form-title": "Detailer änneren",
+ "mapeditor-link-title-switcher-popup-text": "Popup mat Text",
+ "mapeditor-link-title-switcher-link-text": "Link",
+ "mapeditor-form-field-title": "Titel",
+ "mapeditor-form-field-text": "Text",
+ "mapeditor-form-field-link": "Link",
+ "mapeditor-form-field-group": "Grupp",
+ "mapeditor-form-field-fillcolor": "Faarf fir opzefëllen",
+ "mapeditor-form-field-showonhover": "Nëmme weise wann een driwwer fiert",
+ "mapeditor-mapparam-title": "Parametere vun der Kaart änneren",
+ "mapeditor-mapparam-defoption": "-Parameter eraussichen-",
+ "mapeditor-form-field-image": "Bild",
+ "semanticmaps-unrecognizeddistance": "De Wäert $1 ass keng valabel Distanz.",
+ "semanticmaps-kml-link": "KML-Fichier weisen",
+ "semanticmaps-default-kml-pagelink": "Säit $1 weisen",
+ "semanticmaps-latitude": "Breedegrad: $1",
+ "semanticmaps-longitude": "Längtegrad: $1",
+ "semanticmaps-altitude": "Héicht: $1",
+ "semanticmaps-forminput-locations": "Plazen",
+ "semanticmaps-kml-title": "De Standard-Titel fir Resultater",
+ "semanticmaps-kml-linkabsolute": "Solle Linken absolut sinn (am Géigesaz zu relativ)",
+ "validator-type-mapscircle": "Geographesche Krees",
+ "validator-type-mapscircle-list": "Lëscht vun de Kreesser",
+ "validator-type-mapsimageoverlay": "Bildiwwerlagerung",
+ "validator-type-mapsimageoverlay-list": "Lëscht vun de Bildiwwerlagerungen",
+ "validator-type-mapsline": "Geographesch Linn",
+ "validator-type-mapsline-list": "Lëscht vu Linnen",
+ "validator-type-mapslocation": "Geographesch Plaz",
+ "validator-type-mapslocation-list": "Lëscht vu Plazen",
+ "validator-type-mapsrectangle": "Geographesche Rechteck",
+ "validator-type-mapsrectangle-list": "Lëscht vun de Rechtecken",
+ "validator-type-mapspolygon": "Geographesche Polygon",
+ "validator-type-mapspolygon-list": "Lëscht vun de geographesche Polygonen",
+ "validator-type-jsonfile": "Text"
+}
diff --git a/www/wiki/extensions/Maps/i18n/lt.json b/www/wiki/extensions/Maps/i18n/lt.json
new file mode 100644
index 00000000..a54d50ee
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/lt.json
@@ -0,0 +1,119 @@
+{
+ "@metadata": {
+ "authors": [
+ "Eitvys200",
+ "Hugo.arg",
+ "Aswanas",
+ "Zygimantus",
+ "Agne1992"
+ ]
+ },
+ "maps-desc": "Suteikia galimybę atvaizduoti koordinačių duomenis žemėlapiuose ir geografinio kodavimo adresus ([http://mapping.referata.com/wiki/Maps_examples demo]).\nKatrografavimo paslaugos pasiekiamos: $1",
+ "action-geocode": " šioje wiki atlikite geokodavimą",
+ "maps_map": "Žemėlapis",
+ "maps-loading-map": "Kraunamas žemėlapis ...",
+ "maps-load-failed": "Nepavyko įkelti žemėlapio!",
+ "maps-markers": "Žymekliai",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-others": "kita",
+ "maps-ns-layer": "Sluoksnis",
+ "maps-ns-layer-talk": "Sluoksnio aptarimas",
+ "maps-layer-property": "Savybė",
+ "maps-layer-value": "Vertė",
+ "maps-layer-errors": "Klaidos",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Negalimas apibrėžimas|Negalimi apibrėžimai}}",
+ "maps-layerdef-invalid-fatal": "Galutinis negalimas apibrėžimas",
+ "maps-layerpage-usage": "Puslapiai su žemėlapiais, naudojantys sluoksnį „$1“",
+ "maps-layerpage-nousage": "Šiuo metu nėra puslapių, naudojančiu šį sluoksnį",
+ "maps-error-invalid-layertype": "Nėra „$1“ tipo sluoksnių. Tik {{PLURAL:$3|šis tipas|šie tipai}} yra palaikomi: $2",
+ "maps-error-no-layertype": "Turite nurodyti sluoksnio tipą. {{PLURAL:$2|Tik šis tipas|Tik šie tipai}} yra palaikomi: $1",
+ "validation-error-invalid-layer": "Parametras „$1“ turi būti galimas sluoksnis.",
+ "validation-error-invalid-layers": "Parametras „$1“ turi būti vienas ar daugiau galimų sluoksnių.",
+ "maps-layer-of-type": "„$1“ tipo sluoksnis",
+ "maps-layer-of-type-and-name": "„$1“ tipo sluoksnis „$2“",
+ "maps-coordinates-par-location": "Koordinatės, kurias norite formatuoti.",
+ "maps-finddestination-par-location": "Pradinė vieta.",
+ "maps-finddestination-par-distance": "Kelionės atstumas.",
+ "maps-displaymap-par-circles": "Rodomi apskritimai",
+ "maps-displaymap-par-lines": "Rodomos linijos",
+ "maps-displaymap-par-maxzoom": "Didžiausias priartinimo lygis",
+ "maps-displaymap-par-minzoom": "Mažiausias priartinimo lygis",
+ "maps-displaymap-par-polygons": "Rodomi daugiakampiai",
+ "maps-displaymap-par-rectangles": "Rodomi stačiakampiai",
+ "maps-displaymap-par-static": "Padaryti žemėlapį statinį",
+ "maps-displaymap-par-wmsoverlay": "Naudoti WMS perdangą",
+ "maps-fullscreen-button": "Perjungti į visą ekraną",
+ "maps-fullscreen-button-tooltip": "Peržiūrėti žemėlapį visame ekrane arba kaip integruotą",
+ "validation-error-invalid-location": "Parametras „$1“ turi būti galima vieta.",
+ "validation-error-invalid-locations": "Parametras „$1“ turi būti viena ar kelios galimos vietos.",
+ "validation-error-invalid-width": "Parametras „$1“ turi būti galimas plotis.",
+ "validation-error-invalid-height": "Parametras „$1“ turi būti galimas aukštis.",
+ "validation-error-invalid-distance": "Parametras „$1“ turi būti galimas atstumas.",
+ "validation-error-invalid-distances": "Parametras „$1“ turi būti vienas ar keli galimi atstumai.",
+ "validation-error-invalid-image": "Parametras „$1“ turi būti galimas paveikslėlis.",
+ "validation-error-invalid-images": "Parametras „$1“ turi būti vienas ar keli galimi paveikslėliai.",
+ "maps-abb-north": "Š",
+ "maps-abb-east": "R",
+ "maps-abb-south": "P",
+ "maps-abb-west": "V",
+ "maps-latitude": "Platuma:",
+ "maps-longitude": "Ilguma:",
+ "maps-invalid-coordinates": "Vertė $1 nepripažįstama kaip galiojančios koordinatės.",
+ "maps_coordinates_missing": "Nesudarytos koordinatės žemėlapiui.",
+ "maps_geocoding_failed": "{{PLURAL:$2|Šis adresas|Šie adresai}} negali būti kartografuoti: $1.",
+ "maps_unrecognized_coords": "{{PLURAL:$2|Ši koordinatė|Šios koordinatės}} nebuvo atpažintos: $1.",
+ "maps_map_cannot_be_displayed": "Žemėlapis negal būti parodytas.",
+ "maps_leaflet": "Lankstinukas",
+ "maps-leaflet-par-defzoom": "Leidžia nustatyti numatytąjį žemėlapio priartinimo lygį.",
+ "maps-leaflet-par-layers": "Sluoksniai, kuriuos bus galima rasti sluoksnių pasirinkimuose. Pirmasis sluoksnis bus rodomas įkėlus žemėlapį.",
+ "maps_click_to_activate": "Spustelėkite, norėdami įjungti žemėlapį",
+ "maps_centred_on": "Žemėlapis centruotas link $1, $2.",
+ "maps-par-enable-fullscreen": "Įgalinti viso ekrano mygtuką",
+ "maps-par-markercluster": "Leidžia sujungti kelius netoliese esančius žymeklius į vieną žymeklį",
+ "maps-googlemaps3-incompatbrowser": "Jūsų naršyklė nėra suderinama su Google Maps v3.",
+ "maps-googlemaps3-par-type": "Pradinis žemėlapio tipas.",
+ "mapeditor": "Žemėlapio redaktorius",
+ "specialpages-group-maps": "Žemėlapiai",
+ "mapeditor-none-text": "Nėra",
+ "mapeditor-done-button": "Atlikta",
+ "mapeditor-remove-button": "Pašalinti",
+ "mapeditor-import-button2": "Importuoti",
+ "mapeditor-export-button": "Eksportuoti į viki kodą",
+ "mapeditor-import-button": "Importuoti iš viki kodo",
+ "mapeditor-select-button": "Pasirinkti šį daugiakampį",
+ "mapeditor-mapparam-button": "Redaguoti žemėlapio parametrus",
+ "mapeditor-clear-button": "Valyti žemėlapį",
+ "mapeditor-code-title": "Viki kodas",
+ "mapeditor-import-title": "Importuoti vikį kodą",
+ "mapeditor-form-title": "Redaguoti informaciją",
+ "mapeditor-link-title-switcher-popup-text": "Iššokantis langas su tekstu",
+ "mapeditor-link-title-switcher-link-text": "Nuoroda",
+ "mapeditor-form-field-title": "Pavadinimas",
+ "mapeditor-form-field-text": "Tekstas",
+ "mapeditor-form-field-link": "Nuoroda",
+ "mapeditor-form-field-icon": "Ikona",
+ "mapeditor-form-field-group": "Grupė",
+ "mapeditor-form-field-strokecolor": "Potėpio spalva",
+ "mapeditor-form-field-strokeopacity": "Potėpio matomumas",
+ "mapeditor-form-field-strokeweight": "Potėpio svoris",
+ "mapeditor-form-field-fillcolor": "Užpildo spalva",
+ "mapeditor-form-field-fillopcaity": "Užpildo matomumas",
+ "mapeditor-form-field-showonhover": "Rodyti tik užvedus pelytę",
+ "mapeditor-mapparam-title": "Redaguoti žemėlapio parametrus",
+ "mapeditor-mapparam-defoption": "-Pasirinkti parametrą-",
+ "mapeditor-form-field-image": "Paveikslėlis",
+ "semanticmaps-kml-link": "Žiūrėti KML failą",
+ "semanticmaps-default-kml-pagelink": "Žiūrėti puslapį $1",
+ "semanticmaps-latitude": "Platuma: $1",
+ "semanticmaps-longitude": "Ilguma: $1",
+ "semanticmaps-altitude": "Aukštis: $1",
+ "semanticmaps-forminput-locations": "Vietovės",
+ "validator-type-mapscircle": "Geografinis apskritimas",
+ "validator-type-mapscircle-list": "Apskritimų sąrašas",
+ "validator-type-mapsline": "Geografinė linija",
+ "validator-type-mapsline-list": "Linijų sąrašas",
+ "validator-type-mapslocation": "Geografinė vieta",
+ "validator-type-mapslocation-list": "Vietų sąrašas",
+ "validator-type-mapsrectangle": "Geografinis stačiakampis",
+ "validator-type-mapsrectangle-list": "Stačiakampių sąrašas"
+}
diff --git a/www/wiki/extensions/Maps/i18n/lv.json b/www/wiki/extensions/Maps/i18n/lv.json
new file mode 100644
index 00000000..e6513e59
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/lv.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "GreenZeb"
+ ]
+ },
+ "maps-abb-north": "Z",
+ "maps-abb-east": "A",
+ "maps-abb-south": "D",
+ "maps-abb-west": "R",
+ "maps-latitude": "Platums:",
+ "maps-longitude": "Garums:"
+}
diff --git a/www/wiki/extensions/Maps/i18n/magic/MagicWords.php b/www/wiki/extensions/Maps/i18n/magic/MagicWords.php
new file mode 100644
index 00000000..b7065cee
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/magic/MagicWords.php
@@ -0,0 +1,387 @@
+<?php
+
+/**
+ * Internationalisation file for Maps extension.
+ */
+
+$magicWords = [];
+
+/** English (English) */
+$magicWords['en'] = [
+ 'display_map' => [ 0, 'display_map' ],
+ 'display_point' => [ 0, 'display_point' ],
+ 'display_points' => [ 0, 'display_points' ],
+ 'display_line' => [ 0, 'display_line' ],
+ 'geocode' => [ 0, 'geocode' ],
+ 'geodistance' => [ 0, 'geodistance' ],
+ 'finddestination' => [ 0, 'finddestination' ],
+ 'coordinates' => [ 0, 'coordinates' ],
+ 'distance' => [ 0, 'distance' ],
+ 'mapsdoc' => [ 0, 'mapsdoc' ],
+];
+
+/** Afrikaans (Afrikaans) */
+$magicWords['af'] = [
+ 'geocode' => [ 0, 'geokode', 'geocode' ],
+ 'coordinates' => [ 0, 'koördinate', 'coordinates' ],
+];
+
+/** Arabic (العربية) */
+$magicWords['ar'] = [
+ 'display_map' => [ 0, 'عرض_الخريطة' ],
+ 'display_point' => [ 0, 'نقطة_العرض' ],
+ 'display_points' => [ 0, 'نقاط_العرض' ],
+ 'display_line' => [ 0, 'خط_العرض' ],
+ 'geocode' => [ 0, 'كود_جغرافي' ],
+ 'geodistance' => [ 0, 'مسافة_جغرافية' ],
+ 'finddestination' => [ 0, 'إيجاد_الوجهة' ],
+ 'coordinates' => [ 0, 'إحداثيات' ],
+ 'distance' => [ 0, 'مسافة' ],
+ 'mapsdoc' => [ 0, 'توثيق_الخرائط' ],
+];
+
+/** Egyptian Arabic (مصرى) */
+$magicWords['arz'] = [
+ 'display_map' => [ 0, 'عرض_الخريطه', 'عرض_الخريطة' ],
+ 'display_point' => [ 0, 'نقطه_العرض' ],
+ 'display_points' => [ 0, 'نقط_العرض' ],
+ 'display_line' => [ 0, 'خط_العرض' ],
+ 'geocode' => [ 0, 'كود_جغرافي' ],
+ 'geodistance' => [ 0, 'مسافه_جغرافيه', 'مسافة_جغرافية' ],
+ 'finddestination' => [ 0, 'إيجاد_الوجهه', 'إيجاد_الوجهة' ],
+ 'coordinates' => [ 0, 'إحداثيات' ],
+ 'distance' => [ 0, 'مسافه' ],
+ 'mapsdoc' => [ 0, 'توثيق_الخرايط' ],
+];
+
+/** Assamese (অসমীয়া) */
+$magicWords['as'] = [
+ 'display_point' => [ 0, 'মানচিত্ৰ_প্ৰদৰ্শন' ],
+];
+
+/** Breton (brezhoneg) */
+$magicWords['br'] = [
+ 'coordinates' => [ 0, 'daveennoù' ],
+ 'distance' => [ 0, 'hed' ],
+];
+
+/** Chechen (нохчийн) */
+$magicWords['ce'] = [
+ 'geocode' => [ 0, 'геагишар', 'геокод' ],
+];
+
+/** German (Deutsch) */
+$magicWords['de'] = [
+ 'display_map' => [ 0, 'zeige_karte' ],
+ 'display_point' => [ 0, 'zeige_punkt' ],
+ 'display_points' => [ 0, 'zeige_punkte' ],
+ 'display_line' => [ 0, 'zeige_linie' ],
+ 'geocode' => [ 0, 'geokodiere' ],
+ 'geodistance' => [ 0, 'geoentfernung' ],
+ 'finddestination' => [ 0, 'finde_ziel' ],
+ 'coordinates' => [ 0, 'koordinaten' ],
+ 'distance' => [ 0, 'entfernung' ],
+ 'mapsdoc' => [ 0, 'mapsdok' ],
+];
+
+/** Zazaki (Zazaki) */
+$magicWords['diq'] = [
+ 'display_map' => [ 0, 'aseniya_ğerita' ],
+ 'display_point' => [ 0, 'aseniya_dewti' ],
+ 'display_points' => [ 0, 'aseniya_dawıtan' ],
+ 'display_line' => [ 0, 'aseniya_ğeter' ],
+ 'geocode' => [ 0, 'herunkodi' ],
+ 'geodistance' => [ 0, 'heruna_mesafi' ],
+ 'finddestination' => [ 0, 'menzilvinayış' ],
+ 'coordinates' => [ 0, 'koordinati' ],
+ 'distance' => [ 0, 'mesafe' ],
+ 'mapsdoc' => [ 0, 'dokumanéğerita' ],
+];
+
+/** Esperanto (Esperanto) */
+$magicWords['eo'] = [
+ 'coordinates' => [ 0, 'koordinatoj' ],
+ 'distance' => [ 0, 'distanco' ],
+];
+
+/** Spanish (español) */
+$magicWords['es'] = [
+ 'display_point' => [ 0, 'mostrar_punto' ],
+ 'geocode' => [ 0, 'geocodificar' ],
+ 'coordinates' => [ 0, 'coordenadas' ],
+ 'distance' => [ 0, 'distancia' ],
+];
+
+/** French (français) */
+$magicWords['fr'] = [
+ 'display_map' => [ 0, 'afficher_carte' ],
+ 'display_point' => [ 0, 'afficher_point' ],
+ 'display_points' => [ 0, 'afficher_points' ],
+ 'display_line' => [ 0, 'afficher_ligne' ],
+ 'geocode' => [ 0, 'codegéo' ],
+ 'geodistance' => [ 0, 'distancegéo' ],
+ 'finddestination' => [ 0, 'trouverdestination' ],
+ 'coordinates' => [ 0, 'coordonnées' ],
+ 'distance' => [ 0, 'distance' ],
+ 'mapsdoc' => [ 0, 'doccartes' ],
+];
+
+/** Hebrew (עברית) */
+$magicWords['he'] = [
+ 'distance' => [ 0, 'מרחק' ],
+];
+
+/** Indonesian (Bahasa Indonesia) */
+$magicWords['id'] = [
+ 'finddestination' => [ 0, 'petunjukarah' ],
+ 'coordinates' => [ 0, 'koordinat' ],
+ 'distance' => [ 0, 'jarak' ],
+];
+
+/** Igbo (Igbo) */
+$magicWords['ig'] = [
+ 'display_map' => [ 0, 'zí_otúzọr', 'display_map' ],
+];
+
+/** Italian (italiano) */
+$magicWords['it'] = [
+ 'display_map' => [ 0, 'mostra_mappa' ],
+ 'display_point' => [ 0, 'mostra_punto' ],
+ 'display_points' => [ 0, 'mostra_punti' ],
+ 'geocode' => [ 0, 'geocodice' ],
+ 'geodistance' => [ 0, 'geodistanza' ],
+ 'finddestination' => [ 0, 'trovadestinazione' ],
+ 'coordinates' => [ 0, 'coordinate' ],
+ 'distance' => [ 0, 'distanza' ],
+ 'mapsdoc' => [ 0, 'docmappe' ],
+];
+
+/** Japanese (日本語) */
+$magicWords['ja'] = [
+ 'display_map' => [ 0, '地図表示' ],
+ 'display_point' => [ 0, '地図点' ],
+ 'geocode' => [ 0, '地理的コード' ],
+ 'geodistance' => [ 0, '地理的距離' ],
+ 'finddestination' => [ 0, '目的地検索' ],
+ 'coordinates' => [ 0, '座標' ],
+ 'distance' => [ 0, '距離' ],
+];
+
+/** Georgian (ქართული) */
+$magicWords['ka'] = [
+ 'coordinates' => [ 0, 'კოორდინატები' ],
+ 'distance' => [ 0, 'მანძილი' ],
+];
+
+/** Korean (한국어) */
+$magicWords['ko'] = [
+ 'display_map' => [ 0, '지도_표시' ],
+ 'display_point' => [ 0, '포인트_표시' ],
+ 'display_points' => [ 0, '포인트목록_표시' ],
+ 'display_line' => [ 0, '선_표시' ],
+ 'geocode' => [ 0, '지리코드' ],
+ 'geodistance' => [ 0, '지리거리' ],
+ 'finddestination' => [ 0, '목적지찾기' ],
+ 'coordinates' => [ 0, '좌표' ],
+ 'distance' => [ 0, '거리' ],
+ 'mapsdoc' => [ 0, '지도문서' ],
+];
+
+/** Kurdish (Latin script) (Kurdî (latînî)‎) */
+$magicWords['ku-latn'] = [
+ 'coordinates' => [ 0, 'koordînat' ],
+];
+
+/** Ladino (Ladino) */
+$magicWords['lad'] = [
+ 'distance' => [ 0, 'distancia', 'distance' ],
+];
+
+/** Luxembourgish (Lëtzebuergesch) */
+$magicWords['lb'] = [
+ 'coordinates' => [ 0, 'Koordinaten' ],
+ 'distance' => [ 0, 'Distanz' ],
+];
+
+/** Minangkabau (Baso Minangkabau) */
+$magicWords['min'] = [
+ 'finddestination' => [ 0, 'petunjukarah' ],
+ 'coordinates' => [ 0, 'koordinat' ],
+ 'distance' => [ 0, 'jarak' ],
+];
+
+/** Macedonian (македонски) */
+$magicWords['mk'] = [
+ 'display_map' => [ 0, 'прикажи_карта' ],
+ 'display_point' => [ 0, 'прикажи_точка' ],
+ 'display_points' => [ 0, 'прикажи_точки' ],
+ 'display_line' => [ 0, 'прикажи_линија' ],
+ 'geocode' => [ 0, 'геокод' ],
+ 'geodistance' => [ 0, 'георастојание' ],
+ 'finddestination' => [ 0, 'најдиодредница' ],
+ 'coordinates' => [ 0, 'координати' ],
+ 'distance' => [ 0, 'растојание' ],
+ 'mapsdoc' => [ 0, 'докумкарти' ],
+];
+
+/** Malayalam (മലയാളം) */
+$magicWords['ml'] = [
+ 'display_map' => [ 0, 'ഭൂപടം_പ്രദർശിപ്പിക്കുക' ],
+ 'display_point' => [ 0, 'ബിന്ദു_പ്രദർശിപ്പിക്കുക' ],
+ 'display_points' => [ 0, 'ബിന്ദുക്കൾ_പ്രദർശിപ്പിക്കുക' ],
+ 'display_line' => [ 0, 'രേഖ_പ്രദർശിപ്പിക്കുക' ],
+ 'geocode' => [ 0, 'ജിയോകോഡ്' ],
+ 'geodistance' => [ 0, 'ഭൗമദൂരം' ],
+ 'finddestination' => [ 0, 'ലക്ഷ്യംതിരയുക' ],
+ 'coordinates' => [ 0, 'നിർദ്ദേശാങ്കങ്ങൾ' ],
+ 'distance' => [ 0, 'ദൂരം' ],
+];
+
+/** Marathi (मराठी) */
+$magicWords['mr'] = [
+ 'distance' => [ 0, 'अंतर' ],
+];
+
+/** Low Saxon (Netherlands) (Nedersaksies) */
+$magicWords['nds-nl'] = [
+ 'display_map' => [ 0, 'kaorte_laoten_zien' ],
+ 'geocode' => [ 0, 'geokoderen' ],
+ 'geodistance' => [ 0, 'geo-aofstaand' ],
+ 'finddestination' => [ 0, 'bestemmingzeuken' ],
+ 'coordinates' => [ 0, 'koordinaoten' ],
+ 'distance' => [ 0, 'aofstaand' ],
+];
+
+/** Dutch (Nederlands) */
+$magicWords['nl'] = [
+ 'display_map' => [ 0, 'kaart_weergeven' ],
+ 'display_point' => [ 0, 'punt_weergeven' ],
+ 'display_points' => [ 0, 'punten_weergeven' ],
+ 'display_line' => [ 0, 'lijn_weergeven' ],
+ 'geocode' => [ 0, 'geocoderen' ],
+ 'geodistance' => [ 0, 'geoafstand' ],
+ 'finddestination' => [ 0, 'bestemmingzoeken' ],
+ 'coordinates' => [ 0, 'coordinaten' ],
+ 'distance' => [ 0, 'afstand' ],
+ 'mapsdoc' => [ 0, 'kaartdoc' ],
+];
+
+/** Polish (polski) */
+$magicWords['pl'] = [
+ 'display_map' => [ 0, 'wyświetl_mapę' ],
+ 'coordinates' => [ 0, 'współrzędne' ],
+ 'distance' => [ 0, 'odległość' ],
+];
+
+/** Pashto (پښتو) */
+$magicWords['ps'] = [
+ 'distance' => [ 0, 'واټن', 'distance' ],
+];
+
+/** Portuguese (português) */
+$magicWords['pt'] = [
+ 'display_map' => [ 0, 'mostrar_mapa' ],
+ 'display_point' => [ 0, 'mostrar_ponto' ],
+ 'display_points' => [ 0, 'mostrar_pontos' ],
+ 'geocode' => [ 0, 'geocódigo' ],
+ 'geodistance' => [ 0, 'geodistância' ],
+ 'finddestination' => [ 0, 'encontrardestino' ],
+ 'coordinates' => [ 0, 'coordenadas' ],
+ 'distance' => [ 0, 'distância' ],
+];
+
+/** Russian (русский) */
+$magicWords['ru'] = [
+ 'display_map' => [ 0, 'показать_карту' ],
+ 'display_point' => [ 0, 'показать_точку' ],
+ 'display_points' => [ 0, 'показать_точки' ],
+ 'display_line' => [ 0, 'показать_линию' ],
+ 'geocode' => [ 0, 'геокод' ],
+ 'geodistance' => [ 0, 'георасстояние' ],
+ 'finddestination' => [ 0, 'найти_местоположение' ],
+ 'coordinates' => [ 0, 'координаты' ],
+ 'distance' => [ 0, 'расстояние' ],
+];
+
+/** Sinhala (සිංහල) */
+$magicWords['si'] = [
+ 'finddestination' => [ 0, 'ගමනාන්තය_සෙවීම', 'finddestination' ],
+ 'coordinates' => [ 0, 'ඛණ්ඩාංක', 'coordinates' ],
+ 'distance' => [ 0, 'දුර', 'distance' ],
+];
+
+/** Serbian (Cyrillic script) (српски (ћирилица)‎) */
+$magicWords['sr-ec'] = [
+ 'display_map' => [ 0, 'прикажи_мапу' ],
+ 'display_point' => [ 0, 'прикажи_тачку', 'прикажи_тачке' ],
+ 'finddestination' => [ 0, 'пронађиодредиште', 'пронађи_одредиште' ],
+ 'coordinates' => [ 0, 'координате' ],
+ 'distance' => [ 0, 'раздаљина' ],
+];
+
+/** Serbian (Latin script) (srpski (latinica)‎) */
+$magicWords['sr-el'] = [
+ 'display_map' => [ 0, 'prikaži_mapu' ],
+ 'display_point' => [ 0, 'prikaži_tačku', 'prikaži_tačke' ],
+ 'geocode' => [ 0, 'geografski_kod' ],
+ 'geodistance' => [ 0, 'geografska_razdaljina' ],
+ 'finddestination' => [ 0, 'pronađi_odredište' ],
+ 'coordinates' => [ 0, 'koordinate' ],
+ 'distance' => [ 0, 'razdaljina' ],
+];
+
+/** Talysh (толышә зывон) */
+$magicWords['tly'] = [
+ 'display_map' => [ 0, 'хәритә_нишо_дој' ],
+];
+
+/** Ukrainian (українська) */
+$magicWords['uk'] = [
+ 'display_map' => [ 0, 'показати_карту' ],
+ 'display_point' => [ 0, 'показати_точку' ],
+ 'display_points' => [ 0, 'показати_точки' ],
+ 'geodistance' => [ 0, 'геовідстань' ],
+ 'finddestination' => [ 0, 'знайти_місцезнаходження' ],
+ 'coordinates' => [ 0, 'координати' ],
+ 'distance' => [ 0, 'відстань' ],
+];
+
+/** Uzbek (oʻzbekcha/ўзбекча) */
+$magicWords['uz'] = [
+ 'coordinates' => [ 0, 'koordinatalar' ],
+];
+
+/** Vietnamese (Tiếng Việt) */
+$magicWords['vi'] = [
+ 'display_map' => [ 0, 'hiển_thị_bản_đồ', 'hiểnthịbảnđồ' ],
+ 'geocode' => [ 0, 'mã_hóa_địa_lý', 'mãhóađịalý', 'mã_hoá_địa_lý', 'mãhoáđịalý', 'mã_hóa_địa_lí', 'mãhóađịalí', 'mã_hoá_địa_lí', 'mãhoáđịalí' ],
+ 'coordinates' => [ 0, 'tọa_độ', 'tọađộ' ],
+];
+
+/** Simplified Chinese (中文(简体)‎) */
+$magicWords['zh-hans'] = [
+ 'display_map' => [ 0, '显示地图' ],
+ 'display_point' => [ 0, '显示点' ],
+ 'display_points' => [ 0, '显示多个点' ],
+ 'display_line' => [ 0, '显示线' ],
+ 'geocode' => [ 0, '地理编码' ],
+ 'geodistance' => [ 0, '地理距离' ],
+ 'finddestination' => [ 0, '寻找目的地' ],
+ 'coordinates' => [ 0, '坐标' ],
+ 'distance' => [ 0, '距离' ],
+ 'mapsdoc' => [ 0, '地图文档' ],
+];
+
+/** Traditional Chinese (中文(繁體)‎) */
+$magicWords['zh-hant'] = [
+ 'display_map' => [ 0, '顯示地圖' ],
+ 'display_point' => [ 0, '顯示位置' ],
+ 'display_points' => [ 0, '顯示多個位置' ],
+ 'display_line' => [ 0, '顯示線' ],
+ 'geocode' => [ 0, '地理編碼' ],
+ 'geodistance' => [ 0, '地理距離' ],
+ 'finddestination' => [ 0, '搜尋目的地' ],
+ 'coordinates' => [ 0, '坐標' ],
+ 'distance' => [ 0, '距離' ],
+ 'mapsdoc' => [ 0, '地圖檔案' ],
+]; \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/i18n/map-bms.json b/www/wiki/extensions/Maps/i18n/map-bms.json
new file mode 100644
index 00000000..fe74234d
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/map-bms.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "StefanusRA"
+ ]
+ },
+ "maps-abb-north": "L",
+ "maps-abb-east": "W",
+ "maps-abb-south": "Kdl",
+ "maps-abb-west": "Kln",
+ "maps-latitude": "Latituda:",
+ "maps-longitude": "Longituda:"
+}
diff --git a/www/wiki/extensions/Maps/i18n/mg.json b/www/wiki/extensions/Maps/i18n/mg.json
new file mode 100644
index 00000000..48eda710
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/mg.json
@@ -0,0 +1,24 @@
+{
+ "@metadata": {
+ "authors": [
+ "Jagwar"
+ ]
+ },
+ "maps-layerdef-invalid-fatal": "Famaritana ny tsy azo ampiasaina mahajanona",
+ "maps-layerdef-wrong-namespace": "Azo ekena ao amin'ny eran'anarana \"$1\" ihany ny famaritan-tsosona",
+ "maps-layerdef-equal-layer-name": "Tsy maintsy tokana ao amin'ny pejy sosona ny anaran-tsosona. Efa ampiasaina anaty sosona hafa \"$1\"",
+ "maps-layerpage-usage": "Pejy misy sarintany mampiasa ny sosona \"$1\"",
+ "maps-layerpage-nousage": "Tsy misy pejy mampiasa ity sosona ity amin'izao fotoana izao.",
+ "validation-error-invalid-layer": "Tsy maintsy sosona azo ampiasaina ny parametatra \"$1\"",
+ "validation-error-invalid-layers": "Tsy maintsy parametatra azo ekena iray na mihoatra ny parametatra \"$1\".",
+ "validation-error-no-non-numerics": "Tsy maintsy tohin-dohavy iray na maro ny parametatra \"$1\".",
+ "maps-layer-of-type": "Karazan-tsosona \"$1\"",
+ "maps-layerdefinition-description": "Mamaritra sosona manokana izay mety aseho amin'ny alalan'ny lefan-tsaritany hafa.",
+ "validation-error-invalid-location": "Tsy maintsy toerana azo ekena ny parametatra \"$1\".",
+ "validation-error-invalid-width": "Tsy maintsy habe azo ekena ny parametatra \"$1\".",
+ "validation-error-invalid-height": "Tsy maintsy haavo azo ekena ny parametatra \"$1\".",
+ "validation-error-invalid-distances": "Tsy maintsy halavirana azo ekena ny parametatra \"$1\".",
+ "validation-error-invalid-image": "Tsy maintsy sary azo ekena ny parametatra \"$1\".",
+ "validation-error-invalid-goverlay": "Tsy maintsy azo ampiasaina ny mpanosona \"$1\".",
+ "validation-error-invalid-goverlays": "Tsy maintsy mpanosona azo ekena iray na mihoatra ny parametatra \"$1\"."
+}
diff --git a/www/wiki/extensions/Maps/i18n/mk.json b/www/wiki/extensions/Maps/i18n/mk.json
new file mode 100644
index 00000000..78a20ef8
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/mk.json
@@ -0,0 +1,226 @@
+{
+ "@metadata": {
+ "authors": [
+ "Bjankuloski06",
+ "McDutchie",
+ "Macofe",
+ "Vlad5250"
+ ]
+ },
+ "maps-desc": "Дава можност за вметнување на динамички карти во викистраници, геокодирање на адреси и други географски постапки",
+ "right-geocode": "Геокод",
+ "action-geocode": "вршење на геокодирање на ова вики",
+ "maps_map": "Карта",
+ "maps-tracking-category": "Страници со карта исцртана со додатокот „Карти“",
+ "maps-loading-map": "Ја вчитувам картата...",
+ "maps-load-failed": "Не можев да ја вчитам картата!",
+ "maps-markers": "Обележувачи",
+ "maps-copycoords-prompt": "Стиснете CTRL+C, па ENTER",
+ "maps-searchmarkers-text": "Филтрирање на одбележувачи",
+ "maps-others": "други",
+ "maps-kml-parsing-failed": "Не можев да расчленам една или повеќе KML-податотеки. Ова обично се должи на нивна недостапност или лошо обликуван XML.",
+ "maps-ns-layer": "Слој",
+ "maps-ns-layer-talk": "Разговор за слој",
+ "maps-layer-property": "Својство",
+ "maps-layer-value": "Вредност",
+ "maps-layer-errors": "Грешки",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Неважечка одредба|Неважечки одредби}}",
+ "maps-layerdef-invalid-fatal": "Кобна неважечка одредба",
+ "maps-layerdef-wrong-namespace": "Одредбите на слоевите се важечки само на страници од именскиот простор „$1“",
+ "maps-layerdef-equal-layer-name": "Имињата на слоевите не може да се повторуваат на една иста страница со слоеви. Веќе има слој со име „$1“.",
+ "maps-layerpage-usage": "Страници со карти што го користат слојот „$1“",
+ "maps-layerpage-nousage": "Засега нема страници што го користат овој слој.",
+ "maps-error-invalid-layertype": "Нема слоеви од типот „$1“. {{PLURAL:$3|Поддржан е само овој тип|Поддржани се само следниве типови}}: $2",
+ "maps-error-no-layertype": "Ќе треба да наведете тип на слој. {{PLURAL:$2|Поддржан е само овој тип|Поддржани се само следниве типови}}: $1",
+ "validation-error-invalid-layer": "Параметарот $1 мора да биде важечки слој.",
+ "validation-error-invalid-layers": "Параметарот $1 мора да биде еден или повеќе важечки слоеви.",
+ "validation-error-no-non-numeric": "Параметарот „$1“ мора да биде небројчена низа.",
+ "validation-error-no-non-numerics": "Параметарот „$1“ мора да биде една или повеќе небројчени низи.",
+ "maps-layer-of-type": "Слој од типот $1",
+ "maps-layer-of-type-and-name": "Слој „$2“ од типот „$1“",
+ "maps-layer-type-supported-by": "Овој тип на слој може да се користи {{PLURAL:$2|само со картографската служба $1|само со следниве картографски служби: $1}}.",
+ "maps-coordinates-description": "Расчленувачкиот пресретник за форматирање на координати. Ги претвора сите поддржани формати.",
+ "maps-displaymap-description": "Приказ на географски карти без никакви обележувачи на нив определени од викито.",
+ "maps-distance-description": "Претворање на растојание од една во друга поддржана единица.",
+ "maps-finddestination-description": "Пронаоѓање на одредница со зададена почетна точка (која може да биде во било кој поддржан формат), првична насока и растојание",
+ "maps-geocode-description": "Овозможува геокодирање на адреси, што значи дека претвора места во координати. Има поддршка за неколку служби за геокодирање (не е исто што и картографски служби).",
+ "maps-geodistance-description": "Пресметување на географското растојание помеѓу две точки (сите поддржани формати).",
+ "maps-mapsdoc-description": "Приказ на табела со параметри за назначена карторафска служба заедно со нивните основни вредности и описи.",
+ "maps-layerdefinition-description": "Опишува прилагоден слој што се прикажува со други функции за карта.",
+ "maps-mapsdoc-par-service": "Картографската служба за која се прикажува параметарска документација.",
+ "maps-mapsdoc-par-language": "На кој јазик да се прикаже документацијата. Ако го нема тој превод, ќе се прикаже англиската верзија.",
+ "maps-coordinates-par-location": "Координатите што сакате да ги форматирате.",
+ "maps-coordinates-par-format": "Целниот формат за координатите.",
+ "maps-coordinates-par-directional": "Покажува дали координатите треба да се испишат со или без насока.",
+ "maps-par-scrollwheelzoom": "Покажува дали е вклучено тркалцето на глушецот.",
+ "maps-distance-par-distance": "Растојанието во кое претворате е истоветно со единицата што се укажува.",
+ "maps-distance-par-decimals": "Највеќе децимални места што ќе се користат во изводната вредност.",
+ "maps-distance-par-unit": "Единица на испишаното растојание во изводот.",
+ "maps-finddestination-par-location": "Првичната местоположба.",
+ "maps-finddestination-par-bearing": "Првичната насока.",
+ "maps-finddestination-par-distance": "Растојанието што треба да се помине.",
+ "maps-finddestination-par-format": "Во кој формат да се испише одредницата.",
+ "maps-finddestination-par-directional": "Покажува дали форматот на одредницата треба да има насока.",
+ "maps-geocode-par-location": "Адресата што сакате да ја геокодирате.",
+ "maps-geocode-par-format": "Форматот на добиените координати.",
+ "maps-geocode-par-directional": "Покажува дали координатите треба да се испишат со или без насока.",
+ "maps-geodistance-par-location1": "Првата точка во пресметката на растојанието.",
+ "maps-geodistance-par-location2": "Втората точка во пресметката на растојанието.",
+ "maps-geodistance-par-unit": "Во која единица да се испише растојанието.",
+ "maps-geodistance-par-decimals": "Допуштениот број на децимални места за добиената вредност.",
+ "maps-displaymap-par-mappingservice": "Овозможува задавање на картографска служба што ќе се користи за создавање на картата.",
+ "maps-displaymap-par-coordinates": "На кое место картата ќе биде првично сосредоточена.",
+ "maps-displaymap-par-visitedicon": "Податотечното име на сликата што ќе се користи на обележувачите откако ќе стиснете на изворните обележувачи",
+ "maps-displaymap-par-zoom": "Овозможува задавање на приближеност на картата.\nАко ова не е зададено, а на картата има повеќе обележувачи, тогаш ќе ви се прикаже приближеноста што најмногу одговара на екранот, наместо прилагодливата стандардна приближеност.",
+ "maps-displaymap-par-centre": "Овозможува укажување на координати на средиштето на картата за display_point(s).\nСе прифаќаат и адреси и координати.\nАко ова не е зададено, тогаш картата ќе се сосредоточи во дадениот обележувач, или помеѓу дадените обележувачи.",
+ "maps-displaymap-par-title": "Овозможува задавање на текст што ќе се прикажува во скокачките прозорчиња на сите обележувачи што немаат конкретен наслов.\nАко се користат заедно со ознака, насловот ќе биде задебелен и ќе има линија под него.",
+ "maps-displaymap-par-label": "Овозможува задавање на текст што ќе се прикажува во скокачките прозорчиња на сите обележувачи што немаат конкретна ознака.",
+ "maps-displaymap-par-icon": "Овозможува задавање на икона што ќе се користи за сите обележувачи.",
+ "maps-displaymap-par-circles": "Кругови за приказ",
+ "maps-displaymap-par-copycoords": "Прикажувај прозорче со координатите на местото при стискање на место од кајшто можат да се прекопираат.",
+ "maps-displaymap-par-lines": "Линии за приказ",
+ "maps-displaymap-par-maxzoom": "Најголема приближеност",
+ "maps-displaymap-par-minzoom": "Најмала приближеност",
+ "maps-displaymap-par-polygons": "Многуаголници за приказ",
+ "maps-displaymap-par-rectangles": "Правоаголници за приказ",
+ "maps-displaymap-par-static": "Напарви ја картата неподвижна",
+ "maps-displaymap-par-wmsoverlay": "Користи WMS-облога",
+ "maps-displaymap-par-geojson": "URL на податотеката или името на страницата со GeoJSON-податоци",
+ "maps-fullscreen-button": "Префрли на широк екран",
+ "maps-fullscreen-button-tooltip": "Поглед на картата на цел екран или вгнездено.",
+ "validation-error-invalid-location": "Параметарот $1 мора да претставува важечка местоположба.",
+ "validation-error-invalid-locations": "Параметарот $1 мора да претставува една или повеќе важечки местоположби.",
+ "validation-error-invalid-width": "Параметарот $1 мора да претставува важечка ширина.",
+ "validation-error-invalid-height": "Параметарот $1 мора да претставува важечка висина.",
+ "validation-error-invalid-distance": "Параметарот $1 мора да претставува важечко растојание.",
+ "validation-error-invalid-distances": "Параметарот $1 мора да претставува едно или повеќе важечки растојанија.",
+ "validation-error-invalid-image": "Параметарот $1 мора да биде важечка слика.",
+ "validation-error-invalid-images": "Параметарот $1 мора да биде една или повеќе важечки слики.",
+ "validation-error-invalid-goverlay": "Параметарот $1 мора да претставува важечка облога.",
+ "validation-error-invalid-goverlays": "Параметарот $1 мора да претставува една или повеќе важечки облоги.",
+ "maps-abb-north": "С",
+ "maps-abb-east": "И",
+ "maps-abb-south": "Ј",
+ "maps-abb-west": "З",
+ "maps-latitude": "Геог. ширина",
+ "maps-longitude": "Геог. должина:",
+ "maps-invalid-coordinates": "Вредноста $1 не беше препознаена како правилен збир координати.",
+ "maps_coordinates_missing": "Нема координати за картата.",
+ "maps_geocoding_failed": "{{PLURAL:$2|Следнава адреса не можеше да се геокодира|Следниве адреси не можеа да се геокодираат}}: $1.\nКартата не може да се прикаже.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|Следнава адреса не можеше да се геокодира|Следниве адреси не можеа да се геокодираат}} и затоа {{PLURAL:$2|беше изоставена|беа изоставени}} од картата:\n$1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|Следнава координата не е препознаена|Следниве координати не се препознаени}}: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|Следнава координата не беше препознаена|Следниве координати не беа препознаени}} и {{PLURAL:$2|беше изоставена|беа изоставени}} од картата:\n$1",
+ "maps_map_cannot_be_displayed": "Картата не може да се прикаже.",
+ "maps-geocoder-not-available": "Функцијата за геокодирање на Карти е недостапна. Вашата местоположба не може да се геокодира.",
+ "maps_googlemaps3": "Google Карти вер. 2",
+ "maps_leaflet": "Леток",
+ "maps-leaflet-par-defzoom": "Овозможува задавање на основен степен на приближеност на картата.",
+ "maps-leaflet-par-layers": "Слојот што ќе се покажува кога се вчитува картата.",
+ "maps-leaflet-par-overlaylayers": "Слоеви на облогата што ќе се покажуваат кога се вчитува картата.",
+ "maps-leaflet-par-maxclusterradius": "Најголемиот полупречник што може да го покрие еден грозд од средишниот обележувач (во пиксели).",
+ "maps-leaflet-par-clusterspiderfy": "Кога ќе стиснете на грозд при најслаба приближеност, истиот ќе се разграни за да можете да ги видите сите негови обележувачи.",
+ "maps_click_to_activate": "Стиснете за активирање на картата",
+ "maps_centred_on": "Средиште на картата во $1, $2.",
+ "maps-par-mappingservice": "Овозможува назначување на картографска служба што ќе се користи за создавање на картата.",
+ "maps-par-resizable": "Дава можност за менување на големината на картите со влечење на долниот десен агол.",
+ "maps-par-searchmarkers": "Овозможува пребарување на поединечни бележници од поле вметнато во картата.",
+ "maps-par-zoom": "Степенот на приближеност на картата. Картите со обележувачи по основно се прикажуваат во најголем степен на приближеност што воедно ги прикажува сите обележувачи.",
+ "maps-par-width": "Овозможува задавање на ширина на картата во пиксели по основно, но по желба можете да изберете една од следниве единици: px, ex, em, %.",
+ "maps-par-height": "Овозможува задавање на висина на картата во пиксели по основно, но по желба можете да изберете една од следниве единици: px, ex, em, %.",
+ "maps-par-centre": "На која местоположба да се сосредоточи картата",
+ "maps-par-enable-fullscreen": "Овозможи копче за цел екран",
+ "maps-par-kml": "KML-податотеки за вчитување во картата.",
+ "maps-par-markercluster": "Овозможува спојување на повеќе соседни бележници во еден",
+ "maps-googlemaps3-incompatbrowser": "Вашиот прелистувач не е погоден за Google Карти вер. 3.",
+ "maps-googlemaps3-par-imageoverlays": "Овозможува додавање на слика што ќе се прикажува на укажаното место на картата.",
+ "maps-googlemaps3-par-type": "Типот на карта за првичниот приказ.",
+ "maps-googlemaps3-par-types": "Типовите на карти што ќе бидат достапни преку контролата за тип.",
+ "maps-googlemaps3-par-layers": "Посебни слоеви за вчитување во картата.",
+ "maps-googlemaps3-par-controls": "Контролите што сакате да стојат на картата.",
+ "maps-googlemaps3-par-zoomstyle": "Стил на контролата за приближување.",
+ "maps-googlemaps3-par-typestyle": "Стилот на контролата за тип.",
+ "maps-googlemaps3-par-autoinfowindows": "Автоматски отворај ги сите инфопрозорци откако ќе се вчита страницата.",
+ "maps-googlemaps3-par-gkml": "KML-податотеки вдомени од Google за вчитување на карта.",
+ "maps-googlemaps3-par-kmlrezoom": "Приближи ја картата повторно откако ќе се вчитаат KML-слоевите.",
+ "maps-googlemaps3-par-poi": "Прикажи точки од интерес.",
+ "maps-googlemaps3-par-clustergridsize": "Решеткаста големина на грозд во пиксели.",
+ "maps-par-clustermaxzoom": "Најголемиот степен на приближеност при кој можат да постојат гроздови.",
+ "maps-par-clusterzoomonclick": "Дали, по основно, стискањето на грозд ќе го приближи истиот.",
+ "maps-par-maxclusterradius": "Најголемиот полупречник што може да го покрие еден грозд.",
+ "maps-googlemaps3-par-clusteraveragecenter": "Дали средиштето на секој гроз треба да биде просек од сите обележувачи во гроздот.",
+ "maps-googlemaps3-par-clusterminsize": "Најмалиот број на обележувачи што ќе бидат во грозд пред истите да бидат скриени и да се прикаже нивната бројка.",
+ "mapeditor": "Уредник на карти",
+ "specialpages-group-maps": "Карти",
+ "mapeditor-parser-error": "Се појави грешка при расчленувањето на метаподатоците. Го занемарувам уделот на корисникот.",
+ "mapeditor-none-text": "Нема",
+ "mapeditor-done-button": "Готово",
+ "mapeditor-remove-button": "Отстрани",
+ "mapeditor-import-button2": "Увези",
+ "mapeditor-export-button": "Извези во викикод",
+ "mapeditor-import-button": "Увези од викикод",
+ "mapeditor-select-button": "Одберете го многуаголников",
+ "mapeditor-mapparam-button": "Уреди параметри на картата",
+ "mapeditor-clear-button": "Исчисти ја картата",
+ "mapeditor-code-title": "Викикод",
+ "mapeditor-import-title": "Увези викикод",
+ "mapeditor-import-note": "Имајте предвид дека расчленувачот очекува многу строг формат на викикодот. Тука внесениот код треба да се совпаѓа со излезниот код на функцијата за извоз.",
+ "mapeditor-form-title": "Уреди подробности",
+ "mapeditor-link-title-switcher-popup-text": "Отскок со текст",
+ "mapeditor-link-title-switcher-link-text": "Врска",
+ "mapeditor-form-field-title": "Наслов",
+ "mapeditor-form-field-text": "Текст",
+ "mapeditor-form-field-link": "Врска",
+ "mapeditor-form-field-icon": "Икона",
+ "mapeditor-form-field-group": "Група",
+ "mapeditor-form-field-inlinelabel": "Вметната ознака",
+ "mapeditor-form-field-strokecolor": "Боја на буквите",
+ "mapeditor-form-field-strokeopacity": "Непроѕирност на буквите",
+ "mapeditor-form-field-strokeweight": "Јачина на буквите",
+ "mapeditor-form-field-fillcolor": "Боја на пополнувањето",
+ "mapeditor-form-field-fillopcaity": "Непроѕирност на пополнувањето",
+ "mapeditor-form-field-showonhover": "Прикажи само при минување врз него",
+ "mapeditor-mapparam-title": "Уреди параметри на картата",
+ "mapeditor-mapparam-defoption": "-Изберете параметар-",
+ "mapeditor-imageoverlay-button": "Додај облога од слика",
+ "mapeditor-form-field-image": "Слика",
+ "mapeditor-imageoverlay-title": "Поединости за облога од слика",
+ "mapeditor-form-field-visitedicon": "Икона за посетено",
+ "semanticmaps-unrecognizeddistance": "Вредноста $1 не претставува важечко растојание.",
+ "semanticmaps-kml-link": "Преглед на KML-податотеката",
+ "semanticmaps-default-kml-pagelink": "Преглед на статијата $1",
+ "semanticmaps-latitude": "Геог. ширина: $1",
+ "semanticmaps-longitude": "Геог. должина: $1",
+ "semanticmaps-altitude": "Надм. вис: $1",
+ "semanticmaps-forminput-locations": "Места",
+ "semanticmaps-par-staticlocations": "Список на места за додавање во картатата заедно со побараните податоци. Како и со „display_points“, тука можете да додадете наслов, опис и икона за секое место, користејќи тилда (~) како одделувач.",
+ "semanticmaps-par-showtitle": "Дали да се прикажува насловот на инфопрозорецот на ознаката. Оневозможете го кога користите шаблон за форматирање на содржината на инфопрозорецот.",
+ "semanticmaps-par-hidenamespace": "Дали да се прикажува називот на именскиот простор во инфопрозорецот за ознаката.",
+ "semanticmaps-par-centre": "Средиштето на картата. Ако не е укажано, картата автоматски ќе го одбере средиштето кајшто оптимално ќе се прикажат сите одбележувачи на картата.",
+ "semanticmaps-par-template": "Шаблон за форматирање на содржината на инфопрозорецот.",
+ "semanticmaps-par-geocodecontrol": "Прикажи геокодни котроли.",
+ "semanticmaps-par-activeicon": "Икона за приказ наместо стандардниот бележник, кога активна страница се совпаѓа со бараното",
+ "semanticmaps-par-pagelabel": "Ако му е зададено „да“, сите обележувачи ќе имаат „inlineLabel“ со врска до страницата што ги содржат координатите за обележувачот",
+ "semanticmaps-par-ajaxcoordproperty": "Назив на координатното својство со кое ќе се срочи барање со Ajax.",
+ "semanticmaps-par-ajaxquery": "Второ барање што се праша преку Ajax за добивање на дополнителни координати.",
+ "semanticmaps-par-userparam": "Вредност која се дава при секое повикување на шаблон, ако се користи",
+ "semanticmaps-kml-text": "Текстот на секоја страница. Се презапишува ако има дополнителни барани својства.",
+ "semanticmaps-kml-title": "Стандарден наслов за исходот",
+ "semanticmaps-kml-linkabsolute": "Дали врските да бидат апсолутни (наспроти релативни)",
+ "semanticmaps-kml-pagelinktext": "Текстот на врските на страницата, каде $1 ќе се замени со насловот на страницата",
+ "semanticmaps-shapes-improperformat": "Несоодветно форматирање на $1. Погледајте во документацијата како треба да се форматира.",
+ "semanticmaps-shapes-missingshape": "Не пронајдов облици за $1. Погледајте во документацијата кои облици ви се на располагање.",
+ "validator-type-mapscircle": "Географски круг",
+ "validator-type-mapscircle-list": "Список на кругови",
+ "validator-type-mapsimageoverlay": "Обложна слика",
+ "validator-type-mapsimageoverlay-list": "Список на обложни слики",
+ "validator-type-mapsline": "Географска линија",
+ "validator-type-mapsline-list": "Список на линии",
+ "validator-type-mapslocation": "Географска месност",
+ "validator-type-mapslocation-list": "Список на месности",
+ "validator-type-mapsrectangle": "Географски правоаголник",
+ "validator-type-mapsrectangle-list": "Список на правоаголници",
+ "validator-type-mapspolygon": "Географски многуаголник",
+ "validator-type-mapspolygon-list": "Список на географски многуаголници",
+ "validator-type-wmsoverlay": "Облога Web Map Service",
+ "validator-type-jsonfile": "текст"
+}
diff --git a/www/wiki/extensions/Maps/i18n/ml.json b/www/wiki/extensions/Maps/i18n/ml.json
new file mode 100644
index 00000000..db56e536
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ml.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": [],
+ "maps-abb-north": "വടക്ക്",
+ "maps-abb-east": "കിഴക്ക്",
+ "maps-abb-south": "തെക്ക്",
+ "maps-abb-west": "പടിഞ്ഞാറ്‌",
+ "maps-latitude": "അക്ഷാംശം:",
+ "maps-longitude": "രേഖാംശം:"
+}
diff --git a/www/wiki/extensions/Maps/i18n/mr.json b/www/wiki/extensions/Maps/i18n/mr.json
new file mode 100644
index 00000000..9f40b7e9
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/mr.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": [],
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "W",
+ "maps-latitude": "अक्षांश:",
+ "maps-longitude": "रेखांश:"
+}
diff --git a/www/wiki/extensions/Maps/i18n/ms.json b/www/wiki/extensions/Maps/i18n/ms.json
new file mode 100644
index 00000000..bdc7c527
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ms.json
@@ -0,0 +1,158 @@
+{
+ "@metadata": {
+ "authors": [
+ "Anakmalaysia",
+ "Pizza1016",
+ "Macofe",
+ "Jeluang Terluang"
+ ]
+ },
+ "maps-desc": "Membolehkan pembenaman petak dinamik pada laman wiki, pembuatan geokod alamat dan bermacam-macam lagi operasi geografi",
+ "right-geocode": "Geokod",
+ "maps_map": "Peta",
+ "maps-loading-map": "Peta sedang dimuatkan...",
+ "maps-load-failed": "Peta tidak dapat dimuatkan!",
+ "maps-markers": "Penanda",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "Penanda penapis",
+ "maps-others": "lain-lain",
+ "maps-ns-layer": "Lapisan",
+ "maps-ns-layer-talk": "Perbincangan lapisan",
+ "maps-layer-property": "Sifat",
+ "maps-layer-value": "Nilai",
+ "maps-layer-errors": "Ralat",
+ "maps-layerdef-invalid": "Definisi{{PLURAL:$1||-definisi}} tidak sah",
+ "maps-error-invalid-layertype": "Tiada lapisan jenis \"$1\". {{PLURAL:$3|Jenis|Jenis-jenis}} ini sahaja yang disokong: $2",
+ "maps-error-no-layertype": "Anda perlu menentukan jenis lapisan. {{PLURAL:$2|Jenis|Jenis-jenis}} ini sahaja yang disokong: $1",
+ "validation-error-invalid-layer": "Parameter $1 mestilah lapisan yang sah.",
+ "validation-error-invalid-layers": "Parameter $1 mestilah sekurang-kurangnya satu lapisan yang sah.",
+ "validation-error-no-non-numeric": "Parameter \"$1\" mestilah rentetan bukan nombor.",
+ "maps-layer-of-type": "Lapisan jenis $1",
+ "maps-layer-of-type-and-name": "Lapisan \"$2\" jenis \"$1\"",
+ "maps-layer-type-supported-by": "Jenis lapisan ini {{PLURAL:$2|hanya boleh digunakan dengan perkhidmatan pemetaan $1|boleh digunakan dengan perkhidmatan-perkhidmatan pemetaan yang berikut: $1}}.",
+ "maps-coordinates-description": "Cangkuk penghurai untuk memformatkan koordinat daripada/kepada sebarang format yang disokong.",
+ "maps-displaymap-description": "Memaparkan peta geografi tanpa sebarang penanda tentuan wiki padanya.",
+ "maps-distance-description": "Menukar nilai jarak yang menggunakan unit disokong yang tertentu kepada setaraannya yang menggunakan unit yang lain.",
+ "maps-finddestination-description": "Mencari tujuan dari satu titik permulaan (yang boleh dinyatakan dalam sebarang format yang disokong), bearing permulaan dan jarak.",
+ "maps-geocode-description": "Membolehkan pembuatan geokod alamat, iaitu menukar lokasi yang boleh dibaca oleh manusia kepada peranggu koordinat. Wujudnya sokongan untuk beberapa perkhidmatan geokod yang tidak boleh disamakan dengan perkhidmatan pemetaan.",
+ "maps-geodistance-description": "Menghitung jarak geografi antara dua titik, daripada/kepada sebarang format yang disokong.",
+ "maps-mapsdoc-description": "Memaparkan jadual parameter untuk perkhidmatan pemetaan tertentu bersama nilai-nilai asali dan keterangannya.",
+ "maps-mapsdoc-par-service": "Perkhidmatan pemetaan untuk memaparkan pendokumenan parameter.",
+ "maps-mapsdoc-par-language": "Bahasa untuk memaparkan pendokumenan. Jika tiada terjemahannya, Bahasa Inggeris digunakan pula.",
+ "maps-coordinates-par-location": "Koordinat yang ingin diformatkan.",
+ "maps-coordinates-par-format": "Format sasaran untuk koordinat.",
+ "maps-coordinates-par-directional": "Menunjukkan sama ada koordinat wajar ditunjukkan berarah atau tidak.",
+ "maps-distance-par-distance": "Nilai jarak yang ingin ditukar kepada setaraannya dengan unit yang tertentu.",
+ "maps-distance-par-decimals": "Bilangan maksimum angka pecahan untuk digunakan dalam nilai hasil.",
+ "maps-distance-par-unit": "Unit paparan nilai jarak.",
+ "maps-finddestination-par-location": "Lokasi permulaan.",
+ "maps-finddestination-par-bearing": "Bearing permulaan.",
+ "maps-finddestination-par-distance": "Jarak perjalanan.",
+ "maps-finddestination-par-format": "Format untuk memaparkan destinasi.",
+ "maps-finddestination-par-directional": "Menunjukkan sama ada format destinasi wajar berarah atau tidak.",
+ "maps-geocode-par-location": "Alamat yang ingin dibuatkan geokod.",
+ "maps-geocode-par-format": "Format koordinat yang terhasil.",
+ "maps-geocode-par-directional": "Menunjukkan sama ada koordinat wajar ditunjukkan berarah atau tidak.",
+ "maps-geodistance-par-location1": "Titik pertama dalam peranggu untuk mengira jarak antara.",
+ "maps-geodistance-par-location2": "Titik kedua dalam peranggu untuk mengira jarak antara.",
+ "maps-geodistance-par-unit": "Unit paparan nilai jarak.",
+ "maps-geodistance-par-decimals": "Bilangan maksimum angka pecahan untuk digunakan dalam nilai hasil.",
+ "maps-displaymap-par-mappingservice": "Membolehkan penetapan perkhidmatan pemetaan yang akan digunakan untuk menghasilkan peta.",
+ "maps-displaymap-par-coordinates": "Lokasi yang akan dijadikan pusat peta pada mulanya.",
+ "maps-displaymap-par-visitedicon": "Lokal fail untuk digunakan sebagai ikon dilawat",
+ "maps-displaymap-par-zoom": "Membolehkan penetapan tahap zum peta.\nApabila tidak diberikan sementara wujudnya berbilang penanda pada peta, zom yang paling muat akan diambil, bukan nilai asali yang terkonfigurasi.",
+ "maps-displaymap-par-centre": "Membolehkan penetapan koordinat pusat peta untuk display_point.\nMenerima alamat dan koordinat sekali.\nApabila sifat ini tidak dinyatakan, peta akan menentukan pusatnya sendiri pada penanda yang diberikan, ataupun di antara penanda-penanda yang diberikan.",
+ "maps-displaymap-par-title": "Membolehkan penetapan teks yang akan dipaparkan dalam tetimbul semua penanda yang tiada tajuk tertentu.\nApabila digunakan bersama label, tajuknya akan berupa tebal dan bergaris bawah.",
+ "maps-displaymap-par-label": "Membolehkan penetapan teks yang akan dipaparkan dalam tetimbul semua penanda yang tiada label tertentu.",
+ "maps-displaymap-par-icon": "Membolehkan penetapan ikon yang digunakan untuk semua penanda.",
+ "maps-displaymap-par-circles": "Bulatan untuk dipaparkan",
+ "maps-displaymap-par-copycoords": "Jika dialog dari mana koordinat sesuatu lokasi harus dipaparkan apabila mengklik sesuatu",
+ "maps-displaymap-par-lines": "Garisan untuk dipaparkan",
+ "maps-displaymap-par-maxzoom": "Takat zum maksimum",
+ "maps-displaymap-par-minzoom": "Takat zum minimum",
+ "maps-displaymap-par-polygons": "Poligon untuk dipaparkan",
+ "maps-displaymap-par-rectangles": "Segiempat untuk dipaparkan",
+ "maps-displaymap-par-static": "Jika peta seharusnya statik",
+ "maps-displaymap-par-wmsoverlay": "Gunakan tindanan WMS",
+ "validation-error-invalid-location": "Parameter $1 mestilah lokasi yang sah.",
+ "validation-error-invalid-locations": "Parameter $1 mestilah sekurang-kurangnya satu lokasi yang sah.",
+ "validation-error-invalid-width": "Parameter $1 mestilah lebar yang sah.",
+ "validation-error-invalid-height": "Parameter $1 mestilah tinggi yang sah.",
+ "validation-error-invalid-distance": "Parameter $1 mestilah jarak yang sah.",
+ "validation-error-invalid-distances": "Parameter $1 mestilah sekurang-kurangnya satu jarak yang sah.",
+ "validation-error-invalid-image": "Parameter $1 mestilah imej yang sah.",
+ "validation-error-invalid-images": "Parameter $1 mestilah sekurang-kurangnya satu imej yang sah.",
+ "validation-error-invalid-goverlay": "Parameter $1 mestilah tindanan yang sah.",
+ "validation-error-invalid-goverlays": "Parameter $1 mestilah sekurang-kurangnya satu tindanan yang sah.",
+ "maps-abb-north": "U",
+ "maps-abb-east": "T",
+ "maps-abb-south": "S",
+ "maps-abb-west": "B",
+ "maps-latitude": "Garis lintang:",
+ "maps-longitude": "Garis bujur:",
+ "maps-invalid-coordinates": "Nilai $1 tidak dikenali sebagai peranggu koordinat yang sah.",
+ "maps_coordinates_missing": "Tiada koordinat diberikan bagi peta.",
+ "maps_geocoding_failed": "{{PLURAL:$2|Alamat|Alamat-alamat}} yang berikut tidak dapat dijadikan geokod: $1.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|Alamat|Alamat-alamat}} yang berikut tidak dapat dijadikan geokod dan digugurkan dari peta:\n$1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|Koordinat|Koordinat-koordinat}} yang berikut tidak dikenali: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|Koordinat|Koordinat-koordinat}} yang berikut tidak dikenali dan digugurkan dari peta:\n$1",
+ "maps_map_cannot_be_displayed": "Peta tidak dapat dipaparkan.",
+ "maps-geocoder-not-available": "Tiada ciri geokod Peta. Lokasi anda tidak boleh diberi geokod.",
+ "maps_click_to_activate": "Klik untuk mengaktifkan peta",
+ "maps_centred_on": "Peta berpusat di $1, $2.",
+ "maps-par-mappingservice": "Membolehkan penetapan perkhidmatan pemetaan yang akan digunakan untuk menghasilkan peta.",
+ "maps-par-resizable": "Membolehkan pelarasan saiz peta dengan menyeret sudut kanan bawahnya.",
+ "maps-par-zoom": "Tahap zum peta. Untuk peta yang ada penanda, ini akan berpangkal kepada tahap paling kerap dizumkan yang masih memaparkan semua penanda.",
+ "maps-par-width": "Membolehkan penetapan lebar peta. Pada asali, piksel dianggap sebagai unit, tetapi anda boleh menetapkan mana-mana unit yang berikut: px, ex, em, %.",
+ "maps-par-height": "Membolehkan penetapan tinggi peta. Pada asali, piksel dianggap sebagai unit, tetapi anda boleh menetapkan mana-mana unit yang berikut: px, ex, em, %.",
+ "maps-par-centre": "Lokasi yang hendak dijadikan pusat peta",
+ "maps-par-kml": "Fail-fail KML untuk dimuatkan kepada peta.",
+ "maps-googlemaps3-incompatbrowser": "Pelayar anda tidak serasi dengan Peta Google v3.",
+ "maps-googlemaps3-par-type": "Jenis peta untuk dipaparkan pada mulanya.",
+ "maps-googlemaps3-par-types": "Jenis-jenis peta yang tersedia melalui kawalan jenis.",
+ "maps-googlemaps3-par-layers": "Lapisan khas untuk dimuatkan kepada peta.",
+ "maps-googlemaps3-par-controls": "Alat kawalan untuk diletakkan pada peta.",
+ "maps-googlemaps3-par-zoomstyle": "Gaya kawalan zum.",
+ "maps-googlemaps3-par-typestyle": "Gaya kawalan jenis.",
+ "maps-googlemaps3-par-autoinfowindows": "Membuka semua tetingkap maklumat secara automatik selepas laman dimuatkan.",
+ "maps-googlemaps3-par-gkml": "Fail-fail KML yang dihoskan oleh Google untuk dimuatkan pada peta.",
+ "maps-googlemaps3-par-kmlrezoom": "Zumkan semula peta selepas lapisan-lapisan KML dimuatkan.",
+ "maps-googlemaps3-par-poi": "Tunjukkan tempat-tempat menarik.",
+ "mapeditor": "Editor peta",
+ "specialpages-group-maps": "Peta",
+ "mapeditor-parser-error": "Timbulnya ralat ketika menghuraikan metadata. Input pengguna diabaikan.",
+ "mapeditor-none-text": "Tiada",
+ "mapeditor-done-button": "Siap",
+ "mapeditor-remove-button": "Padamkan",
+ "mapeditor-import-button2": "Import",
+ "mapeditor-export-button": "Eksport ke kod wiki",
+ "mapeditor-import-button": "Import dari kod wiki",
+ "mapeditor-select-button": "Pilih poligon ini",
+ "mapeditor-mapparam-button": "Sunting parameter peta",
+ "mapeditor-clear-button": "Padamkan peta",
+ "mapeditor-code-title": "Kod wiki",
+ "mapeditor-import-title": "Import kod wiki",
+ "mapeditor-import-note": "Sila ambil perhatian bahawa penghurai mengharapkan format yang amat ketat pada kod wiki. Kod yang dimasukkan di sini mestilah sepadan dengan kod yang dikeluarkan oleh kefungsian eksport",
+ "mapeditor-form-title": "Sunting butiran",
+ "mapeditor-link-title-switcher-popup-text": "Tetimbul dengan teks",
+ "mapeditor-link-title-switcher-link-text": "Pautan",
+ "mapeditor-form-field-title": "Tajuk",
+ "mapeditor-form-field-text": "Teks",
+ "mapeditor-form-field-link": "Pautan",
+ "mapeditor-form-field-icon": "Ikon",
+ "mapeditor-form-field-group": "Kumpulan",
+ "mapeditor-form-field-inlinelabel": "Label sebaris",
+ "mapeditor-form-field-strokecolor": "Warna coret",
+ "mapeditor-form-field-strokeopacity": "Kelegapan coret",
+ "mapeditor-form-field-strokeweight": "Berat coret",
+ "mapeditor-form-field-fillcolor": "Warna isi",
+ "mapeditor-form-field-fillopcaity": "Kelegapan isi",
+ "mapeditor-form-field-showonhover": "Tunjukkan dalam keadaan terapung sahaja",
+ "mapeditor-mapparam-title": "Sunting parameter peta",
+ "mapeditor-mapparam-defoption": "-Pilih parameter-",
+ "mapeditor-imageoverlay-button": "Tambah tindihan imej",
+ "mapeditor-form-field-image": "Imej",
+ "mapeditor-imageoverlay-title": "Butiran tindihan imej",
+ "mapeditor-form-field-visitedicon": "Ikon dikunjungi",
+ "semanticmaps-forminput-locations": "Lokasi"
+}
diff --git a/www/wiki/extensions/Maps/i18n/mt.json b/www/wiki/extensions/Maps/i18n/mt.json
new file mode 100644
index 00000000..8335d9f0
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/mt.json
@@ -0,0 +1,48 @@
+{
+ "@metadata": {
+ "authors": [
+ "Chrisportelli"
+ ]
+ },
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-others": "oħrajn",
+ "maps-ns-layer": "Livell",
+ "maps-ns-layer-talk": "Diskussjoni livell",
+ "maps-layer-property": "Proprjetà",
+ "maps-layer-value": "Valur",
+ "maps-layer-errors": "Żbalji",
+ "maps-error-invalid-layertype": "M'hemm l-ebda livell tat-tip \"$1\". {{PLURAL:$3|Dan it-tip biss huwa appoġġjat|Dawn it-tipi biss huma appoġġjati}}: $2",
+ "maps-error-no-layertype": "Trid tispeċifika t-tip ta' livell. {{PLURAL:$2|Dan it-tip biss huwa appoġġjat|Dawn it-tipi biss huma appoġġjati}}: $1",
+ "validation-error-invalid-layer": "Il-parametru $1 irid ikun livell validu.",
+ "validation-error-invalid-layers": "Il-parametru $1 irid ikun wieħed jew iktar livelli validi.",
+ "maps-layer-of-type": "Livell tat-tip $1",
+ "maps-googlemaps3-incompatbrowser": "Il-browżer li qiegħed tuża' mhuwiex kompatibbli ma' Google Maps v3.",
+ "maps-googlemaps3-par-controls": "Il-kontrolli li trid tpoġġi fuq il-mappa.",
+ "maps-googlemaps3-par-poi": "Uri l-punti ta' interess.",
+ "maps-osm-par-thumbs": "Uri l-minjaturi",
+ "maps-osm-par-photos": "Uri r-ritratti",
+ "mapeditor": "Editur tal-mappa",
+ "specialpages-group-maps": "Mapep",
+ "mapeditor-none-text": "L-ebda",
+ "mapeditor-done-button": "Lest",
+ "mapeditor-remove-button": "Neħħi",
+ "mapeditor-import-button2": "Importa",
+ "mapeditor-export-button": "Esporta għal kodiċi wiki",
+ "mapeditor-import-button": "Importa minn kodiċi wiki",
+ "mapeditor-select-button": "Agħżel dan il-poligonu",
+ "mapeditor-mapparam-button": "Immodifika l-parametri tal-mappa",
+ "mapeditor-clear-button": "Naddaf il-mappa",
+ "mapeditor-code-title": "Kodiċi wiki",
+ "mapeditor-import-title": "Importa l-kodiċi wiki",
+ "mapeditor-form-title": "Immodifika d-dettalji",
+ "mapeditor-link-title-switcher-link-text": "Ħolqa",
+ "mapeditor-form-field-title": "Titlu",
+ "mapeditor-form-field-text": "Test",
+ "mapeditor-form-field-link": "Ħolqa",
+ "mapeditor-form-field-icon": "Ikona",
+ "mapeditor-form-field-group": "Grupp",
+ "mapeditor-form-field-fillcolor": "Kulur ta' ġewwa",
+ "mapeditor-form-field-fillopcaity": "Opaċità ta' ġewwa",
+ "mapeditor-mapparam-defoption": "-Agħżel parametru-",
+ "mapeditor-form-field-image": "Stampa"
+}
diff --git a/www/wiki/extensions/Maps/i18n/myv.json b/www/wiki/extensions/Maps/i18n/myv.json
new file mode 100644
index 00000000..ca7ba283
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/myv.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": [],
+ "maps-abb-north": "Веньэльйонкс",
+ "maps-abb-east": "Чилисемайонкс",
+ "maps-abb-south": "Чиньэльйонкс",
+ "maps-abb-west": "Чивалгомайонкс",
+ "maps-latitude": "Келезэ:",
+ "maps-longitude": "Кувалмозо:"
+}
diff --git a/www/wiki/extensions/Maps/i18n/nah.json b/www/wiki/extensions/Maps/i18n/nah.json
new file mode 100644
index 00000000..18f11140
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/nah.json
@@ -0,0 +1,6 @@
+{
+ "@metadata": [],
+ "maps-abb-north": "M",
+ "maps-abb-east": "T",
+ "maps-abb-south": "H"
+}
diff --git a/www/wiki/extensions/Maps/i18n/nb.json b/www/wiki/extensions/Maps/i18n/nb.json
new file mode 100644
index 00000000..7da896db
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/nb.json
@@ -0,0 +1,228 @@
+{
+ "@metadata": {
+ "authors": [
+ "Event",
+ "Kim Eik",
+ "Nghtwlkr",
+ "Danmichaelo",
+ "Macofe",
+ "EvenT",
+ "Jon Harald Søby"
+ ]
+ },
+ "maps-desc": "Gir mulighet for å bygge inn dynamiske kart i wikisider, geokoding av adresser og andre geografiske operasjoner",
+ "right-geocode": "Geokode",
+ "action-geocode": "utføre geokoding på denne wikien",
+ "maps_map": "Kart",
+ "maps-tracking-category": "Sider med kart som vises med kartutvidelsen",
+ "maps-loading-map": "Laster kart...",
+ "maps-load-failed": "Klarte ikke laste kartet.",
+ "maps-markers": "Markører",
+ "maps-copycoords-prompt": "Chrl+C, Enter",
+ "maps-searchmarkers-text": "Filtrer markører",
+ "maps-others": "andre",
+ "maps-kml-parsing-failed": "Klarte ikke å parse én eller flere KML-filer. Dette skjer vanligvis på grunn av en hentingsfeil eller ugyldig XML.",
+ "maps-ns-layer": "Lag",
+ "maps-ns-layer-talk": "Lagdiskusjon",
+ "maps-layer-property": "Egenskap",
+ "maps-layer-value": "Verdi",
+ "maps-layer-errors": "Feil",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Ugyldig definisjon|Ugyldige definisjoner}}",
+ "maps-layerdef-invalid-fatal": "Katastrofalt ugyldig definisjon",
+ "maps-layerdef-wrong-namespace": "Lagdefinisjoner er kun gyldige på sider i navnerommet «$1»",
+ "maps-layerdef-equal-layer-name": "Lagnavn må være unike innen samme lagside. «$1» brukes allerede av et annet lag.",
+ "maps-layerpage-usage": "Sider med kart som bruker laget «$1»",
+ "maps-layerpage-nousage": "Ingen sider bruker dette laget for tiden.",
+ "maps-error-invalid-layertype": "Det er ingen lag av typen «$1». Bare {{PLURAL:$3|denne typen|disse typene}} er støttet: $2",
+ "maps-error-no-layertype": "Du må angi en lagtype. Bare {{PLURAL:$2|denne typen|disse typene}} er støttet: $1",
+ "validation-error-invalid-layer": "Parameter $1 må været et gyldig lag.",
+ "validation-error-invalid-layers": "Parameter $1 må være et eller flere gyldige lag.",
+ "validation-error-no-non-numeric": "Parameteren «$1» må være en ikke-numerisk streng.",
+ "validation-error-no-non-numerics": "Parameteren «$1» må være én eller flere ikke-numeriske strenger.",
+ "maps-layer-of-type": "Lagtype $1",
+ "maps-layer-of-type-and-name": "Lag «$2» av typen «$1»",
+ "maps-layer-type-supported-by": "Denne lagtypen kan bare brukes med {{PLURAL:$2|karttjenesten $1|disse karttjenestene: $1}}.",
+ "maps-coordinates-description": "Parser-hook for å formatere koordinater, fra og til vilkårlige av de støttede formatene.",
+ "maps-displaymap-description": "Vis geografiske kart uten wiki-spesifiserte markører.",
+ "maps-distance-description": "Gjør om en distanse basert på en viss støttet enhet til tilsvarende verdi i en annen enhet.",
+ "maps-finddestination-description": "Finn et mål med gitt startpunkt (som kan være i ett av de støttede formatene), en initial retning og distanse.",
+ "maps-geocode-description": "Gjør tilgjengelig geokoding av adresser, dvs. gjør menneskelesbare lokasjoner til koordinatsett. Det finnes støtte for flere geokodingstjenester, som ikke må forveksles med mappingtjenester.",
+ "maps-geodistance-description": "Beregn den geografiske avstanden mellom to punkter, fra og til et vilkårlig av de støttede formatene.",
+ "maps-mapsdoc-description": "Vis en tabell med parametrene for en spesifisert mappingstjeneste sammen med deres standardverdier og beskrivelser.",
+ "maps-layerdefinition-description": "Beskriver et egendefinert lag som kan vises med andre kartfunksjoner.",
+ "maps-mapsdoc-par-service": "Mappingstjenesten for å vise parameterdokumentasjon.",
+ "maps-mapsdoc-par-language": "Språket for å vise dokumentasjonen. Hvis ingen slik oversettelse er tilgjengelig, blir engelsk brukt.",
+ "maps-coordinates-par-location": "Koordinatene du ønsker å formatere.",
+ "maps-coordinates-par-format": "Målformatet for koordinatene.",
+ "maps-coordinates-par-directional": "Angir om koordinatene burde vises med retning eller ikke.",
+ "maps-par-scrollwheelzoom": "Angir om musbasert skrolling skal aktiveres eller ikke.",
+ "maps-distance-par-distance": "Avstanden som skal omregnes til en bestemt enhet.",
+ "maps-distance-par-decimals": "Maksimalt antall desimalsifre som skal brukes for resulterende verdi.",
+ "maps-distance-par-unit": "Enhet for visning av avstand.",
+ "maps-finddestination-par-location": "Opprinnelig lokasjon.",
+ "maps-finddestination-par-bearing": "Startpeiling.",
+ "maps-finddestination-par-distance": "Reiseavstand",
+ "maps-finddestination-par-format": "Utdataformat for målposisjonen.",
+ "maps-finddestination-par-directional": "Angir om formatet for målposisjonen skal være retningsbasert eller ikke.",
+ "maps-geocode-par-location": "Adressen du ønsker å geokode.",
+ "maps-geocode-par-format": "Formatet for de resulterende koordinatene.",
+ "maps-geocode-par-directional": "Angir om koordinatene skal vises med retning eller ikke.",
+ "maps-geodistance-par-location1": "Første punktet i settet for å beregne avstand.",
+ "maps-geodistance-par-location2": "Andre punktet i settet for å beregne avstand.",
+ "maps-geodistance-par-unit": "Enheten avstanden skal måles i.",
+ "maps-geodistance-par-decimals": "Maksimalt antall desimalsifre som skal brukes i resultatverdien.",
+ "maps-displaymap-par-mappingservice": "Tillater å sette karttjenesten som vil brukes for å skape kartet.",
+ "maps-displaymap-par-coordinates": "En eller flere steder til å markeres på kartet. De blir angitt med en markør.",
+ "maps-displaymap-par-visitedicon": "Filnavnet for bildet som skal brukes som markørikon etter at de opprinnelige markørene har blitt klikket.",
+ "maps-displaymap-par-zoom": "Tillater innstilling av zoom-nivået for kartet.\nHvis nivået ikke er valgt og det er flere markører til stede på kartet, vil det mest passende zoom-nivået bli valgt, ikke standardverdien fra konfigureringen.",
+ "maps-displaymap-par-centre": "Tillater innstilling av koordinatene for sentrum av kartet for display_point(s).\nAksepter både adresser og koordinater.\nHvis denne egenskapene ikke er angitt, vil kartet sentrere seg selv rundt en tilgjengelig markør eller mellom disse om det finnes flere.",
+ "maps-displaymap-par-title": "Tillater å angi tekst som skal vises i popup-vindu for alle markører som mangler en særskilt tittel.\nNår dette anvendes sammen med en etikett, blir tittelen fet og understreket.",
+ "maps-displaymap-par-label": "Tillater å angi tekst som skal vises i popup-vinduet for de markørene som mangler en særskilt etikett.",
+ "maps-displaymap-par-icon": "Tillater å sette ikonet som brukes for alle markørene.",
+ "maps-displaymap-par-circles": "Sirkler å vise",
+ "maps-displaymap-par-copycoords": "Viser en dialog når du klikker på et sted hvorfra du kan kopiere koordinatene.",
+ "maps-displaymap-par-lines": "Linjer å vise",
+ "maps-displaymap-par-maxzoom": "Maksimalt zoomnivå",
+ "maps-displaymap-par-minzoom": "Minste zoomnivå",
+ "maps-displaymap-par-polygons": "Polygoner å vise",
+ "maps-displaymap-par-rectangles": "Rektangler å vise",
+ "maps-displaymap-par-static": "Gjør kartet statisk",
+ "maps-displaymap-par-wmsoverlay": "Legger på et WMS-lag",
+ "maps-displaymap-par-geojson": "URL-en til ei GeoJSON-fil",
+ "maps-fullscreen-button": "Slå av/på fullskjerm",
+ "maps-fullscreen-button-tooltip": "Vis kartet som fullskjerm eller innbygd.",
+ "validation-error-invalid-location": "Parameter $1 må være en gyldig lokasjon.",
+ "validation-error-invalid-locations": "Parameter $1 må være en eller flere gyldige lokasjoner.",
+ "validation-error-invalid-width": "Parameter $1 må være en gyldig bredde.",
+ "validation-error-invalid-height": "Parameter $1 må være en gyldig høyde.",
+ "validation-error-invalid-distance": "Parameter $1 må være en gyldig avstand.",
+ "validation-error-invalid-distances": "Parameter $1 må være en eller flere gyldige avstander.",
+ "validation-error-invalid-image": "Parameter $1 må være et gyldig bilde.",
+ "validation-error-invalid-images": "Parameter $1 må være et eller flere gyldige bilder.",
+ "validation-error-invalid-goverlay": "Parameter $1 må være et gyldig overlegg.",
+ "validation-error-invalid-goverlays": "Parameter $1 må være et eller flere gyldige overlegg.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "Ø",
+ "maps-abb-south": "S",
+ "maps-abb-west": "V",
+ "maps-latitude": "Breddegrad:",
+ "maps-longitude": "Lengdegrad:",
+ "maps-invalid-coordinates": "Verdien $1 ble ikke gjenkjent som et gyldig sett med koordinater.",
+ "maps_coordinates_missing": "Ingen koordinater oppgitt for kartet.",
+ "maps_geocoding_failed": "Følgende {{PLURAL:$2|adresse|adresser}} kunne ikke geokodes: $1.",
+ "maps_geocoding_failed_for": "Følgende {{PLURAL:$2|adresse|adresser}} kunne ikke geokodes og har blitt utelatt fra kartet:\n$1",
+ "maps_unrecognized_coords": "Følgende {{PLURAL:$2|koordinat|koordinat}} ble ikke gjenkjent: $1.",
+ "maps_unrecognized_coords_for": "Følgende {{PLURAL:$2|koordinat|koordinater}} ble ikke gjenkjent og har blitt utelatt fra kartet:\n$1",
+ "maps_map_cannot_be_displayed": "Kartet kan ikke vises.",
+ "maps-geocoder-not-available": "Geokodingsfunksjonen i Maps er ikke tilgjengelig. Din plassering kan ikke geokodes.",
+ "maps_leaflet": "Leaflet",
+ "maps-leaflet-par-defzoom": "Tillater å sette standard zoom-nivå for kartet.",
+ "maps-leaflet-par-layers": "Laget som vil vises mens kartet laster.",
+ "maps-leaflet-par-overlaylayers": "Overleggslagene som vises mens kartet lastes.",
+ "maps-leaflet-par-maxclusterradius": "Minimum radius som et kluster dekker fra den sentrale markøren (angitt i piksler)",
+ "maps-leaflet-par-clusterspiderfy": "Når du klikker et kluster ved laveste zoom-nivå, så spres den ut slik at du kan se alle dens markører.",
+ "maps_click_to_activate": "Klikk for å aktivere kartet",
+ "maps_centred_on": "Kart sentrert om $1, $2.",
+ "maps-par-mappingservice": "Tillater å sette karttjenesten som vil brukes for å skape kartet.",
+ "maps-par-resizable": "Gjør at kartets størrelse kan endres ved å dra idets nedre hjørne.",
+ "maps-par-searchmarkers": "Tillater å søke etter spesifikke markører via et felt som er innfestet i kartet.",
+ "maps-par-zoom": "Zoomnivået for kartet. For kart med markører vil standardverdien være den med mest høyest zoom som viser alle markørene.",
+ "maps-par-width": "Tillater at kartbredden angis. Standard enhet er pixler /(px), men du kan alternativt angi ex, em, %.",
+ "maps-par-height": "Tillater at karthøyden angis. Standard enhet er pixler /(px), men du kan alternativt angi ex, em, %.",
+ "maps-par-centre": "Stedet som kartet sentreres rundt",
+ "maps-par-enable-fullscreen": "Aktiver fullskjermknappen",
+ "maps-par-kml": "KML-filer som lastes inn på kortet.",
+ "maps-par-markercluster": "Tillater å slå sammen flere nærliggende markører til en samlet markør",
+ "maps-googlemaps3-incompatbrowser": "Din nettleser er ikke kompatibel med Google Maps v3.",
+ "maps-googlemaps3-par-imageoverlays": "Tillater å legge inn et bilde som vises på det angitte stedet på kartet.",
+ "maps-googlemaps3-par-type": "Karttypen som vises initialt.",
+ "maps-googlemaps3-par-types": "Karttypene som vil bli tilgjengelig via typevelger.",
+ "maps-googlemaps3-par-layers": "Spesielle lag som kan lastes inn på kartet.",
+ "maps-googlemaps3-par-controls": "Velgerne som kan plasseres på kartet.",
+ "maps-googlemaps3-par-zoomstyle": "Stilen for zoom-velgeren.",
+ "maps-googlemaps3-par-typestyle": "Stilen for typevelgeren.",
+ "maps-googlemaps3-par-autoinfowindows": "Åpne automatisk alle informasjonsvinduer etter at siden er blitt lastet.",
+ "maps-googlemaps3-par-gkml": "KML-filer, forvaltet av Google, til å legge inn på kartet.",
+ "maps-googlemaps3-par-kmlrezoom": "Zoom tilbake til kartet etter at KML-lagene har blitt lastet.",
+ "maps-googlemaps3-par-poi": "Vis plasser av interesse.",
+ "maps-googlemaps3-par-clustergridsize": "Rutenettstørrelsen for et kluster angitt i piksler.",
+ "maps-par-clustermaxzoom": "Det maksimale zoomnivået der et kluster kan eksistere.",
+ "maps-par-clusterzoomonclick": "Angir at standardoppførselen ved å klikke på et kluster er å zoome inn på det.",
+ "maps-par-maxclusterradius": "Maksimal radius som et kluster kan dekke.",
+ "maps-googlemaps3-par-clusteraveragecenter": "Angir at sentrum for alle kluster bør være middelposisjonen av alle markører i klustret.",
+ "maps-googlemaps3-par-clusterminsize": "Minimalt antall markører i et kluster før markørene skjules og kun antallet vises.",
+ "mapeditor": "Kart tegner",
+ "specialpages-group-maps": "Kart",
+ "mapeditor-parser-error": "En feil oppstod under parsing av data.",
+ "mapeditor-none-text": "Ingen",
+ "mapeditor-done-button": "Ferdig",
+ "mapeditor-remove-button": "Fjern",
+ "mapeditor-import-button2": "Importer",
+ "mapeditor-export-button": "Eksporter til wiki kode",
+ "mapeditor-import-button": "Importer fra wiki kode",
+ "mapeditor-select-button": "Velg dette polygonet",
+ "mapeditor-mapparam-button": "Rediger kart parametere",
+ "mapeditor-clear-button": "Tøm kart",
+ "mapeditor-code-title": "Wiki kode",
+ "mapeditor-import-title": "Importer wiki kode",
+ "mapeditor-import-note": "Vennligst merk at tolkeren krever veldig streng format på wiki koden. Koden bør være lik i struktur som ved eksport funksjonaliteten.",
+ "mapeditor-form-title": "Rediger detaljer",
+ "mapeditor-link-title-switcher-popup-text": "Popup m/tekst",
+ "mapeditor-link-title-switcher-link-text": "Lenke",
+ "mapeditor-form-field-title": "Tittel",
+ "mapeditor-form-field-text": "Tekst",
+ "mapeditor-form-field-link": "Lenke",
+ "mapeditor-form-field-icon": "Ikon",
+ "mapeditor-form-field-group": "Gruppe",
+ "mapeditor-form-field-inlinelabel": "Merkelapp",
+ "mapeditor-form-field-strokecolor": "Kant farge",
+ "mapeditor-form-field-strokeopacity": "Kant gjennomsiktighet",
+ "mapeditor-form-field-strokeweight": "Kant tykkelse",
+ "mapeditor-form-field-fillcolor": "Fyll farge",
+ "mapeditor-form-field-fillopcaity": "Fyll gjennomsiktighet",
+ "mapeditor-form-field-showonhover": "Vis bare når peker er over.",
+ "mapeditor-mapparam-title": "Rediger kart parametere",
+ "mapeditor-mapparam-defoption": "-Velg parameter-",
+ "mapeditor-imageoverlay-button": "Legg til bilde",
+ "mapeditor-form-field-image": "Bilde",
+ "mapeditor-imageoverlay-title": "Bilde lag detaljer",
+ "mapeditor-form-field-visitedicon": "Besøkt ikon",
+ "semanticmaps-unrecognizeddistance": "Verdien $1 er ikke en gyldig avstand.",
+ "semanticmaps-kml-link": "Vis KML-filen",
+ "semanticmaps-default-kml-pagelink": "Vis siden $1",
+ "semanticmaps-latitude": "Breddegrad: $1",
+ "semanticmaps-longitude": "Lengdegrad: $1",
+ "semanticmaps-altitude": "Høyde over havet: $1",
+ "semanticmaps-forminput-locations": "Lokasjoner",
+ "semanticmaps-par-staticlocations": "En lokasjonsliste til å legge inn i kartet sammen med data fra spørringen. Som med display_points, kan du legge inn en tittel, en beskrivelse og et ikon per lokasjon med tilde \"~\" som skilletegn.",
+ "semanticmaps-par-showtitle": "Vise tittel i markørinfovinduet eller ikke. Deaktivering er ofte nyttig når en bruker en mal for å formatere innholdet i infovinduet.",
+ "semanticmaps-par-hidenamespace": "Vis tittelen for navnerommet i markørens informasjonsboks",
+ "semanticmaps-par-centre": "Kartets sentrum. Hvis dette ikke er angitt, vil kartet automatisk velge det optimale senteret for å vise alle kartmarkørene.",
+ "semanticmaps-par-template": "Mal som brukes for å formatere innholdet i infovinduet.",
+ "semanticmaps-par-geocodecontrol": "Vis geokodingsstyringen",
+ "semanticmaps-par-activeicon": "Ikon som vises istedenfor standardmarkøren, når aktiv side er lik resultatet av spørringen",
+ "semanticmaps-par-pagelabel": "Når satt til \"ja\", vil alle markører ha en \"inlineLabel\" med en lenke til siden som inneholder markørens koordinater",
+ "semanticmaps-par-ajaxcoordproperty": "Navnet på posisjonsegenskapen som vil brukes for å bygge ajax-spørringen.",
+ "semanticmaps-par-ajaxquery": "Den andre spørringen som sendes via ajax for å hente ytterligere koordinater.",
+ "semanticmaps-par-userparam": "Verdi som sendes til hvert malkall, hvis en mal brukes",
+ "semanticmaps-kml-text": "Teksten knyttes til hver side. Overstyrt av spørringer på ekstra egenskaper, om noen.",
+ "semanticmaps-kml-title": "Standard resultatoverskrift",
+ "semanticmaps-kml-linkabsolute": "Skal lenker være absolutte (eller relative)",
+ "semanticmaps-kml-pagelinktext": "Teksten som skal brukes for lenker til siden, der $1 erstattes av sidetittelen",
+ "semanticmaps-shapes-improperformat": "Ulovlig formatering av $1. Vennligst les dokumentasjonen for å korrigere",
+ "semanticmaps-shapes-missingshape": "Ingen figurer funnet for $1. Vennlig bruk tilgjengelige figurer fra dokumentasjonen",
+ "validator-type-mapscircle": "Geografisk sirkel",
+ "validator-type-mapscircle-list": "Liste over sirkler",
+ "validator-type-mapsimageoverlay": "Bildeoverlegg",
+ "validator-type-mapsimageoverlay-list": "Liste over bildeoverlegg",
+ "validator-type-mapsline": "Geografisk linje",
+ "validator-type-mapsline-list": "Liste over linjer",
+ "validator-type-mapslocation": "Geografisk sted",
+ "validator-type-mapslocation-list": "Liste over steder",
+ "validator-type-mapsrectangle": "Geografisk rektangel",
+ "validator-type-mapsrectangle-list": "Liste over rektangler",
+ "validator-type-mapspolygon": "Geografisk polygon",
+ "validator-type-mapspolygon-list": "Liste over geografiske polygoner",
+ "validator-type-wmsoverlay": "Webmappetjensteoverlegg",
+ "validator-type-jsonfile": "URL"
+}
diff --git a/www/wiki/extensions/Maps/i18n/nds.json b/www/wiki/extensions/Maps/i18n/nds.json
new file mode 100644
index 00000000..52c347b7
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/nds.json
@@ -0,0 +1,24 @@
+{
+ "@metadata": {
+ "authors": [
+ "Joachim Mos"
+ ]
+ },
+ "maps_map": "Koort",
+ "maps-layer-property": "Egenschop",
+ "maps-layer-errors": "Fählers",
+ "maps-abb-north": "N",
+ "maps-abb-east": "O",
+ "maps-abb-south": "S",
+ "maps-abb-west": "W",
+ "mapeditor-none-text": "Keen",
+ "mapeditor-done-button": "Färtig",
+ "mapeditor-code-title": "Wikicode",
+ "mapeditor-link-title-switcher-link-text": "Lenk",
+ "mapeditor-form-field-title": "Titel",
+ "mapeditor-form-field-text": "Text",
+ "mapeditor-form-field-link": "Lenk",
+ "mapeditor-form-field-icon": "Symbool",
+ "mapeditor-form-field-group": "Köppel",
+ "mapeditor-form-field-image": "Bild"
+}
diff --git a/www/wiki/extensions/Maps/i18n/nl-informal.json b/www/wiki/extensions/Maps/i18n/nl-informal.json
new file mode 100644
index 00000000..ad0f967a
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/nl-informal.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "Siebrand"
+ ]
+ },
+ "maps-error-no-layertype": "Je moet het laagtype opgeven. Alleen {{PLURAL:$2|dit type wordt|deze typen worden}} ondersteund: $1",
+ "maps-geocoder-not-available": "Geocoderen via Maps is niet beschikbaar. Het geocoderen van je locatie is niet mogelijk.",
+ "maps-par-width": "Maakt het mogelijk om de breedte van de kaart in te stellen. Standaard worden pixels als eenheid gebruikt, maar je kunt expliciet een van deze eenheden opgeven: px, ex, em, %.",
+ "maps-par-height": "Maakt het mogelijk om de hoogte van de kaart in te stellen. Standaard worden pixels als eenheid gebruikt, maar je kunt expliciet een van deze eenheden aangeven: px, ex, em, %.",
+ "maps-googlemaps3-incompatbrowser": "Je browser kan niet werken met Google Maps v3."
+}
diff --git a/www/wiki/extensions/Maps/i18n/nl.json b/www/wiki/extensions/Maps/i18n/nl.json
new file mode 100644
index 00000000..53b3c869
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/nl.json
@@ -0,0 +1,206 @@
+{
+ "@metadata": {
+ "authors": [
+ "AvatarTeam",
+ "Kjell",
+ "Konovalov",
+ "SPQRobin",
+ "Siebrand",
+ "Sjoerddebruin",
+ "Esketti",
+ "MrLeopold",
+ "Hex",
+ "Mainframe98"
+ ]
+ },
+ "maps-desc": "Maakt het insluiten van dynamische kaarten in de wikipagina's, het geocoderen van adressen en andere geografische activiteiten mogelijk",
+ "right-geocode": "Geocoderen",
+ "action-geocode": "doe geocoding op deze wiki",
+ "maps_map": "Kaart",
+ "maps-tracking-category": "Pagina's met een kaart die gerenderd is door de uitbreiding Maps",
+ "maps-loading-map": "Bezig met het laden van de kaart...",
+ "maps-load-failed": "De kaart kon niet geladen worden!",
+ "maps-markers": "Markeringen",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "Filtermarkeringen",
+ "maps-others": "anderen",
+ "maps-kml-parsing-failed": "Eén of meer KML-bestanden konden niet verwerkt worden. Dit is meestal te wijten aan het niet kunnen ophalen van het bestand of ongeldige XML.",
+ "maps-ns-layer": "Laag",
+ "maps-ns-layer-talk": "Overleg_laag",
+ "maps-layer-property": "Eigenschap",
+ "maps-layer-value": "Waarde",
+ "maps-layer-errors": "Fouten",
+ "maps-layerdef-invalid": "Ongeldige definitie{{PLURAL:$1||s}}",
+ "maps-layerdef-invalid-fatal": "Fatale ongeldige definitie",
+ "maps-layerdef-wrong-namespace": "Lagendefinities zijn alleen geldig op pagina's van de naamruimte \"$1\"",
+ "maps-layerdef-equal-layer-name": "Lagennamen moeten uniek zijn binnen dezelfde lagenpagina. \"$1'wordt al gebruikt door een andere laag.",
+ "maps-layerpage-usage": "Pagina's met kaarten die laag \"$1\" gebruiken",
+ "maps-layerpage-nousage": "Er zijn op het moment geen pagina's die deze laag gebruiken.",
+ "maps-error-invalid-layertype": "Er zijn geen lagen van het type \"$1\". Alleen {{PLURAL:$3|dit type wordt|deze typen worden}} ondersteund: $2",
+ "maps-error-no-layertype": "U moet het laagtype opgeven. Alleen {{PLURAL:$2|dit type wordt|deze typen worden}} ondersteund: $1",
+ "validation-error-invalid-layer": "Parameter $1 moet een geldige laag zijn.",
+ "validation-error-invalid-layers": "Parameter $1 moet een of meer geldige lagen zijn.",
+ "validation-error-no-non-numeric": "Parameter \"$1\" moet een niet-numerieke tekenreeks zijn.",
+ "validation-error-no-non-numerics": "Parameter \"$1\" moet één of meer niet-numerieke tekenreeksen zijn.",
+ "maps-layer-of-type": "Laag van het type $1",
+ "maps-layer-of-type-and-name": "Laag \"$2\" van het type \"$1\"",
+ "maps-layer-type-supported-by": "Dit laagtype kan {{PLURAL:$2|alleen gebruikt worden met de kaartdienst $1|gebruikt worden met de kaartdiensten $1}}.",
+ "maps-coordinates-description": "Parserhook om coördinaten op te maken, van en naar alle ondersteunde notaties.",
+ "maps-displaymap-description": "Geografische kaarten weergeven zonder in de wiki gedefinieerde markeringen.",
+ "maps-distance-description": "Converteren naar een afstand met behulp van een bepaalde ondersteunde eenheid naar een equivalent in een andere eenheid.",
+ "maps-finddestination-description": "Een bestemming vinden via een gegeven beginpunt (in elke ondersteunde notatie), een richting en een afstand.",
+ "maps-geocode-description": "Maakt het geocoderen van adressen mogelijk, dat wil zeggen dat leesbare locaties worden omgezet in verzamelingen coördinaten. Er is ondersteuning voor verschillende geocoderingsdiensten die niet verward moeten worden met kaartdiensten.",
+ "maps-geodistance-description": "De geografische afstand tussen twee punten berekenen, van en naar alle ondersteunde notaties.",
+ "maps-mapsdoc-description": "Een tabel met de parameters voor een bepaalde kaartdienst weergeven samen met hun standaardwaarde en beschrijving.",
+ "maps-layerdefinition-description": "Beschrijft een aangepaste laag die weergegeven kan worden met andere Kaart-functies.",
+ "maps-mapsdoc-par-service": "De kaartdienst waarvoor parameterdocumentatie weergegeven moet worden.",
+ "maps-mapsdoc-par-language": "De taal waarin de documentatie wordt weergegeven. Indien er geen vertaling beschikbaar is, wordt Engels gebruikt.",
+ "maps-coordinates-par-location": "De op te maken coördinaten.",
+ "maps-coordinates-par-format": "Gewenste notatie voor de coördinaten.",
+ "maps-coordinates-par-directional": "Geeft aan of de coördinaten wel of niet directioneel uitgestuurd moeten worden.",
+ "maps-distance-par-distance": "De afstand om te converteren naar haar equivalent in een opgegeven eenheid.",
+ "maps-distance-par-decimals": "Het maximale aantal te gebruiken decimalen in de resultaatwaarde.",
+ "maps-distance-par-unit": "De eenheid waarin de afstand weergegeven moet worden.",
+ "maps-finddestination-par-location": "De initiële locatie.",
+ "maps-finddestination-par-bearing": "De initiële richting.",
+ "maps-finddestination-par-distance": "De te reizen afstand.",
+ "maps-finddestination-par-format": "De notatie waarin de bestemming weergegeven moet worden.",
+ "maps-finddestination-par-directional": "Geeft aan of de notatie van de bestemming directioneel moet zijn of niet.",
+ "maps-geocode-par-location": "Het te geocoderen adres.",
+ "maps-geocode-par-format": "De notatie voor de resulterende coördinaten.",
+ "maps-geocode-par-directional": "Geeft aan of de coördinaten directioneel uitgevoerd moeten worden of niet.",
+ "maps-geodistance-par-location1": "Het eerste punt in de verzameling om de afstand tussen te berekenen.",
+ "maps-geodistance-par-location2": "Het tweede punt in de verzameling om de afstand tussen te berekenen.",
+ "maps-geodistance-par-unit": "De eenheid om de afstand in weer te geven.",
+ "maps-geodistance-par-decimals": "Het maximale aantal te gebruiken decimalen in de resulterende waarde.",
+ "maps-displaymap-par-mappingservice": "Maakt het mogelijk om de kaartdienst in te stellen die wordt gebruikt om de kaart te maken.",
+ "maps-displaymap-par-coordinates": "De locatie die zich bij de eerste keer laden in het centrum van de kaart bevindt.",
+ "maps-displaymap-par-visitedicon": "De bestandsnaam van een afbeelding voen gebruik als markeringspictogrammen nadat op de oorspronkelijke markeringen is geklikt",
+ "maps-displaymap-par-zoom": "Maakt het mogelijk het schaalniveau van de kaart in te stellen.\nAls deze waarde niet wordt ingesteld en er staan meerdere markers op de kaart, dan wordt het best passende schaalniveau gekozen, niet het instelbare standaardniveau.",
+ "maps-displaymap-par-centre": "Maakt het mogelijk de coördinaten voor het midden van de kaart in te stellen voor display_point(s).\nKan ingesteld worden met adressen en coördinaten.\nAls deze waarde niet wordt ingesteld, wordt de kaart gecentreerd op of tussen de opgegeven marker(s).",
+ "maps-displaymap-par-title": "Maakt het mogelijk om tekst in te stellen die wordt weergegeven in de pop-ups van alle markeringen zonder gespecificeerde naam.\nAls dit samen met een label wordt gebruikt, wordt de titel vetgedrukt en onderstreept weergegeven.",
+ "maps-displaymap-par-label": "Maakt het mogelijk om tekst in te stellen die wordt weergegeven in de pop-ups van alle markeringen zonder gespecificeerd label.",
+ "maps-displaymap-par-icon": "Maakt het mogelijk het pictogram voor alle markers in te stellen.",
+ "maps-displaymap-par-circles": "Weer te geven cirkels",
+ "maps-displaymap-par-copycoords": "Dialoogvenster weergeven na klikken op een locatie waarvan de coördinaten gekopieerd kunnen worden",
+ "maps-displaymap-par-lines": "Weer te geven regels",
+ "maps-displaymap-par-maxzoom": "Het maximale zoomniveau",
+ "maps-displaymap-par-minzoom": "Het minimale zoomniveau",
+ "maps-displaymap-par-polygons": "Weer te geven polygonen",
+ "maps-displaymap-par-rectangles": "Weer te geven rechthoeken",
+ "maps-displaymap-par-static": "Maak de kaart statisch",
+ "maps-displaymap-par-wmsoverlay": "WMS-overlay gebruiken",
+ "maps-fullscreen-button": "Volledig scherm in- of uitschakelen",
+ "maps-fullscreen-button-tooltip": "De kaart als volledig scherm of ingesloten bekijken.",
+ "validation-error-invalid-location": "Parameter $1 moet een geldige locatie zijn.",
+ "validation-error-invalid-locations": "Parameter $1 moet een of meer geldige locaties zijn.",
+ "validation-error-invalid-width": "Parameter $1 moet een geldige breedte zijn.",
+ "validation-error-invalid-height": "Parameter $1 moet een geldige hoogte zijn.",
+ "validation-error-invalid-distance": "Parameter $1 moet een geldige afstand zijn.",
+ "validation-error-invalid-distances": "Parameter $1 moet een of meer geldige afstanden zijn.",
+ "validation-error-invalid-image": "Parameter $1 moet een geldige afbeelding zijn.",
+ "validation-error-invalid-images": "Parameter $1 moet een of meer geldige afbeeldingen zijn.",
+ "validation-error-invalid-goverlay": "Parameter $1 moet een geldige overlay zijn.",
+ "validation-error-invalid-goverlays": "Parameter $1 moet een of meer geldige overlays zijn.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "O",
+ "maps-abb-south": "Z",
+ "maps-abb-west": "W",
+ "maps-latitude": "Breedtegraad:",
+ "maps-longitude": "Lengtegraad:",
+ "maps-invalid-coordinates": "De waarde \"$1\" is niet herkend als geldige coördinaten.",
+ "maps_coordinates_missing": "Er zijn geen coördinaten opgegeven voor de kaart.",
+ "maps_geocoding_failed": "Voor {{PLURAL:$2|het volgende adres|de volgende adressen}} was geocodering niet mogelijk: $1\nDe kaart kan niet worden weergegeven.",
+ "maps_geocoding_failed_for": "Voor {{PLURAL:$2|het volgende adres|de volgende adressen}} was geocodering niet mogelijk en {{PLURAL:$2|dit is|deze zijn}} weggelaten uit de kaart:\n$1",
+ "maps_unrecognized_coords": "De volgende {{PLURAL:$2|coördinaat is|coördinaten zijn}} niet herkend: $1.",
+ "maps_unrecognized_coords_for": "De volgende {{PLURAL:$2|coördinaat is niet herkend en is|coördinaten zijn niet herkend en zijn}} weggelaten uit de kaart:\n$1.",
+ "maps_map_cannot_be_displayed": "De kaart kan niet weergegeven worden.",
+ "maps-geocoder-not-available": "Geocoderen via Maps is niet beschikbaar. Het geocoderen van uw locatie is niet mogelijk.",
+ "maps_click_to_activate": "Klik om de kaart te activeren",
+ "maps_centred_on": "Kaart gecentreerd op $1, $2.",
+ "maps-par-mappingservice": "Maakt het mogelijk om de kaartdienst in te stellen die wordt gebruikt om de kaart te maken.",
+ "maps-par-resizable": "Maakt het mogelijk de afmetingen van de kaart te wijzigen door aan de rechter onderhoek te trekken.",
+ "maps-par-searchmarkers": "Maakt het mogelijk om te zoeken naar specifieke markers via een veld ingebed in de kaart.",
+ "maps-par-zoom": "Het zoomniveau van de kaart. Voor kaarten met markers is dit standaard op de meest ingezoomde niveau waarop nog steeds alle markers zichtbaar zijn.",
+ "maps-par-width": "Maakt het mogelijk om de breedte van de kaart in te stellen. Standaard worden pixels als eenheid gebruikt, maar u kunt expliciet een van deze eenheden opgeven: px, ex, em, %.",
+ "maps-par-height": "Maakt het mogelijk om de hoogte van de kaart in te stellen. Standaard worden pixels als eenheid gebruikt, maar u kunt expliciet een van deze eenheden opgeven: px, ex, em, %.",
+ "maps-par-centre": "De locatie waar de kaart op gecentreerd moet worden",
+ "maps-par-enable-fullscreen": "Knop voor volledig scherm inschakelen",
+ "maps-par-kml": "Op de kaart te laden KML-bestanden.",
+ "maps-par-markercluster": "Laat het samenvoegen van meerdere markers in de buurt in één markering",
+ "maps-googlemaps3-incompatbrowser": "Uw browser kan niet werken met Google Maps v3.",
+ "maps-googlemaps3-par-imageoverlays": "Laat het toevoegen van een afbeelding worden weergegeven op de opgegeven locatie op de kaart.",
+ "maps-googlemaps3-par-type": "Het initieel weer te geven kaarttype.",
+ "maps-googlemaps3-par-types": "De kaarttypen die beschikbaar zijn in via het besturingselement type.",
+ "maps-googlemaps3-par-layers": "Op de kaart te laden speciale lagen.",
+ "maps-googlemaps3-par-controls": "De op de kaart te plaatsen besturingselementen.",
+ "maps-googlemaps3-par-zoomstyle": "De stijl van het besturingselement zoom.",
+ "maps-googlemaps3-par-typestyle": "De stijl van het besturingselement type.",
+ "maps-googlemaps3-par-autoinfowindows": "Automatisch alle informatievensters openen na het laden van een pagina.",
+ "maps-googlemaps3-par-gkml": "KML-bestanden die gehost worden door Google om op de kaart te laden.",
+ "maps-googlemaps3-par-kmlrezoom": "Het zoomniveau van de kaart opnieuw instellen als de KML-lagen geladen zijn.",
+ "maps-googlemaps3-par-poi": "Interessante plaatsen weergeven.",
+ "maps-par-clustermaxzoom": "Het maximale zoomniveau waar clusters kunnen bestaan.",
+ "mapeditor": "Kaarteditor",
+ "specialpages-group-maps": "Kaarten",
+ "mapeditor-parser-error": "Er is een fout opgetreden tijdens het verwerken van metagegevens. De gebruikersinvoer wordt genegeerd.",
+ "mapeditor-none-text": "Geen",
+ "mapeditor-done-button": "Afgerond",
+ "mapeditor-remove-button": "Verwijderen",
+ "mapeditor-import-button2": "Importeren",
+ "mapeditor-export-button": "Exporteren naar wikicode",
+ "mapeditor-import-button": "Importeren vanuit wikicode",
+ "mapeditor-select-button": "Deze polygoon selecteren",
+ "mapeditor-mapparam-button": "Kaartparameters bewerken",
+ "mapeditor-clear-button": "Kaartmarkeringen wissen",
+ "mapeditor-code-title": "Wikicode",
+ "mapeditor-import-title": "Wikicode importeren",
+ "mapeditor-import-note": "Let op: de parser verwacht een strikte opmaak van de wikicode. De hier opgegeven code moet gelijk zijn aan de code die wordt uitgevoerd door de exportfunctie.",
+ "mapeditor-form-title": "Details bewerken",
+ "mapeditor-link-title-switcher-popup-text": "Pop-up met tekst",
+ "mapeditor-link-title-switcher-link-text": "Koppeling",
+ "mapeditor-form-field-title": "Titel",
+ "mapeditor-form-field-text": "Tekst",
+ "mapeditor-form-field-link": "Koppeling",
+ "mapeditor-form-field-icon": "Pictogram",
+ "mapeditor-form-field-group": "Groep",
+ "mapeditor-form-field-inlinelabel": "Inline label",
+ "mapeditor-form-field-strokecolor": "Lijnkleur",
+ "mapeditor-form-field-strokeopacity": "Lijndoorzichtigheid",
+ "mapeditor-form-field-strokeweight": "Lijngewicht",
+ "mapeditor-form-field-fillcolor": "Opvulkleur",
+ "mapeditor-form-field-fillopcaity": "Opvulkleurdoorzichtigheid",
+ "mapeditor-form-field-showonhover": "Alleen weergeven bij erboven zweven",
+ "mapeditor-mapparam-title": "Kaartparameters bewerken",
+ "mapeditor-mapparam-defoption": "- Selecteer een parameter -",
+ "mapeditor-imageoverlay-button": "Afbeeldingslaag toevoegen",
+ "mapeditor-form-field-image": "Afbeelding",
+ "mapeditor-imageoverlay-title": "Gegevens over afbeeldingslaag",
+ "mapeditor-form-field-visitedicon": "Pictogram voor bezocht",
+ "semanticmaps-unrecognizeddistance": "De waarde \"$1\" is geen geldige afstand.",
+ "semanticmaps-kml-link": "KML-bestand bekijken",
+ "semanticmaps-default-kml-pagelink": "Pagina $1 bekijken",
+ "semanticmaps-latitude": "Breedtegraad: $1",
+ "semanticmaps-longitude": "Lengtegraad: $1",
+ "semanticmaps-altitude": "Hoogte: $1",
+ "semanticmaps-forminput-locations": "Locaties",
+ "semanticmaps-par-staticlocations": "Een lijst met aan de kaart toe te voegen locaties samen met de opgegeven gegevens. Zoals bij display_points, kunt u een naam, beschrijving en pictogram per locatie toevoegen door de tilde (\"~\") als scheidingsteken te gebruiken.",
+ "semanticmaps-par-showtitle": "Een naam weergeven in het gegevensvenster van de markering of niet. Dit uitschakelen is vaak handig als er een sjabloon wordt gebruikt om de inhoud van het gegevensvenster vorm te geven.",
+ "semanticmaps-par-hidenamespace": "De naamruimtenaam in het informatievenster van de marker weergeven",
+ "semanticmaps-par-centre": "Het centrum van de kaart. Als deze waarde niet wordt opgegeven, wordt automatisch een keuze gemaakt voor een centrum op basis van alle markeringen op de kaart.",
+ "semanticmaps-par-template": "Een te gebruiken sjabloon om de inhoud van het gegevensvenster op te maken.",
+ "semanticmaps-par-geocodecontrol": "Besturingselement voor geocodering weergeven.",
+ "semanticmaps-par-activeicon": "Pictogram dat wordt weergegeven in plaats van de standaard marker, als de actieve pagina gelijk is aan het zoekresultaat",
+ "semanticmaps-par-pagelabel": "Wanneer dit is ingesteld op \"yes\", krijgen alle markers een \"inlineLabel\" met een koppeling naar de pagina waarop de coördinaten voor de marker staan",
+ "semanticmaps-kml-text": "De tekst die gekoppeld is aan iedere pagina. Als er extra opgegeven eigenschappen zijn, wordt deze tekst daardoor overschreven.",
+ "semanticmaps-kml-title": "De standaard titel voor resultaten",
+ "semanticmaps-kml-linkabsolute": "Moeten koppelingen absoluut zijn (in tegenstelling tot relatief)",
+ "semanticmaps-kml-pagelinktext": "De tekst om te gebruiken voor de koppelingen naar de pagina, waarin $1 vervangen wordt door de paginanaam",
+ "semanticmaps-shapes-improperformat": "Onjuiste opmaak van $1. Raadpleeg de documentatie voor de juiste opmaak",
+ "semanticmaps-shapes-missingshape": "Geen vormen gevonden voor $1. Raadpleeg de documentatie voor beschikbare vormen",
+ "validator-type-mapscircle-list": "Lijst van circels",
+ "validator-type-mapsline-list": "Lijst met lijnen",
+ "validator-type-mapslocation-list": "Lijst met locaties",
+ "validator-type-mapsrectangle-list": "Lijst met rechthoeken",
+ "validator-type-jsonfile": "tekst"
+}
diff --git a/www/wiki/extensions/Maps/i18n/nn.json b/www/wiki/extensions/Maps/i18n/nn.json
new file mode 100644
index 00000000..d2ce8781
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/nn.json
@@ -0,0 +1,20 @@
+{
+ "@metadata": {
+ "authors": [
+ "Harald Khan",
+ "Njardarlogar"
+ ]
+ },
+ "maps_map": "Kart",
+ "maps-abb-north": "N",
+ "maps-abb-east": "A",
+ "maps-abb-south": "S",
+ "maps-abb-west": "V",
+ "maps-latitude": "Breiddegrad:",
+ "maps-longitude": "Lengdegrad:",
+ "maps_coordinates_missing": "Ingen koordinatar vart oppgjevne for kartet.",
+ "maps_unrecognized_coords": "Dei fylgjande koordinatane vart ikkje kjende att: $1.",
+ "maps_map_cannot_be_displayed": "Kartet kan ikkje verta vist.",
+ "maps_click_to_activate": "Trykk for å aktivera kartet",
+ "maps_centred_on": "Kart sentrert på $1, $2."
+}
diff --git a/www/wiki/extensions/Maps/i18n/oc.json b/www/wiki/extensions/Maps/i18n/oc.json
new file mode 100644
index 00000000..5560c7b4
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/oc.json
@@ -0,0 +1,74 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cedric31",
+ "Macofe"
+ ]
+ },
+ "maps-desc": "Permet d'afichar de mapas dinamicas dins las paginas del wiki, d'adreças geoencodadas e d'autras operacions geograficas",
+ "right-geocode": "Geocòde",
+ "maps_map": "Mapa",
+ "maps-loading-map": "Cargament de la mapa...",
+ "maps-load-failed": "Impossible de cargar la mapa !",
+ "maps-markers": "Marcadors",
+ "maps-copycoords-prompt": "CTRL+C, ENTRADA",
+ "maps-searchmarkers-text": "Marcadors de filtre",
+ "maps-others": "autres",
+ "maps-ns-layer": "Jaç",
+ "maps-ns-layer-talk": "Discussion jaç",
+ "maps-layer-property": "Proprietat",
+ "maps-layer-value": "Valor",
+ "maps-layer-errors": "Errors",
+ "validation-error-no-non-numerics": "Lo paramètre « $1 » deu èsser una o mantuna cadenas non numericas.",
+ "validation-error-invalid-locations": "Lo paramètre $1 deu èsser un o mantun emplaçament valid(s).",
+ "validation-error-invalid-width": "Lo paramètre $1 deu èsser una largor valida.",
+ "validation-error-invalid-height": "Lo paramètre $1 deu èsser una nautor valida.",
+ "validation-error-invalid-distance": "Lo paramètre $1 deu èsser una distància valida.",
+ "validation-error-invalid-distances": "Lo paramètre $1 deu èsser una o mantuna distància valida(s).",
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "O",
+ "maps-latitude": "Latitud :",
+ "maps-longitude": "Longitud :",
+ "maps_coordinates_missing": "Cap de coordenada es pas estada provesida pel plan.",
+ "maps_geocoding_failed": "{{PLURAL:$2|L'adreça seguenta a pas pogut èsser geoencodada|Las adreças seguentas an pas pogut èsser geoencodadas}} : $1.\nLo plan pòt pas èsser afichat.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|L'adreça seguenta a pas pogut èsser geoencodada|Las adreças seguentas an pas pogut èsser geoencodadas}} e {{PLURAL:$2|es pas afichada|son pas afichadas}} sul plan : $1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|La coordenada seguenta es pas estada reconeguda|Las coordenadas seguentas son pas estadas reconegudas}} : $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|La coordenada seguenta es pas estada reconeguda|Las coordenadas seguentas son pas estadas reconegudas}} e {{PLURAL:$2|es estada omesa|son estadas omesas}} sus la mapa :\n$1",
+ "maps_map_cannot_be_displayed": "La mapa pòt pas èsser afichada.",
+ "maps_leaflet": "Desplegant",
+ "maps_click_to_activate": "Clicar per activar la mapa",
+ "maps_centred_on": "Mapa centrada sus $1, $2.",
+ "mapeditor": "Editor de mapa",
+ "specialpages-group-maps": "Mapas",
+ "mapeditor-none-text": "Pas cap",
+ "mapeditor-done-button": "Fait",
+ "mapeditor-remove-button": "Suprimir",
+ "mapeditor-import-button2": "Importar",
+ "mapeditor-export-button": "Exportar en còdi wiki",
+ "mapeditor-import-button": "Importar a partir de còdi wiki",
+ "mapeditor-link-title-switcher-link-text": "Ligam",
+ "mapeditor-form-field-title": "Títol",
+ "mapeditor-form-field-text": "Tèxte",
+ "mapeditor-form-field-link": "Ligam",
+ "mapeditor-form-field-icon": "Icòna",
+ "mapeditor-form-field-group": "Grop",
+ "mapeditor-form-field-inlinelabel": "Etiqueta en linha",
+ "mapeditor-form-field-strokecolor": "Color de trait",
+ "mapeditor-form-field-strokeopacity": "Opacitat del trait",
+ "mapeditor-form-field-strokeweight": "Grossor de trait",
+ "mapeditor-form-field-image": "Imatge",
+ "semanticmaps-unrecognizeddistance": "La valor $1 es pas una distància valida",
+ "semanticmaps-kml-link": "Veire lo fichièr KML",
+ "semanticmaps-default-kml-pagelink": "Veire l’article $1",
+ "semanticmaps-latitude": "Latitud : $1",
+ "semanticmaps-longitude": "Longitud : $1",
+ "semanticmaps-altitude": "Altitud : $1",
+ "semanticmaps-forminput-locations": "Emplaçaments",
+ "semanticmaps-par-staticlocations": "Una lista dels endreches d'apondre a la mapa amb las donadas demandadas. Coma amb display_points, podètz apondre un títol, una descripcion e una icòna per emplaçament en utilizant la tilda « ~ » coma separador.",
+ "semanticmaps-par-geocodecontrol": "Afichar lo contraròtle de geoencodatge.",
+ "semanticmaps-kml-title": "Lo títol per defaut pels resultats",
+ "validator-type-mapslocation-list": "Lista dels emplaçaments",
+ "validator-type-mapsrectangle-list": "Lista dels rectangles"
+}
diff --git a/www/wiki/extensions/Maps/i18n/or.json b/www/wiki/extensions/Maps/i18n/or.json
new file mode 100644
index 00000000..defcdd93
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/or.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Odisha1"
+ ]
+ },
+ "maps-others": "ବାକିସବୁ",
+ "maps-ns-layer": "ସ୍ତର",
+ "maps-layer-value": "ମୂଲ୍ୟ"
+}
diff --git a/www/wiki/extensions/Maps/i18n/pa.json b/www/wiki/extensions/Maps/i18n/pa.json
new file mode 100644
index 00000000..cfb243c4
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/pa.json
@@ -0,0 +1,72 @@
+{
+ "@metadata": {
+ "authors": [
+ "Babanwalia"
+ ]
+ },
+ "right-geocode": "ਜੀਓਕੋਡ",
+ "maps_map": "ਨਕਸ਼ਾ",
+ "maps-loading-map": "ਨਕਸ਼ਾ ਲੋਡ ਹੋ ਰਿਹਾ ਹੈ...",
+ "maps-load-failed": "ਨਕਸ਼ਾ ਲੋਡ ਨਾ ਹੋ ਸਕਿਆ!",
+ "maps-markers": "ਮਾਰਕਰ",
+ "maps-copycoords-prompt": "CTRL+C, ਐਂਟਰ",
+ "maps-searchmarkers-text": "ਮਾਰਕਰ ਛਾਣੋ",
+ "maps-others": "ਹੋਰ",
+ "maps-ns-layer": "ਪਰਤ",
+ "maps-ns-layer-talk": "ਤਹਿਨੁਮਾ ਗੱਲਬਾਤ",
+ "maps-layer-property": "ਮਲਕੀਅਤ",
+ "maps-layer-value": "ਮੁੱਲ",
+ "maps-layer-errors": "ਦੋਸ਼",
+ "validation-error-invalid-layer": "ਮਾਪਦੰਡ $1 ਇੱਕ ਸਹੀ ਪਰਤ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ।",
+ "maps-layer-of-type": "$1 ਕਿਸਮ ਦੀ ਪਰਤ",
+ "maps-distance-par-unit": "ਵਿੱਥ ਵਿਖਾਉਣ ਲਈ ਇਕਾਈ",
+ "maps-finddestination-par-location": "ਮੁਢਲੀ ਸਥਿਤੀ",
+ "maps-finddestination-par-bearing": "ਮੁਢਲੀ ਦਿਸ਼ਾ",
+ "maps-finddestination-par-distance": "ਗਾਹੁਣ ਲਈ ਵਿੱਥ",
+ "maps-geocode-par-location": "ਉਹ ਪਤਾ ਜਿਹਨੂੰ ਤੁਸੀਂ ਜੀਓਕੋਡ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ",
+ "maps-geodistance-par-unit": "ਵਿੱਥ ਵਿਖਾਉਣ ਲਈ ਇਕਾਈ",
+ "maps-displaymap-par-circles": "ਵਿਖਾਉਣ ਲਈ ਚੱਕਰ",
+ "maps-displaymap-par-lines": "ਵਿਖਾਉਣ ਲਈ ਰੇਖਾਵਾਂ",
+ "maps-displaymap-par-maxzoom": "ਵੱਧ ਤੋਂ ਵੱਧ ਜ਼ੂਮ ਪੱਧਰ",
+ "maps-displaymap-par-minzoom": "ਘੱਟੋ ਘੱਟ ਜ਼ੂਮ ਪੱਧਰ",
+ "maps-displaymap-par-polygons": "ਵਿਖਾਉਣ ਲਈ ਬਹੁਭੁਜ",
+ "maps-displaymap-par-rectangles": "ਵਿਖਾਉਣ ਲਈ ਚਤੁਰਭੁਜ",
+ "maps-abb-north": "ਉ",
+ "maps-abb-east": "ਪੂ",
+ "maps-abb-south": "ਦ",
+ "maps-abb-west": "ਪ",
+ "maps-latitude": "ਅਕਸ਼ਾਂਸ਼:",
+ "maps-longitude": "ਰੇਖਾਂਸ਼:",
+ "maps_coordinates_missing": "ਇਸ ਨਕਸ਼ੇ ਲਈ ਕੋਈ ਗੁਣਕ ਨਹੀਂ ਹਨ।",
+ "maps_map_cannot_be_displayed": "ਇਹ ਨਕਸ਼ਾ ਵਿਖਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ।",
+ "maps_click_to_activate": "ਨਕਸ਼ਾ ਕਿਰਿਆਸ਼ੀਲ ਬਣਾਉਣ ਲਈ ਕਲਿੱਕ ਕਰੋ",
+ "maps_centred_on": "$1, $2 ਉੱਤੇ ਕੇਂਦਰਤ ਨਕਸ਼ਾ।",
+ "maps-par-centre": "ਉਹ ਥਾਂ ਜਿਸ ਉੱਤੇ ਨਕਸ਼ਾ ਕੇਂਦਰਤ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ",
+ "maps-googlemaps3-incompatbrowser": "ਤੁਹਾਡਾ ਬਰਾਊਜ਼ਰ Google Maps v3 ਨਾਲ਼ ਕੰਮ ਕਰਨ ਲਈ ਅਨੁਕੂਲ ਨਹੀਂ ਹੈ।",
+ "maps-googlemaps3-par-layers": "ਨਕਸ਼ੇ ਉੱਤੇ ਲੋਡ ਕਰਨ ਲਈ ਖ਼ਾਸ ਪਰਤਾਂ।",
+ "mapeditor": "ਨਕਸ਼ਾ ਸੰਪਾਦਕ",
+ "specialpages-group-maps": "ਨਕਸ਼ੇ",
+ "mapeditor-none-text": "ਕੋਈ ਨਹੀਂ",
+ "mapeditor-done-button": "ਹੋ ਗਿਆ",
+ "mapeditor-remove-button": "ਹਟਾਓ",
+ "mapeditor-import-button2": "ਮੰਗਾਓ",
+ "mapeditor-export-button": "ਵਿਕੀ ਕੋਡ ਨੂੰ ਭੇਜੋ",
+ "mapeditor-import-button": "ਵਿਕੀ ਕੋਡ ਤੋਂ ਮੰਗਾਓ",
+ "mapeditor-select-button": "ਇਹ ਬਹੁਭੁਜ ਚੁਣੋ",
+ "mapeditor-mapparam-button": "ਨਕਸ਼ੇ ਦੇ ਮਾਪਦੰਡ ਸੋਧੋ",
+ "mapeditor-clear-button": "ਨਕਸ਼ਾ ਸਾਫ਼ ਕਰੋ",
+ "mapeditor-code-title": "ਵਿਕੀ ਕੋਡ",
+ "mapeditor-import-title": "ਵਿਕੀ ਕੋਡ ਮੰਗਾਓ",
+ "mapeditor-form-title": "ਸੋਧ ਦਾ ਬਿਉਰਾ",
+ "mapeditor-link-title-switcher-link-text": "ਕੜੀ",
+ "mapeditor-form-field-title": "ਸਿਰਲੇਖ",
+ "mapeditor-form-field-text": "ਲਿਖਤ",
+ "mapeditor-form-field-link": "ਕੜੀ",
+ "mapeditor-form-field-icon": "ਆਇਕਨ",
+ "mapeditor-form-field-group": "ਟੋਲੀ",
+ "mapeditor-form-field-fillcolor": "ਰੰਗ ਭਰੋ",
+ "mapeditor-form-field-showonhover": "ਸਿਰਫ਼ ਉੱਤੇ ਮੰਡਰਾਉਣ 'ਤੇ ਵਿਖਾਓ",
+ "mapeditor-mapparam-title": "ਨਕਸ਼ੇ ਦੇ ਮਾਪਦੰਡ ਸੋਧੋ",
+ "mapeditor-mapparam-defoption": "-ਮਾਪਦੰਡ ਚੁਣੋ-",
+ "mapeditor-form-field-image": "ਤਸਵੀਰ"
+}
diff --git a/www/wiki/extensions/Maps/i18n/pdc.json b/www/wiki/extensions/Maps/i18n/pdc.json
new file mode 100644
index 00000000..3c5335d5
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/pdc.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Xqt"
+ ]
+ },
+ "maps-abb-north": "N",
+ "maps-abb-east": "O",
+ "maps-abb-south": "S",
+ "maps-abb-west": "W"
+}
diff --git a/www/wiki/extensions/Maps/i18n/pfl.json b/www/wiki/extensions/Maps/i18n/pfl.json
new file mode 100644
index 00000000..dd936687
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/pfl.json
@@ -0,0 +1,53 @@
+{
+ "@metadata": {
+ "authors": [
+ "Manuae"
+ ]
+ },
+ "maps_map": "Kaad",
+ "maps-loading-map": "Die Kaad werd glaade …",
+ "maps-load-failed": "Die Kaad hod net glaade were kenne.",
+ "maps-markers": "Kennzaischnunge",
+ "maps-copycoords-prompt": "STRG+C, ENTER",
+ "maps-others": "onnare",
+ "maps-ns-layer": "Ewene",
+ "maps-layer-property": "Aischeschafd",
+ "maps-layer-value": "Werd",
+ "maps-error-invalid-layertype": "S'hod kä Ewene vunde Ard „$1“. Bloß {{PLURAL:$3|die Ard werd|die Arde weren}} unaschdidzd: $2",
+ "maps-error-no-layertype": "Du mugschd die Ard vunde Ewene ogewe. Bloß {{PLURAL:$2|die Ard werd|die Arde weren}} unaschdidzd: $1",
+ "validation-error-invalid-layer": "Parameter $1 muss ä gildische Ewene soi.",
+ "validation-error-invalid-layers": "Parameter $1 muss minischdns ä gildische Ewene soi.",
+ "maps-layer-of-type": "Ewene vunde Ard $1",
+ "maps-layer-type-supported-by": "Die Ard vun Ewene konn {{PLURAL:$2|bloß baim Kaadeweese $1 bnudzd were|bai dene Kaadeweese bnudzd were: $1}}.",
+ "maps-distance-description": "Nemmä unaschdidzdi Greeß un wondl die Dischdonz inän enschbrechende, awa onare Werd um.",
+ "maps-finddestination-par-location": "De Ausgongspungd",
+ "maps-finddestination-par-bearing": "Die Ausgongspailung",
+ "maps-finddestination-par-distance": "Wie waid weg bischd.",
+ "validation-error-invalid-location": "Parameter $1 mussn gildische Schdondord soi.",
+ "validation-error-invalid-locations": "Parameter $1 muss minischdns än gildische Schdondord soi.",
+ "validation-error-invalid-width": "Parameter $1 muss ä gildischi Braid soi.",
+ "validation-error-invalid-height": "Parameter $1 muss ä gildische Hee soi.",
+ "validation-error-invalid-distance": "Parameter $1 muss ä gildischi Dischdonz soi.",
+ "validation-error-invalid-distances": "Parameter $1 muss minischdns ä gildischi Dischdonz soi.",
+ "validation-error-invalid-image": "Parameter $1 muss ä gildisches Bild soi.",
+ "validation-error-invalid-images": "Parameter $1 muss minischdns ä gildisches Bild soi.",
+ "validation-error-invalid-goverlay": "Parameter $1 mussn gildischi Iwalachachung soi.",
+ "validation-error-invalid-goverlays": "Parameter $1 muss minischdns ä gildischi Iwalachachung soi.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "O",
+ "maps-abb-south": "S",
+ "maps-abb-west": "W",
+ "maps-latitude": "Braidegrad:",
+ "maps-longitude": "Längegrad:",
+ "maps-invalid-coordinates": "De Werd $1 isch kä gildisches Paa vun Koordinaade.",
+ "maps_coordinates_missing": "S'hod kä Koordinaade uff de Kaad.",
+ "maps_geocoding_failed": "Die {{PLURAL:$2|folschnd Adress hod|folschnde Adresse hawen}} ned umgwondld were kenne: $1.",
+ "maps_geocoding_failed_for": "Die {{PLURAL:$2||folschnd Adress hod|folschnde Adresse hawen}} ned gfunne were kenne un {{PLURAL:$2|isch|sin}} uff de Kaad ned druff:\n$1",
+ "maps_unrecognized_coords": "Die {{PLURAL:$2|folschnd Koordinaad|folschnde Koordinaade}} hodma ned erkennd: $1.",
+ "maps_unrecognized_coords_for": "Die {{PLURAL:$2|folschnd Koordinaad|folschnde Koordinaade}} hodma ned erkennd un {{PLURAL:$2|isch|sin}} uff de Kaad ned druff:\n$1",
+ "maps_map_cannot_be_displayed": "Die Kaad komma ned zaische.",
+ "maps_click_to_activate": "Um die Kaad oazuschalde, mugschd druffdrigge",
+ "maps_centred_on": "Die Kaad isch uff $1, $2 inde Midd.",
+ "maps-googlemaps3-par-kmlrezoom": "Nomol die Kaad vagreeßere, wonn die KML-Ewene druff glaade worre sin.",
+ "maps-osm-par-photos": "Fodografieje zaische"
+}
diff --git a/www/wiki/extensions/Maps/i18n/pl.json b/www/wiki/extensions/Maps/i18n/pl.json
new file mode 100644
index 00000000..bb71af4d
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/pl.json
@@ -0,0 +1,170 @@
+{
+ "@metadata": {
+ "authors": [
+ "BeginaFelicysym",
+ "Chrumps",
+ "Sp5uhe",
+ "Ty221",
+ "Yarl",
+ "Alan ffm",
+ "Macofe",
+ "Deejay1",
+ "Railfail536"
+ ]
+ },
+ "maps-desc": "Umożliwia zamieszczanie na stronach wiki map dynamicznych, geokodowanych adresów i innych danych geograficznych",
+ "right-geocode": "Geokodowanie",
+ "action-geocode": "geokodowania na tej wiki",
+ "maps_map": "Mapa",
+ "maps-tracking-category": "Strony z mapami renderowanymi przy użyciu rozszerzenia map",
+ "maps-loading-map": "Wczytywanie mapy…",
+ "maps-load-failed": "Nie można załadować mapy!",
+ "maps-markers": "Zaznaczenia",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-others": "inne",
+ "maps-kml-parsing-failed": "Błąd podczas parsowania jednego lub więcej plików KML, najczęściej jest to wynik błędnego XML",
+ "maps-ns-layer": "Warstwa",
+ "maps-ns-layer-talk": "Dyskusja warstwy",
+ "maps-layer-property": "Własność",
+ "maps-layer-value": "Wartość",
+ "maps-layer-errors": "Błędy",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Nieprawidłowa definicja|Nieprawidłowe definicje}}",
+ "maps-layerpage-usage": "Strony z mapami z warstwą „$1”",
+ "maps-layerpage-nousage": "Nie ma stron, które używają w tej chwili tej warstwy.",
+ "maps-error-invalid-layertype": "Brak warstw typu „$1”. {{PLURAL:$3|Wspierany jest wyłącznie typ|Wspierane są wyłącznie typy:}} $2",
+ "maps-error-no-layertype": "Musisz określić typ warstwy. {{PLURAL:$2|Wspierany jest wyłącznie typ|Wspierane są wyłącznie typy:}} $1",
+ "validation-error-invalid-layer": "Parametr $1 musi określać prawidłową warstwę.",
+ "validation-error-invalid-layers": "Parametr $1 musi wskazywać jedną lub więcej prawidłowych warstw.",
+ "validation-error-no-non-numeric": "Parametr „$1” musi być ciągiem nienumerycznym.",
+ "maps-layer-of-type": "Warstwa typu $1",
+ "maps-layer-of-type-and-name": "Warstwa „$2” typu „$1”",
+ "maps-layer-type-supported-by": "Tego typu warstwa może być używana wyłącznie z {{PLURAL:$2|serwisem map|serwisami map:}} $1.",
+ "maps-coordinates-description": "Przechwycenie analizatora do formatowania współrzędnych z i na dowolny z obsługiwanych formatów.",
+ "maps-displaymap-description": "Wyświetlanie map geograficznych bez żadnych naniesionych na nich znaczników na wiki.",
+ "maps-distance-description": "Konwertuj odległości za pomocą pewnych obsługiwanych jednostek do ich odpowiedników w innych jednostkach.",
+ "maps-finddestination-description": "Znajdź drogę do celu z podanego punktu początkowego, (który może być w dowolnym z obsługiwanych formatów), początkowy namiar i odległości.",
+ "maps-geocode-description": "Umożliwia geokodowanie adresów, innymi słowy, przekształcenie zapisu miejsca czytelnego dla ludzi w zbiory współrzędnych. Obsługiwane jest kilka usług geokodowania, których nie należy mylić z usługami dostarczania map.",
+ "maps-geodistance-description": "Oblicz odległość geograficzną między dwoma punktami wskazanymi w jednym z obsługiwanych formatów.",
+ "maps-mapsdoc-description": "Wyświetl tabelę parametrów dla określonej usługi dostarczania map wraz z ich wartościami domyślnymi i opisami.",
+ "maps-mapsdoc-par-service": "Usługa dostarczania map do wyświetlenia dokumentacji parametru.",
+ "maps-mapsdoc-par-language": "Język wyświetlania dokumentacji. Jeśli takie tłumaczenie nie jest dostępne, zostanie użyty angielski.",
+ "maps-coordinates-par-location": "Współrzędne, które chcesz sformatować.",
+ "maps-coordinates-par-format": "Format docelowy dla współrzędnych.",
+ "maps-coordinates-par-directional": "Wskazuje, czy współrzędne powinny być wyświetlone kierunkowo czy nie.",
+ "maps-distance-par-distance": "Odległość do konwersji na odpowiednik w określonych jednostkach.",
+ "maps-distance-par-decimals": "Maksymalna liczba cyfr części ułamkowej w wartości wynikowej.",
+ "maps-distance-par-unit": "Jednostka wynikowa odległości.",
+ "maps-finddestination-par-location": "Położenie początkowe.",
+ "maps-finddestination-par-bearing": "Kierunek początkowy.",
+ "maps-finddestination-par-distance": "Odległość podróży.",
+ "maps-geocode-par-location": "Adres, który chcesz geokodować.",
+ "maps-geocode-par-format": "Format wynikowych współrzędnych.",
+ "maps-geodistance-par-unit": "Jednostka wynikowa odległości.",
+ "maps-geodistance-par-decimals": "Maksymalna liczba cyfr części ułamkowej w wartości wynikowej.",
+ "maps-displaymap-par-coordinates": "Jedna lub więcej lokalizacji do wyświetlenia na mapie. Zostaną oznaczone znacznikiem.",
+ "maps-displaymap-par-circles": "Okręgi do wyświetlenia",
+ "maps-displaymap-par-lines": "Linie do wyświetlenia",
+ "maps-displaymap-par-maxzoom": "Maksymalny poziom powiększenia",
+ "maps-displaymap-par-minzoom": "Minimalny poziom powiększenia",
+ "maps-displaymap-par-polygons": "Wielokąty do wyświetlenia",
+ "maps-displaymap-par-rectangles": "Prostokąty do wyświetlenia",
+ "maps-displaymap-par-static": "Uczyń mapę statyczną",
+ "maps-displaymap-par-wmsoverlay": "Użyj nakładki WMS",
+ "maps-displaymap-par-geojson": "Adres URL pliku lub nazwa strony zawierającej dane GeoJSON",
+ "maps-fullscreen-button": "Przełącz na tryb pełnoekranowy",
+ "maps-fullscreen-button-tooltip": "Zobacz mapę jako pełnoekranową lub osadzoną.",
+ "validation-error-invalid-location": "Parametr $1 musi wskazywać prawidłową lokalizację.",
+ "validation-error-invalid-locations": "Parametr $1 musi wskazywać jedną lub więcej prawidłowych lokalizacji.",
+ "validation-error-invalid-width": "Parametr $1 musi określać prawidłową szerokość.",
+ "validation-error-invalid-height": "Parametr $1 musi określać prawidłową wysokość.",
+ "validation-error-invalid-distance": "Parametr $1 musi określać prawidłową odległość.",
+ "validation-error-invalid-distances": "Parametr $1 musi określać jedną lub więcej prawidłowych odległości.",
+ "validation-error-invalid-image": "Parametr $1 musi określać prawidłową grafikę.",
+ "validation-error-invalid-images": "Parametr $1 musi wskazywać jedną lub więcej prawidłowych grafik.",
+ "validation-error-invalid-goverlay": "Parametr $1 musi być prawidłową nakładką.",
+ "validation-error-invalid-goverlays": "Parametr $1 musi być jedną lub więcej prawidłową nakładką.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "W",
+ "maps-latitude": "Szerokość geograficzna",
+ "maps-longitude": "Długość geograficzna",
+ "maps-invalid-coordinates": "Wartość $1 nie została rozpoznana jako prawidłowe współrzędne.",
+ "maps_coordinates_missing": "Brak współrzędnych dla mapy.",
+ "maps_geocoding_failed": "{{PLURAL:$2|Następującego adresu nie można odnaleźć na mapie|Następujących adresów nie można odnaleźć na mapie:}} $1.\nMapa nie może zostać wyświetlona.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|Następujący adres został pominięty, ponieważ nie można go odnaleźć na mapie|Następujące adresy zostały pominięte, ponieważ nie można ich odnaleźć na mapie:}} $1.",
+ "maps_unrecognized_coords": "{{PLURAL:$2|Następująca współrzędna nie została rozpoznana –|Następujące współrzędne nie zostały rozpoznane:}} $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|Następującą współrzędną|Następujące współrzędne}} pominięto, ponieważ nie {{PLURAL:$2|została rozpoznana|zostały rozpoznane}}:\n$1",
+ "maps_map_cannot_be_displayed": "Mapa nie może zostać wyświetlona.",
+ "maps-geocoder-not-available": "Funkcja geokodowania map nie jest dostępna. Lokalizacja nie może zostać zakodowana.",
+ "maps-leaflet-par-defzoom": "Umożliwia ustawienie domyślnego poziomu powiększenia mapy.",
+ "maps-leaflet-par-layers": "Warstwa, która będzie wyświetlana podczas ładowania mapy.",
+ "maps-leaflet-par-overlaylayers": "Nakładane warstwy, które będą wyświetlane podczas ładowania mapy.",
+ "maps_click_to_activate": "Kliknij, aby aktywować mapę",
+ "maps_centred_on": "Środek mapy – $1, $2.",
+ "maps-par-resizable": "Zmienia rozmiar mapy przez przeciąganie prawego dolnego rogu.",
+ "maps-par-centre": "Lokalizacja, w której mapa powinna być wyśrodkowana",
+ "maps-par-enable-fullscreen": "Włącz przycisk trybu pełnoekranowego",
+ "maps-par-kml": "Pliki KML do załadowania na mapie.",
+ "maps-googlemaps3-incompatbrowser": "Twoja przeglądarka nie jest zgodna z Google Maps v3.",
+ "maps-googlemaps3-par-type": "Typ mapy do wyświetlenia na początku.",
+ "maps-googlemaps3-par-types": "Typy map, które będą dostępne za pośrednictwem formantu typu.",
+ "maps-googlemaps3-par-layers": "Specjalne warstwy do załadowania na mapie.",
+ "maps-googlemaps3-par-controls": "Formanty do umieszczenia na mapie.",
+ "maps-googlemaps3-par-zoomstyle": "Styl formantu powiększenia.",
+ "maps-googlemaps3-par-typestyle": "Styl formantu typu.",
+ "maps-googlemaps3-par-autoinfowindows": "Automatycznie otwórz wszystkie okna informacyjne po załadowaniu strony.",
+ "maps-googlemaps3-par-gkml": "Pliki KML udostępniane przez serwery Google do załadowania na mapie.",
+ "maps-googlemaps3-par-poi": "Pokaż atrakcje.",
+ "maps-googlemaps3-par-clustergridsize": "Rozmiar siatki klastra w pikselach.",
+ "maps-par-clustermaxzoom": "Maksymalny poziom powiększenia, w którym klaster może istnieć.",
+ "maps-par-maxclusterradius": "Maksymalny promień, który obejmuje klaster.",
+ "mapeditor": "Edytor map",
+ "specialpages-group-maps": "Mapy",
+ "mapeditor-none-text": "Brak",
+ "mapeditor-done-button": "Gotowe",
+ "mapeditor-remove-button": "Usuń",
+ "mapeditor-import-button2": "Importuj",
+ "mapeditor-export-button": "Eksport do kodu wiki",
+ "mapeditor-import-button": "Import z kodu wiki",
+ "mapeditor-select-button": "Wybierz ten wielokąt",
+ "mapeditor-mapparam-button": "Edytuj parametry mapy",
+ "mapeditor-clear-button": "Wyczyść mapę",
+ "mapeditor-code-title": "Kod wiki",
+ "mapeditor-import-title": "Import kodu wiki",
+ "mapeditor-import-note": "Zwróć uwagę że parser oczekuje kodu wiki w bardzo określonym formacie. Umieszczone tutaj źródło powinno odpowiadać kodowy wygenerowanemu przez narzędzie do eksportu mapy.",
+ "mapeditor-form-title": "Edytuj szczegóły",
+ "mapeditor-link-title-switcher-link-text": "Link",
+ "mapeditor-form-field-title": "Tytuł",
+ "mapeditor-form-field-text": "Tekst",
+ "mapeditor-form-field-link": "Link",
+ "mapeditor-form-field-icon": "Ikona",
+ "mapeditor-form-field-group": "Grupa",
+ "mapeditor-form-field-fillcolor": "Kolor wypełnienia",
+ "mapeditor-form-field-showonhover": "Pokaż tylko po najechaniu",
+ "mapeditor-mapparam-title": "Edytuj ustawienia mapy",
+ "mapeditor-mapparam-defoption": "-Wybierz parametr-",
+ "mapeditor-form-field-image": "Grafika",
+ "semanticmaps-unrecognizeddistance": "Wartość $1 nie jest poprawną odległością.",
+ "semanticmaps-kml-link": "Wyświetla plik KML",
+ "semanticmaps-default-kml-pagelink": "Pokaż stronę $1",
+ "semanticmaps-latitude": "Szerokość geograficzna: $1",
+ "semanticmaps-longitude": "Długość geograficzna: $1",
+ "semanticmaps-altitude": "Wysokość: $1",
+ "semanticmaps-forminput-locations": "Miejsca",
+ "semanticmaps-par-geocodecontrol": "Pokaż kontrolę geokodowania.",
+ "semanticmaps-par-ajaxcoordproperty": "Nazwa właściwości współrzędnych, która jest używana do tworzenia zapytania AJAX.",
+ "semanticmaps-kml-title": "Domyślny tytuł wyników",
+ "validator-type-mapscircle": "Koło geograficzne",
+ "validator-type-mapscircle-list": "Lista kół",
+ "validator-type-mapsimageoverlay": "Nakładanie obrazu",
+ "validator-type-mapsline": "Linia geograficzna",
+ "validator-type-mapsline-list": "Lista linii",
+ "validator-type-mapslocation": "Położenie geograficzne",
+ "validator-type-mapslocation-list": "Lista lokalizacji",
+ "validator-type-mapsrectangle": "Prostokąt geograficzny",
+ "validator-type-mapsrectangle-list": "Lista prostokątów",
+ "validator-type-mapspolygon": "Wielokąt geograficzny",
+ "validator-type-mapspolygon-list": "Lista wielokątów geograficznych",
+ "validator-type-jsonfile": "tekst"
+}
diff --git a/www/wiki/extensions/Maps/i18n/pms.json b/www/wiki/extensions/Maps/i18n/pms.json
new file mode 100644
index 00000000..5e8b86b2
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/pms.json
@@ -0,0 +1,174 @@
+{
+ "@metadata": {
+ "authors": [
+ "Borichèt",
+ "Dragonòt",
+ "McDutchie",
+ "පසිඳු කාවින්ද",
+ "Macofe"
+ ]
+ },
+ "maps-desc": "A abìlita l'anseriment dle carte dinàmiche ant le pàgine ëd la wiki, dj'adrësse geocodificà e d'àutre operassion geogràfiche",
+ "right-geocode": "Geocodìfica",
+ "maps_map": "Pian",
+ "maps-loading-map": "Cariament ëd la carta...",
+ "maps-load-failed": "As peul pa cariesse la carta!",
+ "maps-markers": "Marcador",
+ "maps-copycoords-prompt": "CTRL+C, A CAP",
+ "maps-searchmarkers-text": "Marcador ëd filtr",
+ "maps-others": "àutri",
+ "maps-ns-layer": "Livel",
+ "maps-ns-layer-talk": "Ciaciarada ëd livel",
+ "maps-layer-property": "Propietà",
+ "maps-layer-value": "Valor",
+ "maps-layer-errors": "Eror",
+ "maps-error-invalid-layertype": "A-i é pa gnun livej ëd sòrt \"$1\". A {{PLURAL:$3|l'é mach mantùa costa|a son mach mantnùe coste}} sòrt: $2",
+ "maps-error-no-layertype": "A dev specifiché la sòrt ëd livel. Mach {{PLURAL:$2|sta sòrt a l'é mantnùa|ste sòrt a son mantnùe}}: $1",
+ "validation-error-invalid-layer": "Ël paràmetr $1 a dev esse un livel bon.",
+ "validation-error-invalid-layers": "Ël paràmetr $1 a dev esse un o pi livej bon.",
+ "maps-layer-of-type": "Livel ëd sòrt $1",
+ "maps-layer-type-supported-by": "Costa sòrt ëd livel a peul {{PLURAL:$2|mach esse dovrà con ël servissi ëd cartografìa $1|esse dovrà con sti servissi ëd cartografìa: $1}}.",
+ "maps-coordinates-description": "Gancio ëd l'analisator për formaté le coordinà, da e vers qualsëssìa dij formà mantnù.",
+ "maps-displaymap-description": "Visualisé le carte geogràfiche sensa gnun marcador definì ëd wiki ansima a lor.",
+ "maps-distance-description": "Convertì na distansa dovrand na serta unità mantnùa an sò equivalent dovrand n'àutra unità.",
+ "maps-finddestination-description": "Trové na destinassion dàit un pont ëd partensa (che a peul esse an qualsëssìa dij formà mantnù), n'orientassion inissial e na distansa.",
+ "maps-geocode-description": "A abìlita la geocodìfica d'adrësse, an d'àutre paròle, la trasformassion dle posission lesìbij da n'uman an ansema ëd coordinà. Vàire sërvissi ëd geocodìfica a son mantnù, lòn che a dev nen esse confondù con ij sërvissi ëd cartografìa.",
+ "maps-geodistance-description": "Calcolé la distansa geogràfica tra doi pont, da e vers qualsëssìa dij formà mantnù.",
+ "maps-mapsdoc-description": "Visualisé na tàula con ij paràmetr për un servissi specìfich ëd cartografìa ansema con ij sò valor predefinì e soe descrission.",
+ "maps-mapsdoc-par-service": "Ël sërvissi ëd cartografìaa për mostré la documentassion dij paràmetr.",
+ "maps-mapsdoc-par-language": "La lenga ant la qual mostré la documentassion. Se gnun-a tradussion a l'é disponìbil, al pòst a sarà dovrà l'Anglèis.",
+ "maps-coordinates-par-location": "Le coordinà ch'it veule formaté.",
+ "maps-coordinates-par-format": "Ël formà destinassion për le coordinà.",
+ "maps-coordinates-par-directional": "A ìndica se le coordinà a devo esse stampà diressionaj o nò.",
+ "maps-distance-par-distance": "La distansa da convertì a sò equivalent con n'unità specificà.",
+ "maps-distance-par-decimals": "Ël nùmer màssim ëd gifre frassionaj da dovré ant ël valor arzultant.",
+ "maps-distance-par-unit": "L'unità pr'ëstampé la distansa.",
+ "maps-finddestination-par-location": "La locassion inissial.",
+ "maps-finddestination-par-bearing": "La posission inissial.",
+ "maps-finddestination-par-distance": "La distansa da viagé.",
+ "maps-finddestination-par-format": "Ël formà ant ël qual ësmon-e la destinassion.",
+ "maps-finddestination-par-directional": "A ìndica se ël formà dla destinassion a dev esse diressional o pa.",
+ "maps-geocode-par-location": "L'adrëssa ch'it veule geocodifiché.",
+ "maps-geocode-par-format": "Ël formà për le coordinà arzultante.",
+ "maps-geocode-par-directional": "A ìndica se le coordinà a devo esse stampà diressionaj o nò.",
+ "maps-geodistance-par-location1": "Ël prim pont ant l'ansema për calcolé la distansa.",
+ "maps-geodistance-par-location2": "Lë scond pont ant l'ansema për calcolé la distansa.",
+ "maps-geodistance-par-unit": "L'unità da dovré për mostré la distansa.",
+ "maps-geodistance-par-decimals": "Ël nùmer màssim ëd gifre frassionaj da dovré ant ël valor arzultant.",
+ "maps-displaymap-par-mappingservice": "A përmët d'amposté ël sërvissi ëd cartografìa che a sarà dovrà për generé la carta.",
+ "maps-displaymap-par-coordinates": "Un-a o pi locassion da smon-e an sla carta. A saran mostrà con na marca.",
+ "maps-displaymap-par-visitedicon": "L'element local ëd n'archivi da dovré com plancia visità",
+ "maps-displaymap-par-zoom": "A përmët d'amposté ël livel d'angrandiment ëd la carta.\nQuand nen dàit e pi marche a son presente an sla carta, a sarà dovrà ël mej angrandiment, pa la configurassion predefinìa.",
+ "maps-displaymap-par-centre": "A përmët d'amposté le coordinà dël sénter ëd la carta për display_point(s).\nA aceta sia adrësse che coordinà.\nQuand sta propietà a l'é pa fornìa, la carta as sentrerà an sla marca dàita, o tra le marche dàite.",
+ "maps-displaymap-par-title": "A përmët d'amposté dël test che a sarà smonì ant le fnestre ch'as duverto ëd tute le marche che a l'han pa un tìtol specificà.\nQuand a l'é dovrà con la tichëtta, ël tìtol a sarà an grassèt e sotlinià.",
+ "maps-displaymap-par-label": "A përmët d'amposté dël test che a sarà smonù ant le fnestre ch'as duverto ëd tute le marche che a l'han pa na tichëtta spessìfica.",
+ "maps-displaymap-par-icon": "A përmët d'amposté la plancia dovrà për tuti ij marcador.",
+ "maps-displaymap-par-circles": "Sercc da mostré",
+ "maps-displaymap-par-copycoords": "Se na casela ëd diàlogh anté che le coordinà ëd na locassion a devo esse smonùe quand a së sgnaca su quaicòs",
+ "maps-displaymap-par-lines": "Linie da smon-e",
+ "maps-displaymap-par-maxzoom": "Ël livel d'angrandiment màssim",
+ "maps-displaymap-par-minzoom": "Ël livel d'angrandiment mìnim",
+ "maps-displaymap-par-polygons": "Polìgon da smon-e",
+ "maps-displaymap-par-rectangles": "Retàngoj da smon-e",
+ "maps-displaymap-par-static": "Se la carta a dev esse stàtica",
+ "maps-displaymap-par-wmsoverlay": "Dovré na covertura WMS",
+ "validation-error-invalid-location": "Ël paràmetr $1 a dev esse na locassion bon-a.",
+ "validation-error-invalid-locations": "Ël paràmetr $1 a dev esse un-a o pi locassion bon-e.",
+ "validation-error-invalid-width": "Ël paràmetr $1 a dev esse na larghëssa bon-a.",
+ "validation-error-invalid-height": "Ël paràmetr $1 a dev esse n'autëssa bon-a.",
+ "validation-error-invalid-distance": "Ël valor $1 a deuv esse na distansa bon-a.",
+ "validation-error-invalid-distances": "Ël paràmetr $1 a dev esse un-a o pi distanse bon-e.",
+ "validation-error-invalid-image": "Ël paràmetr $1 a dev esse na figura bon-a.",
+ "validation-error-invalid-images": "Ël paràmetr $1 a dev esse un-a o pi figure bon-e.",
+ "validation-error-invalid-goverlay": "Ël paràmetr $1 a dev esse un coatament bon.",
+ "validation-error-invalid-goverlays": "Ël paràmetr $1 a dev esse un o pi coatament bon.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "W",
+ "maps-latitude": "Latitùdin:",
+ "maps-longitude": "Longitùdin:",
+ "maps-invalid-coordinates": "Ël valor $1 a l'é pa stàit arconossù con n'ansema bon ëd coordinà.",
+ "maps_coordinates_missing": "Pa gnun-e coordinà dàite për la mapa.",
+ "maps_geocoding_failed": "{{PLURAL:$2|L'adrëssa|J'adrësse}} sì sota a peulo pa esse sota geocode: $1.\nLa mapa a peul pa esse visualisà.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|L'adrëssa|J'adrësse}} sì sota a peula pa esse sota geocode e a {{PLURAL:$2|l'é pa stàita|son pa stàite}} butà ant la mapa: $1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|La coordinà sota a l'é pa stàita arconossùa|Le coordinà sota a son pa stàite arconossùe}}: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|La coordinatà sota a l'é pa stàita arconossùa|Le coordinà sota a son pa stàite arconossùe}} e a {{PLURAL:$2|l'é stàita|a son stàite}} pa butà ant la carta:\n$1",
+ "maps_map_cannot_be_displayed": "La carta a peul pa esse mostrà.",
+ "maps-geocoder-not-available": "La possibilità ëd geocodìfica dle carte a l'é pa disponìbil. Soa locassion a peul pa esse geocodificà.",
+ "maps_click_to_activate": "Sgnaca për ativé la carta",
+ "maps_centred_on": "Carta sentrà su $1, $2.",
+ "maps-par-mappingservice": "A përmët d'amposté ël sërvissi ëd cartografìa che a sarà dovrà për generé la carta.",
+ "maps-par-resizable": "A rend la carta ridimensionàbil an spostand sò canton an bass a drita.",
+ "maps-par-zoom": "Ël livel d'angrandiment për la carta. Për le carte con dij marcador cost-sì as predefinirà al livel pi agrandì che a smon anco' tuti ij marcador.",
+ "maps-par-width": "A përmët d'amposté la larghëssa dla carta. Për predefinission ij pontin a saran contà com unità, ma a peul specifiché ëd fasson esplìssita un-a ëd coste unità: px, ex, em, %.",
+ "maps-par-height": "A përmët d'amposté l'autëssa dla carta. Për predefinission ij pontin a saran considerà com unità, ma a peul ëspessifiché ëd fasson esplìssita un-a ëd coste unità: px, ex, em, %.",
+ "maps-par-centre": "Ël pòst anté che la carta a dovrà esse sentrà",
+ "maps-par-kml": "Archivi KML da carié dzora a la carta.",
+ "maps-googlemaps3-incompatbrowser": "Tò navigator a l'é pa compatìbil con Google Maps v3.",
+ "maps-googlemaps3-par-type": "La sòrt ëd carta da smon-e inissialment.",
+ "maps-googlemaps3-par-types": "La sòrt ëd carta che a sarà disponìbil travers al contròl ëd sòrt.",
+ "maps-googlemaps3-par-layers": "Seuli speciaj da carié an sla carta.",
+ "maps-googlemaps3-par-controls": "Ël control da piassé an sla carta.",
+ "maps-googlemaps3-par-zoomstyle": "Lë stil dël contròl d'angrandiment.",
+ "maps-googlemaps3-par-typestyle": "Lë stil dël contròl ëd sòrt.",
+ "maps-googlemaps3-par-autoinfowindows": "Duverté automaticament tute le fnestre d'anformassion apress che la pàgina a l'é cariasse.",
+ "maps-googlemaps3-par-gkml": "Archivi KML ospità da Google da carié dzor la carta.",
+ "maps-googlemaps3-par-kmlrezoom": "Angrandì torna la carta na vira che ij seuj KML a son stàit carià.",
+ "maps-googlemaps3-par-poi": "Smon-e ij pont d'anteresse.",
+ "mapeditor": "Editor ëd carta",
+ "specialpages-group-maps": "Carte",
+ "mapeditor-parser-error": "A l'é capitaje n'eror an analisand dij metadat. Ignorà l'anseriment ëd l'utent.",
+ "mapeditor-none-text": "Gnun",
+ "mapeditor-done-button": "Fàit",
+ "mapeditor-remove-button": "Gava",
+ "mapeditor-import-button2": "Ampòrta",
+ "mapeditor-export-button": "Esporté an còdes wiki",
+ "mapeditor-import-button": "Ampòrta da còdes wiki",
+ "mapeditor-select-button": "Selessioné ës polìgon",
+ "mapeditor-mapparam-button": "Modifiché ij paràmetr ëd la carta",
+ "mapeditor-clear-button": "Scancelé la carta",
+ "mapeditor-code-title": "Còdes wiki",
+ "mapeditor-import-title": "Amporté ël còdes wiki",
+ "mapeditor-import-note": "Për piasì, ch'a nòta che l'analisator a së speta un formà motobin limità an sël còdes wiki. Ël còdes anserì ambelessì a dovrìa corisponde al còdes generà da la fonsionalità d'esportassion.",
+ "mapeditor-form-title": "Modifiché ij detaj",
+ "mapeditor-link-title-switcher-popup-text": "Fnestra ch'as duverta con test",
+ "mapeditor-link-title-switcher-link-text": "Anliura",
+ "mapeditor-form-field-title": "Tìtol",
+ "mapeditor-form-field-text": "Test",
+ "mapeditor-form-field-link": "Anliura",
+ "mapeditor-form-field-icon": "Plancia",
+ "mapeditor-form-field-group": "Partìa",
+ "mapeditor-form-field-inlinelabel": "Tichëtta an linia",
+ "mapeditor-form-field-strokecolor": "Color dël trat",
+ "mapeditor-form-field-strokeopacity": "Opassità dël trat",
+ "mapeditor-form-field-strokeweight": "Pèis dël trat",
+ "mapeditor-form-field-fillcolor": "Color d'ampiniment",
+ "mapeditor-form-field-fillopcaity": "Opassità d'ampiniment",
+ "mapeditor-form-field-showonhover": "Mostré mach cand a-i passa ël rat dëdzora",
+ "mapeditor-mapparam-title": "Modifiché ij paràmetr ëd la carta",
+ "mapeditor-mapparam-defoption": "-Ch'a selession-a un paràmetr-",
+ "mapeditor-imageoverlay-button": "Gionté la dzorposission ëd plancia",
+ "mapeditor-form-field-image": "Figura",
+ "mapeditor-imageoverlay-title": "Detaj dla dzorposission ëd plancia",
+ "mapeditor-form-field-visitedicon": "Plancia visità",
+ "semanticmaps-unrecognizeddistance": "Ël valor $1 a l'é pa na distansa bon-a.",
+ "semanticmaps-kml-link": "Vëdde l'archivi KML",
+ "semanticmaps-default-kml-pagelink": "Lese la pàgina $1",
+ "semanticmaps-latitude": "Latitùdin: $1",
+ "semanticmaps-longitude": "Longitùdin: $1",
+ "semanticmaps-altitude": "Autitùdin: $1",
+ "semanticmaps-forminput-locations": "Locassion",
+ "semanticmaps-par-staticlocations": "Na lista ëd locassion da gionté a la carta ansema ai dat ciamà. Com con dispay_points, a peul gionté un tìtol, na descrission e na plancia për locassion an dovrand la tilde \"~\" com separator.",
+ "semanticmaps-par-showtitle": "Smon-e un tìtol ant la fnesta d'anformassion dël marcator opura nò. La disabilitassion ëd sòn a l'é soens ùtil quand as deuvra në stamp për formaté ël contnù dla fnesta d'anformassion.",
+ "semanticmaps-par-hidenamespace": "Mostré ël tìtol dlë spassi nominal ant la fnestra d'anformassion dël marcador.",
+ "semanticmaps-par-centre": "Ël sènter ëd la carta. Quand a l'é pa dàit, la carta a trovrà automaticament ël sènter otimal për smon-e tùit ij marcador an sla carta.",
+ "semanticmaps-par-template": "Në stamp da dovré deje a forma ai contnù dla fnesta d'anformassion.",
+ "semanticmaps-par-geocodecontrol": "Smon-e ël contròl ëd geocodìfica.",
+ "semanticmaps-kml-text": "Ël test associà con minca pagina. Coatà da le propietà adissionaj ciamà s'a-i në j'é.",
+ "semanticmaps-kml-title": "Ël tìtol predefinì për j'arzultà",
+ "semanticmaps-kml-linkabsolute": "Si le liure a devo esse assolùe o nò (visadì relativ)",
+ "semanticmaps-kml-pagelinktext": "Ël test da dovré për le liure a la pàgina, dont $1 a sarà rimpiassà da 'l tìtol ëd la pàgina",
+ "semanticmaps-shapes-improperformat": "Formatà ëd $1 nen bon. Për piasì, ch'a fasa arferiment a la documentassion për ël formà vorsù",
+ "semanticmaps-shapes-missingshape": "Gnun-e forme trovà për $1. Për piasì, ch'a varda la documentassion për le forme disponìbij"
+}
diff --git a/www/wiki/extensions/Maps/i18n/ps.json b/www/wiki/extensions/Maps/i18n/ps.json
new file mode 100644
index 00000000..26b6d102
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ps.json
@@ -0,0 +1,26 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ahmed-Najib-Biabani-Ibrahimkhel"
+ ]
+ },
+ "maps_map": "کښېده",
+ "maps-others": "نور",
+ "maps-layer-property": "ځانتيا",
+ "maps-layer-value": "ارزښت",
+ "maps-layer-errors": "تېروتنې",
+ "maps-abb-north": "سهـ",
+ "maps-abb-east": "خ",
+ "maps-abb-south": "سو",
+ "maps-abb-west": "ل",
+ "maps-osm-par-photos": "انځورونه ښکاره کول",
+ "mapeditor-none-text": "هېڅ",
+ "mapeditor-done-button": "ترسره شو",
+ "mapeditor-remove-button": "غورځول",
+ "mapeditor-link-title-switcher-link-text": "تړنه",
+ "mapeditor-form-field-title": "سرليک",
+ "mapeditor-form-field-text": "متن",
+ "mapeditor-form-field-link": "تړنه",
+ "mapeditor-form-field-group": "ډله",
+ "mapeditor-form-field-image": "انځور"
+}
diff --git a/www/wiki/extensions/Maps/i18n/pt-br.json b/www/wiki/extensions/Maps/i18n/pt-br.json
new file mode 100644
index 00000000..e1b7df59
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/pt-br.json
@@ -0,0 +1,231 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cainamarques",
+ "Eduardo.mps",
+ "Giro720",
+ "Luckas",
+ "Luckas Blade",
+ "555",
+ "Macofe",
+ "Jaideraf",
+ "Eduardo Addad de Oliveira",
+ "Felipe L. Ewald"
+ ]
+ },
+ "maps-desc": "Permite a incorporação de mapas dinâmicos em páginas wiki, geocodificação de endereços e outras operações geográficas",
+ "right-geocode": "Geocodificar",
+ "action-geocode": "fazer geocodificação nesta wiki",
+ "maps_map": "Mapa",
+ "maps-tracking-category": "Páginas com um mapa composto pela extensão Maps",
+ "maps-loading-map": "Carregando mapa...",
+ "maps-load-failed": "Não foi possível carregar o mapa!",
+ "maps-markers": "Marcadores",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "Filtrar marcadores",
+ "maps-others": "outros",
+ "maps-kml-parsing-failed": "Falha na análise sintática de um ou mais ficheiros KML. Normalmente isto sucede devido a uma falha na importação ou devido a XML malformado.",
+ "maps-ns-layer": "Camada",
+ "maps-ns-layer-talk": "Camada Discussão",
+ "maps-layer-property": "Propriedade",
+ "maps-layer-value": "Valor",
+ "maps-layer-errors": "Erros",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Definição inválida|Definições inválidas}}",
+ "maps-layerdef-invalid-fatal": "Definição inválida fatal",
+ "maps-layerdef-wrong-namespace": "As definições de camadas só são válidas em páginas do espaço nominal \"$1\"",
+ "maps-layerdef-equal-layer-name": "Os nomes das camadas devem ser únicos dentro da mesma página de camadas. \"$1\" já está a ser utilizado por outra camada.",
+ "maps-layerpage-usage": "Páginas com mapas que usam a camada \"$1\"",
+ "maps-layerpage-nousage": "Esta camada não está a ser usada por nenhuma página de momento.",
+ "maps-error-invalid-layertype": "Não existem camadas do tipo \"$1\". Só {{PLURAL:$3|é suportado o tipo|são suportados os tipos}}: $2",
+ "maps-error-no-layertype": "Você precisa especificar o tipo de camada. {{PLURAL:$2|Só é suportado o tipo|São suportados os tipos}}: $1",
+ "validation-error-invalid-layer": "O parâmetro $1 precisa ser uma camada válida.",
+ "validation-error-invalid-layers": "O parâmetro $1 precisa ser uma ou mais camada(s) válida(s).",
+ "validation-error-no-non-numeric": "O parâmetro \"$1\" tem de ser um texto não numérico.",
+ "validation-error-no-non-numerics": "O parâmetro \"$1\" tem de ser um ou mais textos não numéricos.",
+ "maps-layer-of-type": "Camada de tipo $1",
+ "maps-layer-of-type-and-name": "Camada \"$2\" do tipo \"$1\"",
+ "maps-layer-type-supported-by": "Este tipo de camada só pode ser usado com {{PLURAL:$2|o serviço de cartografia $1|os serviços de cartografia: $1}}.",
+ "maps-coordinates-description": "Hook do analisador sintático para formatar coordenadas, a partir de qualquer um dos formatos suportados para qualquer outro formato suportado.",
+ "maps-displaymap-description": "Apresentar os mapas sem qualquer marcador definido na wiki.",
+ "maps-distance-description": "Converter uma distância numa unidade suportada para a distância equivalente noutra unidade.",
+ "maps-finddestination-description": "Encontrar um destino a partir de um ponto de partida (expresso em qualquer um dos formatos suportados), uma orientação inicial e uma distância.",
+ "maps-geocode-description": "Permite a geocodificação de moradas; por outras palavras, transforma locais legíveis por seres humanos em conjuntos de coordenadas. Há apoio para vários serviços de geocodificação, que não devem ser confundidos com serviços de cartografia.",
+ "maps-geodistance-description": "Calcula a distância geográfica entre dois pontos, a partir e para qualquer um dos formatos suportados.",
+ "maps-mapsdoc-description": "Apresentar uma tabela com os parâmetros de um serviço de cartografia especificado, em conjunto com os respetivos valores por omissão e descrições.",
+ "maps-layerdefinition-description": "Descreve uma camada personalizada que pode ser apresentada com outras funções Mapa.",
+ "maps-mapsdoc-par-service": "O serviço de cartografia para o qual será apresentada a documentação dos parâmetros.",
+ "maps-mapsdoc-par-language": "A língua de apresentação da documentação. Se essa tradução não estiver disponível, será usado o inglês.",
+ "maps-coordinates-par-location": "As coordenadas que quer formatar.",
+ "maps-coordinates-par-format": "O formato em que quer as coordenadas.",
+ "maps-coordinates-par-directional": "Indica se as coordenadas devem ser produzidas com formato direccional ou não.",
+ "maps-par-scrollwheelzoom": "Indica se a rolagem do mouse deve ser ativada ou não.",
+ "maps-distance-par-distance": "A distância para converter para a equivalente numa unidade especificada.",
+ "maps-distance-par-decimals": "O número máximo de casas decimais para usar no resultado.",
+ "maps-distance-par-unit": "A unidade na qual a distância será retornada.",
+ "maps-finddestination-par-location": "A localização inicial.",
+ "maps-finddestination-par-bearing": "A direção inicial.",
+ "maps-finddestination-par-distance": "A distância para percorrer.",
+ "maps-finddestination-par-format": "O formato em que sairá o destino.",
+ "maps-finddestination-par-directional": "Indica se o formato do destino deve ser direcional ou não.",
+ "maps-geocode-par-location": "O endereço que você deseja geocodificar.",
+ "maps-geocode-par-format": "O formato das coordenadas resultantes.",
+ "maps-geocode-par-directional": "Indica se as coordenadas devem ser produzidas com formato direcional ou não.",
+ "maps-geodistance-par-location1": "O primeiro ponto do conjunto para calcular a distância entre dois pontos.",
+ "maps-geodistance-par-location2": "O segundo ponto do conjunto para calcular a distância entre dois pontos.",
+ "maps-geodistance-par-unit": "A unidade na qual a distância será retornada.",
+ "maps-geodistance-par-decimals": "O número máximo de casas decimais para usar no resultado.",
+ "maps-displaymap-par-mappingservice": "Permite definir o serviço de cartografia que será usado para gerar o mapa.",
+ "maps-displaymap-par-coordinates": "A localização na qual o mapa será inicialmente centrado.",
+ "maps-displaymap-par-visitedicon": "O nome de arquivo de uma imagem a ser usada como ícones de marcação depois de os marcadores originais terem sido clicados",
+ "maps-displaymap-par-zoom": "Permite definir o nível de zoom.\nQuando este não for fornecido e existirem vários marcadores no mapa, será usado o zoom que resulte no melhor dimensionamento, não o valor padrão configurado.",
+ "maps-displaymap-par-centre": "Permite definir as coordenadas do centro do mapa para display_point(s).\nAceita tanto endereços como coordenadas.\nQuando esta propriedade não for fornecida, o mapa será centrado no marcador fornecido, ou entre os marcadores fornecidos.",
+ "maps-displaymap-par-title": "Permite a configuração do texto que será apresentado nos pop-ups de todos os marcadores que não têm um título específico.\nQuando usado em conjunto com um rótulo, o título aparecerá a negrito e sublinhado.",
+ "maps-displaymap-par-label": "Permite a configuração do texto que será apresentado nos pop-ups de todos os marcadores que não têm um rótulo específico.",
+ "maps-displaymap-par-icon": "Permite configurar o ícone usado para todos os marcadores.",
+ "maps-displaymap-par-circles": "Círculos para exibir",
+ "maps-displaymap-par-copycoords": "Mostrar uma caixa de diálogo quando clica numa localização da qual as coordenadas da mesma podem ser copiadas",
+ "maps-displaymap-par-lines": "Linhas para mostrar",
+ "maps-displaymap-par-maxzoom": "O nível máximo de zoom",
+ "maps-displaymap-par-minzoom": "O nível mínimo de zoom",
+ "maps-displaymap-par-polygons": "Polígonos para exibir",
+ "maps-displaymap-par-rectangles": "Retângulos para exibir",
+ "maps-displaymap-par-static": "Tornar o mapa estático",
+ "maps-displaymap-par-wmsoverlay": "Usar uma sobreposição WMS",
+ "maps-displaymap-par-geojson": "URL de um arquivo ou nome da página que contém dados GeoJSON",
+ "maps-fullscreen-button": "Ativar ou desativar a tela completa",
+ "maps-fullscreen-button-tooltip": "Ver o mapa em toda tela ou incorporada.",
+ "validation-error-invalid-location": "O parâmetro $1 precisa ser uma localização válida.",
+ "validation-error-invalid-locations": "O parâmetro $1 precisa ser uma ou mais localização(ões) válida(s).",
+ "validation-error-invalid-width": "O parâmetro $1 precisa ser uma largura válida.",
+ "validation-error-invalid-height": "O parâmetro $1 precisa ser uma altura válida.",
+ "validation-error-invalid-distance": "O parâmetro $1 precisa ser uma distância válida.",
+ "validation-error-invalid-distances": "O parâmetro $1 deve ser uma ou mais distância(s) válida(s).",
+ "validation-error-invalid-image": "O parâmetro $1 precisa ser uma imagem válida.",
+ "validation-error-invalid-images": "O parâmetro $1 precisa ser uma ou mais imagem(ns) válida(s).",
+ "validation-error-invalid-goverlay": "O parâmetro $1 precisa ser uma sobreposição válida.",
+ "validation-error-invalid-goverlays": "O parâmetro $1 precisa ser uma ou mais sobreposição(ões) válida(s).",
+ "maps-abb-north": "N",
+ "maps-abb-east": "L",
+ "maps-abb-south": "S",
+ "maps-abb-west": "O",
+ "maps-latitude": "Latitude:",
+ "maps-longitude": "Longitude:",
+ "maps-invalid-coordinates": "O valor $1 não foi reconhecido como um conjunto de coordenadas válido.",
+ "maps_coordinates_missing": "Não foram fornecidas coordenadas para o mapa.",
+ "maps_geocoding_failed": "{{PLURAL:$2|O seguinte endereço não pode|Os seguintes endereços não puderam}} ser {{PLURAL:$2|geocodificado|geocodificados}}: $1.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|O seguinte endereço não pode|Os seguintes endereços não puderam}} ser {{PLURAL:$2|geocodificado e foi omitido|geocodificados e foram omitidos}} do mapa:\n$1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|A seguinte coordenada não foi reconhecida|As seguintes coordenadas não foram reconhecidas}}: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|A seguinte coordenada não foi reconhecida e foi omitida|As seguintes coordenadas não foram reconhecidas e foram omitidas}} do mapa:\n$1",
+ "maps_map_cannot_be_displayed": "O mapa não pode ser mostrado.",
+ "maps-geocoder-not-available": "A funcionalidade de georeferenciação do Mapas está indisponível; a sua localização não pode ser georeferenciada.",
+ "maps_leaflet": "Folheto",
+ "maps-leaflet-par-defzoom": "Permite definir o nível de zoom predefinido do mapa.",
+ "maps-leaflet-par-layers": "A camada que será mostrada quando o mapa é carregado.",
+ "maps-leaflet-par-overlaylayers": "As camadas de sobreposição que serão mostradas quando o mapa é carregado.",
+ "maps-leaflet-par-maxclusterradius": "O raio máximo que um agregado irá cobrir a partir do marcador central (em píxeis).",
+ "maps-leaflet-par-clusterspiderfy": "Quando clica um agregado no nível de zoom mais baixo os marcadores são enteados para que possa vê-los todos.",
+ "maps_click_to_activate": "Clique para ativar o mapa",
+ "maps_centred_on": "Mapa centrado nas coordenadas $1, $2.",
+ "maps-par-mappingservice": "Permite definir o serviço de cartografia que será usado para gerar o mapa.",
+ "maps-par-resizable": "Permite alterar as dimensões do mapa, arrastando o canto inferior direito.",
+ "maps-par-searchmarkers": "Permite pesquisar marcadores específicos usando um campo incorporado no mapa.",
+ "maps-par-zoom": "O nível de zoom do mapa. Nos mapas com marcadores será usado o maior zoom que, mesmo assim, mostre todos os marcadores.",
+ "maps-par-width": "Permite definir a largura do mapa. A unidade por omissão é o píxel, mas pode defini-la explicitamente como: px, ex, em, %.",
+ "maps-par-height": "Permite definir a altura do mapa. A unidade por omissão é o píxel, mas pode defini-la explicitamente como: px, ex, em.",
+ "maps-par-centre": "A localização na qual o mapa deve estar centrado",
+ "maps-par-enable-fullscreen": "Ativar o botão de tela cheia",
+ "maps-par-kml": "Arquivos KML que serão carregados no mapa.",
+ "maps-par-markercluster": "Permite fundir vários marcadores próximos num único marcador",
+ "maps-googlemaps3-incompatbrowser": "O seu browser não é compatível com o Google Maps v3.",
+ "maps-googlemaps3-par-imageoverlays": "Permite adicionar uma imagem a ser mostrada na localização especificada do mapa.",
+ "maps-googlemaps3-par-type": "O tipo de mapa para mostrar inicialmente.",
+ "maps-googlemaps3-par-types": "Os tipos de mapas que estarão disponíveis através do controlo de tipos.",
+ "maps-googlemaps3-par-layers": "As camadas especiais que serão carregadas no mapa.",
+ "maps-googlemaps3-par-controls": "Os controles que serão colocados no mapa.",
+ "maps-googlemaps3-par-zoomstyle": "O estilo do controle de zoom.",
+ "maps-googlemaps3-par-typestyle": "O estilo do controle de tipos.",
+ "maps-googlemaps3-par-autoinfowindows": "Abrir automaticamente todas as janelas informativas depois de carregar a página.",
+ "maps-googlemaps3-par-gkml": "Arquivos KML alojados pelo Google que serão carregados no mapa.",
+ "maps-googlemaps3-par-kmlrezoom": "Voltar a ajustar o zoom do mapa depois de ter carregado as camadas KML.",
+ "maps-googlemaps3-par-poi": "Mostrar pontos de interesse.",
+ "maps-googlemaps3-par-clustergridsize": "A dimensão de grelha de um agregado, em píxeis.",
+ "maps-par-clustermaxzoom": "O nível máximo de zoom onde podem existir agregados.",
+ "maps-par-clusterzoomonclick": "Indica se o comportamento por omissão quando um agregado for clicado, é usar o zoom para aproximá-lo.",
+ "maps-par-maxclusterradius": "O raio máximo que um agregado irá cobrir.",
+ "maps-googlemaps3-par-clusteraveragecenter": "Indica se o centro de cada agregado deve ser o baricentro de todos os marcadores desse agregado.",
+ "maps-googlemaps3-par-clusterminsize": "O número mínimo de marcadores num agregado antes que os marcadores sejam ocultados e seja mostrada uma contagem.",
+ "mapeditor": "Editor de mapas",
+ "specialpages-group-maps": "Mapas",
+ "mapeditor-parser-error": "Ocorreu um erro durante a análise de metadados. A entrada de utilizador será ignorada.",
+ "mapeditor-none-text": "Nenhum",
+ "mapeditor-done-button": "Feito",
+ "mapeditor-remove-button": "Remover",
+ "mapeditor-import-button2": "Importar",
+ "mapeditor-export-button": "Exportar para código wiki",
+ "mapeditor-import-button": "Importar de código wiki",
+ "mapeditor-select-button": "Seleciona este polígono",
+ "mapeditor-mapparam-button": "Editar parâmetros do mapa",
+ "mapeditor-clear-button": "Limpar mapa",
+ "mapeditor-code-title": "Código wiki",
+ "mapeditor-import-title": "Importar código wiki",
+ "mapeditor-import-note": "Note, por favor, que o analisador espera um formato muito rigoroso no código wiki. O código inserido aqui deve corresponder ao código gerado pela funcionalidade de exportação.",
+ "mapeditor-form-title": "Editar detalhes",
+ "mapeditor-link-title-switcher-popup-text": "<i>Popup</i> com texto",
+ "mapeditor-link-title-switcher-link-text": "Link",
+ "mapeditor-form-field-title": "Título",
+ "mapeditor-form-field-text": "Texto",
+ "mapeditor-form-field-link": "Link",
+ "mapeditor-form-field-icon": "Ícone",
+ "mapeditor-form-field-group": "Grupo",
+ "mapeditor-form-field-inlinelabel": "Etiqueta em linha",
+ "mapeditor-form-field-strokecolor": "Cor de traço",
+ "mapeditor-form-field-strokeopacity": "Opacidade do traço",
+ "mapeditor-form-field-strokeweight": "Espessura do traço",
+ "mapeditor-form-field-fillcolor": "Cor de preenchimento",
+ "mapeditor-form-field-fillopcaity": "Opacidade do preenchimento",
+ "mapeditor-form-field-showonhover": "Mostrar só ao passar o ponteiro do mouse por cima",
+ "mapeditor-mapparam-title": "Editar parâmetros do mapa",
+ "mapeditor-mapparam-defoption": "-Selecionar parâmetro-",
+ "mapeditor-imageoverlay-button": "Adicionar sobreposição de imagem",
+ "mapeditor-form-field-image": "Imagem",
+ "mapeditor-imageoverlay-title": "Detalhes da sobreposição de imagem",
+ "mapeditor-form-field-visitedicon": "Ícone visitado",
+ "semanticmaps-unrecognizeddistance": "O valor $1 não é uma distância válida.",
+ "semanticmaps-kml-link": "Ver o arquivo KML",
+ "semanticmaps-default-kml-pagelink": "Ver a página $1",
+ "semanticmaps-latitude": "Latitude: $1",
+ "semanticmaps-longitude": "Longitude: $1",
+ "semanticmaps-altitude": "Altitude: $1",
+ "semanticmaps-forminput-locations": "Locais",
+ "semanticmaps-par-staticlocations": "Uma lista de localizações para adicionar ao mapa junto aos dados consultados. Assim como nos pontos a serem exibidos (\"display_points\"), você pode adicionar um título, descrição e ícone por localização, usando o til (\"~\") como separador.",
+ "semanticmaps-par-showtitle": "Mostrar, ou não, um título na janela informativa do marcador. É frequentemente desejável desativar este recurso quando estiver usando uma predefinição para formatar o conteúdo da janela informativa.",
+ "semanticmaps-par-hidenamespace": "Mostrar o título do domínio na janela de informações do marcador",
+ "semanticmaps-par-centre": "O centro do mapa. Quando este não for definido, o mapa escolherá automaticamente o centro ideal para apresentar todos os marcadores do mapa.",
+ "semanticmaps-par-template": "Uma predefinição que será usada para formatar o conteúdo da janela informativa.",
+ "semanticmaps-par-geocodecontrol": "Exibir o controle de geocodificação.",
+ "semanticmaps-par-activeicon": "Ícone a ser exibido ao invés do marcador padrão, quando a página ativa é igual ao resultado da consulta",
+ "semanticmaps-par-pagelabel": "Quando configurado para \"yes\", todos os marcadores terão um \"inlineLabel\" com um link para a página que contém as coordenadas para o marcador",
+ "semanticmaps-par-ajaxcoordproperty": "Nome da propriedade das coordenadas que é usada para construir a consulta ajax.",
+ "semanticmaps-par-ajaxquery": "Uma segunda consulta que é enviada via ajax para obter coordenadas adicionais.",
+ "semanticmaps-par-userparam": "Valor fornecido a cada chamada da predefinição, se uma predefinição for utilizada",
+ "semanticmaps-kml-text": "O texto associado a cada página. Será substituído quando propriedades adicionais consultadas existirem.",
+ "semanticmaps-kml-title": "O título padrão para os resultados",
+ "semanticmaps-kml-linkabsolute": "Os links deverão ser absolutos (ao contrário de relativos)",
+ "semanticmaps-kml-pagelinktext": "O texto a ser usado nos links para a página, onde $1 será substituído pelo título da página",
+ "semanticmaps-shapes-improperformat": "Formatação imprópria em $1. Por favor, veja a documentação para formatação",
+ "semanticmaps-shapes-missingshape": "Nenhuma forma foi encontrada para $1. Por favor, veja a documentação para formas disponíveis",
+ "validator-type-mapscircle": "Círculo geográfico",
+ "validator-type-mapscircle-list": "Lista de círculos",
+ "validator-type-mapsimageoverlay": "Sobreposição de imagem",
+ "validator-type-mapsimageoverlay-list": "Lista de sobreposições de imagem",
+ "validator-type-mapsline": "Linha geográfica",
+ "validator-type-mapsline-list": "Lista de linhas",
+ "validator-type-mapslocation": "Localização geográfica",
+ "validator-type-mapslocation-list": "Lista de locais",
+ "validator-type-mapsrectangle": "Retângulo geográfico",
+ "validator-type-mapsrectangle-list": "Lista de retângulos",
+ "validator-type-mapspolygon": "Polígono geográfico",
+ "validator-type-mapspolygon-list": "Lista de polígonos geográficos",
+ "validator-type-wmsoverlay": "Sobreposição Web Map Service",
+ "validator-type-jsonfile": "texto"
+}
diff --git a/www/wiki/extensions/Maps/i18n/pt.json b/www/wiki/extensions/Maps/i18n/pt.json
new file mode 100644
index 00000000..7bf9b166
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/pt.json
@@ -0,0 +1,233 @@
+{
+ "@metadata": {
+ "authors": [
+ "Alchimista",
+ "Giro720",
+ "GoEThe",
+ "Hamilton Abreu",
+ "Lijealso",
+ "Luckas",
+ "Waldir",
+ "Vitorvicentevalente",
+ "Macofe",
+ "Malafaya",
+ "Fúlvio",
+ "Athena in Wonderland"
+ ]
+ },
+ "maps-desc": "Permite incorporar mapas dinâmicos nas páginas da wiki, converter endereços em geocódigos e outras operações geográficas",
+ "right-geocode": "Geocodificar",
+ "action-geocode": "fazer geocodificação nesta wiki",
+ "maps_map": "Mapa",
+ "maps-tracking-category": "Páginas com um mapa composto pela extensão Maps",
+ "maps-loading-map": "A carregar o mapa...",
+ "maps-load-failed": "Não foi possível carregar o mapa!",
+ "maps-markers": "Marcadores",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "Filtrar marcadores",
+ "maps-others": "outros",
+ "maps-kml-parsing-failed": "Falha na análise sintática de um ou mais ficheiros KML. Normalmente isto sucede devido a uma falha na importação ou devido a XML malformado.",
+ "maps-ns-layer": "Camada",
+ "maps-ns-layer-talk": "Camada Discussão",
+ "maps-layer-property": "Propriedade",
+ "maps-layer-value": "Valor",
+ "maps-layer-errors": "Erros",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Definição inválida|Definições inválidas}}",
+ "maps-layerdef-invalid-fatal": "Definição inválida fatal",
+ "maps-layerdef-wrong-namespace": "As definições de camadas só são válidas em páginas do espaço nominal \"$1\"",
+ "maps-layerdef-equal-layer-name": "Os nomes das camadas devem ser únicos dentro da mesma página de camadas. \"$1\" já está a ser utilizado por outra camada.",
+ "maps-layerpage-usage": "Páginas com mapas que usam a camada \"$1\"",
+ "maps-layerpage-nousage": "Esta camada não está a ser usada por nenhuma página de momento.",
+ "maps-error-invalid-layertype": "Não existem camadas do tipo \"$1\". Só {{PLURAL:$3|é suportado o tipo|são suportados os tipos}}: $2",
+ "maps-error-no-layertype": "Tem de especificar o tipo da camada. {{PLURAL:$2|Só é suportado este tipo|São suportados os tipos}}: $1",
+ "validation-error-invalid-layer": "O parâmetro \"$1\" tem de ser uma camada válida.",
+ "validation-error-invalid-layers": "O parâmetro \"$1\" tem de ser uma ou mais camadas válidas.",
+ "validation-error-no-non-numeric": "O parâmetro \"$1\" tem de ser um texto não numérico.",
+ "validation-error-no-non-numerics": "O parâmetro \"$1\" tem de ser um ou mais textos não numéricos.",
+ "maps-layer-of-type": "Camada do tipo \"$1\"",
+ "maps-layer-of-type-and-name": "Camada \"$2\" do tipo \"$1\"",
+ "maps-layer-type-supported-by": "Este tipo de camada só pode ser usado com {{PLURAL:$2|o serviço de cartografia $1|os serviços de cartografia: $1}}.",
+ "maps-coordinates-description": "Hook do analisador sintático para formatar coordenadas, a partir de qualquer um dos formatos suportados para qualquer outro formato suportado.",
+ "maps-displaymap-description": "Apresentar os mapas sem qualquer marcador definido na wiki.",
+ "maps-distance-description": "Converter uma distância numa unidade suportada para a distância equivalente noutra unidade.",
+ "maps-finddestination-description": "Encontrar um destino a partir de um ponto de partida (expresso em qualquer um dos formatos suportados), uma orientação inicial e uma distância.",
+ "maps-geocode-description": "Permite a geocodificação de moradas; por outras palavras, transforma locais legíveis por seres humanos em conjuntos de coordenadas. Há apoio para vários serviços de geocodificação, que não devem ser confundidos com serviços de cartografia.",
+ "maps-geodistance-description": "Calcula a distância geográfica entre dois pontos, a partir e para qualquer um dos formatos suportados.",
+ "maps-mapsdoc-description": "Apresentar uma tabela com os parâmetros de um serviço de cartografia especificado, em conjunto com os respetivos valores por omissão e descrições.",
+ "maps-layerdefinition-description": "Descreve uma camada personalizada que pode ser apresentada com outras funções Mapa.",
+ "maps-mapsdoc-par-service": "O serviço de cartografia para o qual será apresentada a documentação dos parâmetros.",
+ "maps-mapsdoc-par-language": "A língua de apresentação da documentação. Se essa tradução não estiver disponível, será usado o inglês.",
+ "maps-coordinates-par-location": "As coordenadas que quer formatar.",
+ "maps-coordinates-par-format": "O formato em que quer as coordenadas.",
+ "maps-coordinates-par-directional": "Indica se as coordenadas devem ser produzidas com formato direccional ou não.",
+ "maps-par-scrollwheelzoom": "Indica se o deslizamento do rato deve ser ativado ou não.",
+ "maps-distance-par-distance": "A distância para converter para a equivalente numa unidade especificada.",
+ "maps-distance-par-decimals": "O número máximo de casas decimais a usar no valor resultante.",
+ "maps-distance-par-unit": "As unidades em que a distância será produzida.",
+ "maps-finddestination-par-location": "A localização inicial.",
+ "maps-finddestination-par-bearing": "A direção inicial.",
+ "maps-finddestination-par-distance": "A distância a percorrer.",
+ "maps-finddestination-par-format": "O formato em que sairá o destino.",
+ "maps-finddestination-par-directional": "Indica se o formato do destino deve ser direccional ou não.",
+ "maps-geocode-par-location": "A morada que quer geocodificar.",
+ "maps-geocode-par-format": "O formato das coordenadas resultantes.",
+ "maps-geocode-par-directional": "Indica se as coordenadas devem ser produzidas com formato direccional ou não.",
+ "maps-geodistance-par-location1": "O primeiro ponto do conjunto para calcular a distância entre dois pontos.",
+ "maps-geodistance-par-location2": "O segundo ponto do conjunto para calcular a distância entre dois pontos.",
+ "maps-geodistance-par-unit": "As unidades em que a distância será produzida.",
+ "maps-geodistance-par-decimals": "O número máximo de casas decimais a usar no valor resultante.",
+ "maps-displaymap-par-mappingservice": "Permite definir o serviço de cartografia que será usado para gerar o mapa.",
+ "maps-displaymap-par-coordinates": "A localização na qual o mapa será inicialmente centrado.",
+ "maps-displaymap-par-visitedicon": "O nome de ficheiro de uma imagem a ser usada como ícones de marcação depois de os marcadores originais terem sido clicados",
+ "maps-displaymap-par-zoom": "Permite definir o nível de zoom.\nQuando este não for fornecido e existirem vários marcadores no mapa, será usado o zoom que resulte no melhor dimensionamento, não o valor padrão configurado.",
+ "maps-displaymap-par-centre": "Permite definir as coordenadas do centro do mapa para display_point(s).\nAceita tanto endereços como coordenadas.\nQuando esta propriedade não for fornecida, o mapa será centrado no marcador fornecido, ou entre os marcadores fornecidos.",
+ "maps-displaymap-par-title": "Permite a configuração do texto que será apresentado nos pop-ups de todos os marcadores que não têm um título específico.\nQuando usado em conjunto com um rótulo, o título aparecerá a negrito e sublinhado.",
+ "maps-displaymap-par-label": "Permite a configuração do texto que será apresentado nos pop-ups de todos os marcadores que não têm um rótulo específico.",
+ "maps-displaymap-par-icon": "Permite configurar o ícone usado para todos os marcadores.",
+ "maps-displaymap-par-circles": "Círculos a serem apresentados",
+ "maps-displaymap-par-copycoords": "Mostrar uma caixa de diálogo quando clica numa localização da qual as coordenadas da mesma podem ser copiadas",
+ "maps-displaymap-par-lines": "Linhas a serem apresentadas",
+ "maps-displaymap-par-maxzoom": "O nível máximo de zoom",
+ "maps-displaymap-par-minzoom": "O nível mínimo de zoom",
+ "maps-displaymap-par-polygons": "Polígonos a serem apresentados",
+ "maps-displaymap-par-rectangles": "Retângulos a serem apresentados",
+ "maps-displaymap-par-static": "Tornar o mapa estático",
+ "maps-displaymap-par-wmsoverlay": "Usar uma sobreposição WMS",
+ "maps-displaymap-par-geojson": "URL de um ficheiro, ou nome da página, que contém dados GeoJSON",
+ "maps-fullscreen-button": "Ativar ou desativar ecrã completo",
+ "maps-fullscreen-button-tooltip": "Ver o mapa em todo o ecrã ou incorporado.",
+ "validation-error-invalid-location": "O parâmetro \"$1\" tem de ser uma localização válida.",
+ "validation-error-invalid-locations": "O parâmetro \"$1\" tem de ser uma ou mais localizações válidas.",
+ "validation-error-invalid-width": "O parâmetro \"$1\" tem de ser uma largura válida.",
+ "validation-error-invalid-height": "O parâmetro \"$1\" tem de ser uma altura válida.",
+ "validation-error-invalid-distance": "O parâmetro \"$1\" tem de ser uma distância válida.",
+ "validation-error-invalid-distances": "O parâmetro \"$1\" tem de ser uma ou mais distâncias válidas.",
+ "validation-error-invalid-image": "O parâmetro \"$1\" tem de ser uma imagem válida.",
+ "validation-error-invalid-images": "O parâmetro \"$1\" tem de ser uma ou mais imagens válidas.",
+ "validation-error-invalid-goverlay": "O parâmetro \"$1\" tem de ser uma sobreposição válida.",
+ "validation-error-invalid-goverlays": "O parâmetro \"$1\" tem de ser uma ou mais sobreposições válidas.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "O",
+ "maps-latitude": "Latitude:",
+ "maps-longitude": "Longitude:",
+ "maps-invalid-coordinates": "O valor $1 não foi reconhecido como um conjunto de coordenadas válido.",
+ "maps_coordinates_missing": "Não foram fornecidas coordenadas para o mapa.",
+ "maps_geocoding_failed": "Não foi possível geocodificar {{PLURAL:$2|o seguinte endereço|os seguintes endereços}}: $1.",
+ "maps_geocoding_failed_for": "Não foi possível geocodificar {{PLURAL:$2|o seguinte endereço, que foi omitido|os seguintes endereços, que foram omitidos}} do mapa:\n$1.",
+ "maps_unrecognized_coords": "{{PLURAL:$2|A seguinte coordenada não foi reconhecida|As seguintes coordenadas não foram reconhecidas}}: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|A seguinte coordenada não foi reconhecida e foi omitida|As seguintes coordenadas não foram reconhecidas e foram omitidas}} do mapa:\n$1",
+ "maps_map_cannot_be_displayed": "Não é possível apresentar o mapa.",
+ "maps-geocoder-not-available": "A funcionalidade de geocodificação do Mapas está indisponível; a sua localização não pode ser geocodificada.",
+ "maps_leaflet": "Folheto",
+ "maps-leaflet-par-defzoom": "Permite definir o nível de zoom predefinido do mapa.",
+ "maps-leaflet-par-layers": "A camada que será mostrada quando o mapa é carregado.",
+ "maps-leaflet-par-overlaylayers": "As camadas de sobreposição que serão mostradas quando o mapa é carregado.",
+ "maps-leaflet-par-maxclusterradius": "O raio máximo que um agregado irá cobrir a partir do marcador central (em píxeis).",
+ "maps-leaflet-par-clusterspiderfy": "Quando clica um agregado no nível de zoom mais baixo os marcadores são enteados para que possa vê-los todos.",
+ "maps_click_to_activate": "Clique para ativar o mapa",
+ "maps_centred_on": "Mapa centrado nas coordenadas $1, $2.",
+ "maps-par-mappingservice": "Permite definir o serviço de cartografia que será usado para gerar o mapa.",
+ "maps-par-resizable": "Permite alterar as dimensões do mapa, arrastando o canto inferior direito.",
+ "maps-par-searchmarkers": "Permite pesquisar marcadores específicos usando um campo incorporado no mapa.",
+ "maps-par-zoom": "O nível de zoom do mapa. Nos mapas com marcadores será usado o maior zoom que, mesmo assim, mostre todos os marcadores.",
+ "maps-par-width": "Permite definir a largura do mapa. A unidade por omissão é o píxel, mas pode defini-la explicitamente como: px, ex, em, %.",
+ "maps-par-height": "Permite definir a altura do mapa. A unidade por omissão é o píxel, mas pode defini-la explicitamente como: px, ex, em, %.",
+ "maps-par-centre": "A localização na qual o mapa deve estar centrado",
+ "maps-par-enable-fullscreen": "Ativar o botão de ecrã completo",
+ "maps-par-kml": "Ficheiros KML que serão carregados no mapa.",
+ "maps-par-markercluster": "Permite fundir vários marcadores próximos num único marcador",
+ "maps-googlemaps3-incompatbrowser": "O seu browser não é compatível com o Google Maps v3.",
+ "maps-googlemaps3-par-imageoverlays": "Permite adicionar uma imagem a ser mostrada na localização especificada do mapa.",
+ "maps-googlemaps3-par-type": "O tipo de mapa que será apresentado inicialmente.",
+ "maps-googlemaps3-par-types": "Os tipos de mapas que estarão disponíveis através do controlo de tipos.",
+ "maps-googlemaps3-par-layers": "As camadas especiais que serão carregadas no mapa.",
+ "maps-googlemaps3-par-controls": "Os controlos que serão colocados no mapa.",
+ "maps-googlemaps3-par-zoomstyle": "O estilo do controlo de zoom.",
+ "maps-googlemaps3-par-typestyle": "O estilo do controlo de tipos.",
+ "maps-googlemaps3-par-autoinfowindows": "Abrir automaticamente todas as janelas informativas depois de carregar a página.",
+ "maps-googlemaps3-par-gkml": "Ficheiros KML alojados pelo Google que serão carregados no mapa.",
+ "maps-googlemaps3-par-kmlrezoom": "Voltar a ajustar o zoom do mapa depois de ter carregado as camadas KML.",
+ "maps-googlemaps3-par-poi": "Mostrar pontos de interesse.",
+ "maps-googlemaps3-par-clustergridsize": "A dimensão de grelha de um agregado, em píxeis.",
+ "maps-par-clustermaxzoom": "O nível máximo de zoom onde podem existir agregados.",
+ "maps-par-clusterzoomonclick": "Indica se o comportamento por omissão quando um agregado for clicado, é usar o zoom para aproximá-lo.",
+ "maps-par-maxclusterradius": "O raio máximo que um agregado irá cobrir.",
+ "maps-googlemaps3-par-clusteraveragecenter": "Indica se o centro de cada agregado deve ser o baricentro de todos os marcadores desse agregado.",
+ "maps-googlemaps3-par-clusterminsize": "O número mínimo de marcadores num agregado antes que os marcadores sejam ocultados e seja mostrada uma contagem.",
+ "mapeditor": "Editor de mapas",
+ "specialpages-group-maps": "Mapas",
+ "mapeditor-parser-error": "Ocorreu um erro durante a análise de metadados. A entrada de utilizador será ignorada.",
+ "mapeditor-none-text": "Nenhum",
+ "mapeditor-done-button": "Concluído",
+ "mapeditor-remove-button": "Remover",
+ "mapeditor-import-button2": "Importar",
+ "mapeditor-export-button": "Exportar para código wiki",
+ "mapeditor-import-button": "Importar de código wiki",
+ "mapeditor-select-button": "Selecionar este polígono",
+ "mapeditor-mapparam-button": "Editar parâmetros do mapa",
+ "mapeditor-clear-button": "Limpar mapa",
+ "mapeditor-code-title": "Código wiki",
+ "mapeditor-import-title": "Importar código wiki",
+ "mapeditor-import-note": "Note, por favor, que o analisador espera um formato muito rigoroso no código wiki. O código inserido aqui deve corresponder ao código gerado pela funcionalidade de exportação.",
+ "mapeditor-form-title": "Editar detalhes",
+ "mapeditor-link-title-switcher-popup-text": "<i>Popup</i> com texto",
+ "mapeditor-link-title-switcher-link-text": "Hiperligação",
+ "mapeditor-form-field-title": "Título",
+ "mapeditor-form-field-text": "Texto",
+ "mapeditor-form-field-link": "Hiperligação",
+ "mapeditor-form-field-icon": "Ícone",
+ "mapeditor-form-field-group": "Grupo",
+ "mapeditor-form-field-inlinelabel": "Etiqueta em linha",
+ "mapeditor-form-field-strokecolor": "Cor de traço",
+ "mapeditor-form-field-strokeopacity": "Opacidade do traço",
+ "mapeditor-form-field-strokeweight": "Espessura do traço",
+ "mapeditor-form-field-fillcolor": "Cor de preenchimento",
+ "mapeditor-form-field-fillopcaity": "Opacidade do preenchimento",
+ "mapeditor-form-field-showonhover": "Mostrar só ao passar o ponteiro do rato por cima",
+ "mapeditor-mapparam-title": "Editar parâmetros do mapa",
+ "mapeditor-mapparam-defoption": "-Selecione o parâmetro-",
+ "mapeditor-imageoverlay-button": "Adicionar sobreposição de imagem",
+ "mapeditor-form-field-image": "Imagem",
+ "mapeditor-imageoverlay-title": "Detalhes da sobreposição de imagem",
+ "mapeditor-form-field-visitedicon": "Ícone visitado",
+ "semanticmaps-unrecognizeddistance": "O valor $1 não é uma distância válida.",
+ "semanticmaps-kml-link": "Ver o ficheiro KML",
+ "semanticmaps-default-kml-pagelink": "Ver a página $1",
+ "semanticmaps-latitude": "Latitude: $1",
+ "semanticmaps-longitude": "Longitude: $1",
+ "semanticmaps-altitude": "Altitude: $1",
+ "semanticmaps-forminput-locations": "Locais",
+ "semanticmaps-par-staticlocations": "Uma lista de locais para acrescentar ao mapa em conjunto com os dados consultados. Tal como nos pontos a apresentar (\"display_points\"), pode adicionar um título, descrição e ícone por local, usando o til \"~\" como separador.",
+ "semanticmaps-par-showtitle": "Mostrar, ou não, um título na janela informativa do marcador. É frequentemente desejável desativar esta funcionalidade quando usar uma predefinição para formatar o conteúdo da janela informativa.",
+ "semanticmaps-par-hidenamespace": "Mostrar o título do espaço nominal na janela informativa do marcador",
+ "semanticmaps-par-centre": "O centro do mapa. Quando este não for fornecido, o mapa escolherá automaticamente o centro ótimo para apresentar todos os marcadores do mapa.",
+ "semanticmaps-par-template": "Uma predefinição que será usada para formatar o conteúdo da janela informativa.",
+ "semanticmaps-par-geocodecontrol": "Mostrar o controlo de geocodificação.",
+ "semanticmaps-par-activeicon": "Ícone para ser apresentado em vez do marcador padrão, quando a página ativa é igual ao resultado da consulta",
+ "semanticmaps-par-pagelabel": "Quando definido como \"yes\", todos os marcadores terão um rótulo em linha (\"inlineLabel\") com uma hiperligação para a página que contém as coordenadas do marcador",
+ "semanticmaps-par-ajaxcoordproperty": "Nome da propriedade das coordenadas que é usada para construir a consulta ajax.",
+ "semanticmaps-par-ajaxquery": "Uma segunda consulta que é enviada via ajax para obter coordenadas adicionais.",
+ "semanticmaps-par-userparam": "Um valor passado a cada chamada da predefinição, se for usada uma predefinição",
+ "semanticmaps-kml-text": "O texto associado a cada página. Substituído pelas propriedades adicionais consultadas, se existirem.",
+ "semanticmaps-kml-title": "O título padrão para os resultados",
+ "semanticmaps-kml-linkabsolute": "Indica se as hiperligações devem ser absolutas (em vez de relativas)",
+ "semanticmaps-kml-pagelinktext": "O texto a utilizar nas hiperligações para a página, onde $1 será substituído pelo título da página",
+ "semanticmaps-shapes-improperformat": "Formato indevido de $1. Consulte a documentação sobre formatação, por favor",
+ "semanticmaps-shapes-missingshape": "Não foram encontradas nenhumas formas para $1. Consulte a documentação sobre as formas disponíveis, por favor",
+ "validator-type-mapscircle": "Círculo geográfico",
+ "validator-type-mapscircle-list": "Lista de círculos",
+ "validator-type-mapsimageoverlay": "Sobreposição de imagem",
+ "validator-type-mapsimageoverlay-list": "Lista de sobreposições de imagem",
+ "validator-type-mapsline": "Linha geográfica",
+ "validator-type-mapsline-list": "Lista de linhas",
+ "validator-type-mapslocation": "Localização geográfica",
+ "validator-type-mapslocation-list": "Lista de locais",
+ "validator-type-mapsrectangle": "Retângulo geográfico",
+ "validator-type-mapsrectangle-list": "Lista de retângulos",
+ "validator-type-mapspolygon": "Polígono geográfico",
+ "validator-type-mapspolygon-list": "Lista de polígonos geográficos",
+ "validator-type-wmsoverlay": "Sobreposição Web Map Service",
+ "validator-type-jsonfile": "texto"
+}
diff --git a/www/wiki/extensions/Maps/i18n/qqq.json b/www/wiki/extensions/Maps/i18n/qqq.json
new file mode 100644
index 00000000..89298677
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/qqq.json
@@ -0,0 +1,235 @@
+{
+ "@metadata": {
+ "authors": [
+ "EugeneZelenko",
+ "Fryed-peach",
+ "Kghbln",
+ "Purodha",
+ "Raymond",
+ "Shirayuki",
+ "Siebrand",
+ "Umherirrender",
+ "Тест",
+ "아라",
+ "Liuxinyu970226",
+ "Nike",
+ "Lloffiwr",
+ "Robby"
+ ]
+ },
+ "maps-desc": "{{desc|name=Maps|url=https://www.semantic-mediawiki.org/wiki/Extension:Maps}}",
+ "right-geocode": "{{doc-right|geocode}}",
+ "action-geocode": "{{doc-action|geocode}}",
+ "maps_map": "{{Identical|Map}}",
+ "maps-tracking-category": "The name of a category for all pages which use the <code><nowiki>#display_map</nowiki></code> parser function or <code><nowiki>display_map</nowiki></code> tag.",
+ "maps-loading-map": "This is an informatory message.",
+ "maps-load-failed": "This is an error message.",
+ "maps-markers": "{{Identical|Marker}}",
+ "maps-copycoords-prompt": "This is the text displayed in JavaScript prompt to indicate first press ctrl+c to copy text, and press enter to close prompt.",
+ "maps-searchmarkers-text": "{{doc-important|Translate \"Filter\" as being a word.}}\nThis message is located within an input box to assist users with the input. This functionality allows to filter the markers based on the input one provides. See the [https://www.semantic-mediawiki.org/wiki/Maps_examples/OpenLayers_searchmarkers live example].",
+ "maps-others": "{{Identical|Other}}",
+ "maps-kml-parsing-failed": "This is an error message.",
+ "maps-ns-layer": "This is the name of the \"Layer\" namespace. The term \"Layer\" should be identical to the one used for the corresponding namespace. See {{msg-mw|Maps-ns-layer-talk}}\n\n{{Identical|Layer}}",
+ "maps-ns-layer-talk": "This is the name of the talk page for the \"Layer\" namespace. The term \"Layer\" should be identical to the one used for the corresponding namespace. See {{msg-mw|Maps-ns-layer}}\n\n{{Identical|Layer}}",
+ "maps-layer-property": "{{Identical|Property}}",
+ "maps-layer-value": "{{Identical|Value}}",
+ "maps-layer-errors": "{{Identical|Error}}",
+ "maps-layerdef-invalid": "This is an error message.\n\nParameter:\n* $1 - number of definitions",
+ "maps-layerdef-invalid-fatal": "This is an error message.",
+ "maps-layerdef-wrong-namespace": "This is an error message.\n\nParameter:\n* $1 - namespace name",
+ "maps-layerdef-equal-layer-name": "This is an error message.\n\nParameter:\n* $1 - layer name",
+ "maps-layerpage-usage": "This is an informatory message.\n\nParameter:\n* $1 - layer name",
+ "maps-layerpage-nousage": "This is an informatory message.",
+ "maps-error-invalid-layertype": "This is an error message.\n\nParameters:\n* $1 - a layer type\n* $2 - list of available layer types\n* $3 - number of available layer types",
+ "maps-error-no-layertype": "This is an error message.\n\nParameters:\n* $1 - list of available layer types\n* $2 - number of available layer types",
+ "validation-error-invalid-layer": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}",
+ "validation-error-invalid-layers": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}",
+ "validation-error-no-non-numeric": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}",
+ "validation-error-no-non-numerics": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}",
+ "maps-layer-of-type": "This is used as an <code><nowiki><h3></nowiki></code> heading.\n\nParameter:\n* $1 - layer type",
+ "maps-layer-of-type-and-name": "This is used as an <code><nowiki><h3></nowiki></code> heading.\n\nParameters:\n* $1 - layer type\n* $2 - layer name",
+ "maps-layer-type-supported-by": "This is an informatory and/or error message.\n\nParameters:\n* $1 - list of supported services (geonames and/or google).\n* $2 - number of supported services\n\n{{doc-important|Do not localise the supported services shown in parameter $1!}}",
+ "maps-coordinates-description": "This message describes the <code><nowiki>#coordinates</nowiki></code> parser function as well as the <code><nowiki>coordinates</nowiki></code> tag.",
+ "maps-displaymap-description": "This message describes the <code><nowiki>#display_map</nowiki></code> parser function as well as the <code><nowiki>display_map</nowiki></code> tag.",
+ "maps-distance-description": "This message describes the <code><nowiki>#distance</nowiki></code> parser function as well as the <code><nowiki>distance</nowiki></code> tag.",
+ "maps-finddestination-description": "This message describes the <code><nowiki>#finddestination</nowiki></code> parser function as well as the <code><nowiki>finddestination</nowiki></code> tag.",
+ "maps-geocode-description": "This message describes the <code><nowiki>#geocode</nowiki></code> parser function as well as the <code><nowiki>geocode</nowiki></code> tag.",
+ "maps-geodistance-description": "This message describes the <code><nowiki>#geodistance</nowiki></code> parser function as well as the <code><nowiki>geodistance</nowiki></code> tag.",
+ "maps-mapsdoc-description": "This message describes the <code><nowiki>#mapsdoc</nowiki></code> parser function as well as the <code><nowiki>mapsdoc</nowiki></code> tag.",
+ "maps-layerdefinition-description": "This message describes the <code><nowiki>#layerdifinition</nowiki></code> parser function as well as the <code><nowiki>layerdefinition</nowiki></code> tag.",
+ "maps-mapsdoc-par-service": "{{maps-par|mapsdoc|service}}",
+ "maps-mapsdoc-par-language": "{{maps-par|mapsdoc|language}}",
+ "maps-coordinates-par-location": "{{maps-par|coordinates|location}}",
+ "maps-coordinates-par-format": "{{maps-par|coordinates|format}}",
+ "maps-coordinates-par-directional": "{{maps-par|coordinates|directional}}",
+ "maps-par-scrollwheelzoom": "{{maps-par|coordinates|scrollwheelzoom}}",
+ "maps-distance-par-distance": "{{maps-par|distance|distance}}",
+ "maps-distance-par-decimals": "{{maps-par|distance|decimals}}",
+ "maps-distance-par-unit": "{{maps-par|distance|unit}}",
+ "maps-finddestination-par-location": "{{maps-par|finddestination|location}}",
+ "maps-finddestination-par-bearing": "{{maps-par|finddestination|bearing}}",
+ "maps-finddestination-par-distance": "{{maps-par|finddestination|distance}}",
+ "maps-finddestination-par-format": "{{maps-par|finddestination|format}}",
+ "maps-finddestination-par-directional": "{{maps-par|finddestination|directional}}",
+ "maps-geocode-par-location": "{{maps-par|geocode|location}}",
+ "maps-geocode-par-format": "{{maps-par|geocode|format}}",
+ "maps-geocode-par-directional": "{{maps-par|geocode|directional}}",
+ "maps-geodistance-par-location1": "{{Maps-par|geodistance|location1}}",
+ "maps-geodistance-par-location2": "{{Maps-par|geodistance|location2}}",
+ "maps-geodistance-par-unit": "{{Maps-par|geodistance|unit}}",
+ "maps-geodistance-par-decimals": "{{Maps-par|geodistance|decimals}}",
+ "maps-displaymap-par-mappingservice": "{{Maps-par|displaymap|mappingservice}}",
+ "maps-displaymap-par-coordinates": "{{Maps-par|displaymap|coordinates}}",
+ "maps-displaymap-par-visitedicon": "{{Maps-par|displaymap|visitedicon}}",
+ "maps-displaymap-par-zoom": "{{Maps-par|displaymap|zoom}}",
+ "maps-displaymap-par-centre": "{{Maps-par|displaymap|centre}}",
+ "maps-displaymap-par-title": "{{Maps-par|displaymap|title}}",
+ "maps-displaymap-par-label": "{{Maps-par|displaymap|label}}",
+ "maps-displaymap-par-icon": "{{Maps-par|displaymap|icon}}",
+ "maps-displaymap-par-circles": "{{Maps-par|displaymap|circles}}",
+ "maps-displaymap-par-copycoords": "{{Maps-par|displaymap|copycoords}}",
+ "maps-displaymap-par-lines": "{{Maps-par|displaymap|lines}}",
+ "maps-displaymap-par-maxzoom": "{{Maps-par|displaymap|maxzoom}}",
+ "maps-displaymap-par-minzoom": "{{Maps-par|displaymap|minzoom}}",
+ "maps-displaymap-par-polygons": "{{Maps-par|displaymap|polygons}}",
+ "maps-displaymap-par-rectangles": "{{Maps-par|displaymap|rectangles}}",
+ "maps-displaymap-par-static": "{{Maps-par|displaymap|static}}",
+ "maps-displaymap-par-wmsoverlay": "{{Maps-par|displaymap|wmsoverlay}}\n\nWMS stands for [[wikipedia:en:Web_Map_Service|Web Map Service]].",
+ "maps-displaymap-par-geojson": "{{Maps-par|displaymap|geojson}}",
+ "maps-fullscreen-button": "This is the text of a switch.\n\nSee also:\n* {{msg-mw|Maps-fullscreen-button-tooltip}}.",
+ "maps-fullscreen-button-tooltip": "This is the text of a tooltip popup.\n\nSee also:\n* {{msg-mw|Maps-fullscreen-button}}.",
+ "validation-error-invalid-location": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}",
+ "validation-error-invalid-locations": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}",
+ "validation-error-invalid-width": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}",
+ "validation-error-invalid-height": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}",
+ "validation-error-invalid-distance": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}",
+ "validation-error-invalid-distances": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}",
+ "validation-error-invalid-image": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}",
+ "validation-error-invalid-images": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}",
+ "validation-error-invalid-goverlay": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}",
+ "validation-error-invalid-goverlays": "This is an error message.\n\nParameter:\n* $1 - parameter name\n\n{{Related|Maps-validation}}",
+ "maps-abb-north": "Symbol for representing \"north\" in geolocation coordinates.",
+ "maps-abb-east": "Symbol for representing \"east\" in geolocation coordinates.",
+ "maps-abb-south": "Symbol for representing \"south\" in geolocation coordinates.",
+ "maps-abb-west": "Symbol for representing \"west\" in geolocation coordinates.",
+ "maps-latitude": "This is a field label.\n\n{{Identical|Latitude}}",
+ "maps-longitude": "This is a field label.\n\n{{Identical|Longitude}}",
+ "maps-invalid-coordinates": "This is an error message.\n\nParameter:\n* $1 - value",
+ "maps_coordinates_missing": "This is an error message.",
+ "maps_geocoding_failed": "This is an error message.\n\nParameters:\n* $1 - list of addresses\n* $2 - number of addresses",
+ "maps_geocoding_failed_for": "This is an error message.\n\nParameters:\n* $1 - list of items\n* $2 - number of items, for PLURAL support",
+ "maps_unrecognized_coords": "This is an error message.\n\nParameters:\n* $1 - list of coordinates\n* $2 - number of coordinates\n\nSee also:\n* {{msg-mw|Maps unrecognized coords for}}",
+ "maps_unrecognized_coords_for": "This is an error message.\n\nParameters:\n* $1 - list of coordinates\n* $2 - number of coordinates\n\nSee also:\n* {{msg-mw|Maps unrecognized coords}}",
+ "maps_map_cannot_be_displayed": "This is an error message.",
+ "maps-geocoder-not-available": "This is an informatory and/or error message.",
+ "maps_googlemaps3": "Lable for a result format on SMW's special page \"Ask\".\n\n{{optional}}",
+ "maps_leaflet": "This is a field label.\n\n{{optional}}",
+ "maps-leaflet-par-defzoom": "{{maps-par|leaflet|defzoom}}",
+ "maps-leaflet-par-layers": "{{maps-par|leaflet|layers}}",
+ "maps-leaflet-par-overlaylayers": "{{maps-par|leaflet|overlaylayers}}",
+ "maps-leaflet-par-maxclusterradius": "{{maps-par|leaflet|maxclusterradius}}",
+ "maps-leaflet-par-clusterspiderfy": "{{maps-par|leaflet|clusterspiderfy}}",
+ "maps_click_to_activate": "This is an informatory message.",
+ "maps_centred_on": "This is an informatory message.\n\nParameters:\n* $1 - latitude\n* $2 - longitude",
+ "maps-par-mappingservice": "{{maps-par-all|mappingservice}}",
+ "maps-par-resizable": "System message used by several maps services:\n\n{{maps-par|googlemaps3|resizable}}\n{{maps-par|leaflet|resizable}}",
+ "maps-par-searchmarkers": "System message used by serveral maps services:\n\n{{maps-par|googlemaps3|searchmarkers}}\n{{maps-par|leaflet|searchmarkers}}",
+ "maps-par-zoom": "System message used by serveral maps services:\n\n{{maps-par|googlemaps3|zoom}}\n{{maps-par|leaflet|zoom}}",
+ "maps-par-width": "{{maps-par-all|width}}",
+ "maps-par-height": "{{maps-par-all|height}}",
+ "maps-par-centre": "{{maps-par-all|centre}}",
+ "maps-par-enable-fullscreen": "{{maps-par-all|enablefullscreen}}",
+ "maps-par-kml": "System message used by several maps services:\n\n{{maps-par|googlemaps3|kml}}",
+ "maps-par-markercluster": "{{maps-par-all|markercluster}}",
+ "maps-googlemaps3-incompatbrowser": "This is an error message shown to the user instead of a map.",
+ "maps-googlemaps3-par-imageoverlays": "{{maps-par|googlemaps3|imageoverlays}}",
+ "maps-googlemaps3-par-type": "{{maps-par|googlemaps3|type}}",
+ "maps-googlemaps3-par-types": "{{maps-par|googlemaps3|types}}",
+ "maps-googlemaps3-par-layers": "{{maps-par|googlemaps3|layers}}",
+ "maps-googlemaps3-par-controls": "{{maps-par|googlemaps3|controls}}",
+ "maps-googlemaps3-par-zoomstyle": "{{maps-par|googlemaps3|zoomstyle}}",
+ "maps-googlemaps3-par-typestyle": "{{maps-par|googlemaps3|typestyle}}",
+ "maps-googlemaps3-par-autoinfowindows": "{{maps-par|googlemaps3|autoinfowindows}}",
+ "maps-googlemaps3-par-gkml": "{{maps-par|googlemaps3|gkml}}",
+ "maps-googlemaps3-par-kmlrezoom": "{{maps-par|googlemaps3|kmlrezoom}}\n\nKML stands for [[w:Keyhole Markup Language|Keyhole Markup Language]].",
+ "maps-googlemaps3-par-poi": "{{maps-par|googlemaps3|poi}}",
+ "maps-googlemaps3-par-clustergridsize": "{{maps-par|googlemaps3|clustergridsize}}",
+ "maps-par-clustermaxzoom": "{{maps-par|googlemaps3|clustermaxzoom}}",
+ "maps-par-clusterzoomonclick": "{{maps-par|googlemaps3|clusterzoomonclick}}",
+ "maps-par-maxclusterradius": "{{maps-par|googlemaps3|clustermaxradius}}",
+ "maps-googlemaps3-par-clusteraveragecenter": "{{maps-par|googlemaps3|clusteraveragecenter}}",
+ "maps-googlemaps3-par-clusterminsize": "{{maps-par|googlemaps3|clusterminsize}}",
+ "mapeditor": "{{doc-special-group|like=[[Special:MapEditor]]}}",
+ "specialpages-group-maps": "{{doc-special-group|like=[[Special:MapEditor]]}}\n\n{{Identical|Map}}",
+ "mapeditor-parser-error": "This is an error message.",
+ "mapeditor-none-text": "This is an informatory message.\n\n{{Identical|None}}",
+ "mapeditor-done-button": "This is the text on a button.",
+ "mapeditor-remove-button": "This is the text on a button.",
+ "mapeditor-import-button2": "This is the text on a button.\n\n{{Identical|Import}}",
+ "mapeditor-export-button": "This is the text on a button.",
+ "mapeditor-import-button": "This is the text on a button.",
+ "mapeditor-select-button": "This is the text on a button.",
+ "mapeditor-mapparam-button": "This is the text on a button.",
+ "mapeditor-clear-button": "This is the text on a button.",
+ "mapeditor-code-title": "This is the title of a dialogue.",
+ "mapeditor-import-title": "This is the title of a dialogue.",
+ "mapeditor-import-note": "This is an informatory message.",
+ "mapeditor-form-title": "This is the title of a dialogue.",
+ "mapeditor-link-title-switcher-popup-text": "This is the text of a switch with a popup.",
+ "mapeditor-link-title-switcher-link-text": "This is the text for a switch with a link.\n\n{{Identical|Link}}",
+ "mapeditor-form-field-title": "This is and form field name.\n\n{{Identical|Title}}",
+ "mapeditor-form-field-text": "This is and form field name.\n\n{{Identical|Text}}",
+ "mapeditor-form-field-link": "This is and form field name.\n\n{{Identical|Link}}",
+ "mapeditor-form-field-icon": "This is and form field name.\n\n{{Identical|Icon}}",
+ "mapeditor-form-field-group": "This is and form field name.\n\n{{Identical|Group}}",
+ "mapeditor-form-field-inlinelabel": "This is and form field name.",
+ "mapeditor-form-field-strokecolor": "This is and form field name.",
+ "mapeditor-form-field-strokeopacity": "This is and form field name.",
+ "mapeditor-form-field-strokeweight": "This is and form field name.",
+ "mapeditor-form-field-fillcolor": "This is and form field name.\n\n{{Identical|Fill color}}",
+ "mapeditor-form-field-fillopcaity": "This is and form field name.",
+ "mapeditor-form-field-showonhover": "This is the text for a checkbox.",
+ "mapeditor-mapparam-title": "This is the title of a dialogue.",
+ "mapeditor-mapparam-defoption": "This is the default option in a select list.\n\n{{Identical|Select parameter}}",
+ "mapeditor-imageoverlay-button": "This is the text on a button.",
+ "mapeditor-form-field-image": "This is and form field name.\n\n{{Identical|Image}}",
+ "mapeditor-imageoverlay-title": "This is the title of a dialogue.",
+ "mapeditor-form-field-visitedicon": "This is and form field name.",
+ "semanticmaps-unrecognizeddistance": "This is an error message.\n\nParameter:\n* $1 - distance",
+ "semanticmaps-kml-link": "This is the label of a link.",
+ "semanticmaps-kml": "{{optional}}",
+ "semanticmaps-default-kml-pagelink": "Used as default value for \"pagelinktext\" input box.\nSee example: [{{canonicalurl:Special:Ask|format=kml}} Special:Ask]\n\n<code>$1</code> is not a parameter (appears as is), but it will be replaced by the page title.\n\nSee also:\n* {{msg-mw|Semanticmaps-kml-pagelinktext}}\n{{Identical|View page}}",
+ "semanticmaps-latitude": "Parameters:\n* $1 - latitude\nSee also:\n* {{msg-mw|Semanticmaps-longitude}}\n* {{msg-mw|Semanticmaps-altitude}}\n{{Identical|Latitude}}",
+ "semanticmaps-longitude": "Parameters:\n* $1 - longitude\nSee also:\n* {{msg-mw|Semanticmaps-latitude}}\n* {{msg-mw|Semanticmaps-altitude}}\n{{Identical|Longitude}}",
+ "semanticmaps-altitude": "Parameters:\n* $1 - altitude\nSee also:\n* {{msg-mw|Semanticmaps-latitude}}\n* {{msg-mw|Semanticmaps-longitude}}",
+ "semanticmaps-forminput-locations": "This is a button label.\n\n{{Identical|Location}}",
+ "semanticmaps-par-staticlocations": "{{doc-paramdesc|staticlocations}}",
+ "semanticmaps-par-showtitle": "{{doc-paramdesc|showtitle}}",
+ "semanticmaps-par-hidenamespace": "{{doc-paramdesc|hidenamespace}}",
+ "semanticmaps-par-centre": "{{doc-paramdesc|centre}}",
+ "semanticmaps-par-template": "{{doc-paramdesc|template}}",
+ "semanticmaps-par-geocodecontrol": "{{doc-paramdesc|geocodecontrol}}",
+ "semanticmaps-par-activeicon": "{{doc-paramdesc|activeicon}}",
+ "semanticmaps-par-pagelabel": "{{doc-paramdesc|pagelabel}}\n\n{{doc-important|Do not translate the parameter value \"yes\".}}",
+ "semanticmaps-par-ajaxcoordproperty": "{{doc-paramdesc|ajaxcoordproperty}}",
+ "semanticmaps-par-ajaxquery": "{{doc-paramdesc|ajaxquery}}",
+ "semanticmaps-par-userparam": "{{doc-paramdesc|userparam}}",
+ "semanticmaps-kml-text": "{{doc-paramdesc|text}}",
+ "semanticmaps-kml-title": "{{doc-paramdesc|title}}",
+ "semanticmaps-kml-linkabsolute": "{{doc-paramdesc|absolute}}",
+ "semanticmaps-kml-pagelinktext": "Used as description for \"pagelinktext\" input box.\nSee example: [{{canonicalurl:Special:Ask|format=kml}} Special:Ask]\n\n<code>$1</code> is not a parameter, and appears as is.\n\nDefault value for the input box is {{msg-mw|Semanticmaps-default-kml-pagelink}}.",
+ "semanticmaps-shapes-improperformat": "This is an error message.\n\nParameter:\n* $1 - improper text",
+ "semanticmaps-shapes-missingshape": "This is an error message.\n\nParameter:\n* $1 - name of the shape",
+ "validator-type-mapscircle": "This is the name of a type of values that may be assigned to a parameter.",
+ "validator-type-mapscircle-list": "This is the name of a type of values that may be assigned to a parameter.",
+ "validator-type-mapsimageoverlay": "This is the name of a type of values that may be assigned to a parameter.",
+ "validator-type-mapsimageoverlay-list": "This is the name of a type of values that may be assigned to a parameter.",
+ "validator-type-mapsline": "This is the name of a type of values that may be assigned to a parameter.",
+ "validator-type-mapsline-list": "This is the name of a type of values that may be assigned to a parameter.",
+ "validator-type-mapslocation": "This is the name of a type of values that may be assigned to a parameter.",
+ "validator-type-mapslocation-list": "This is the name of a type of values that may be assigned to a parameter.",
+ "validator-type-mapsrectangle": "This is the name of a type of values that may be assigned to a parameter.",
+ "validator-type-mapsrectangle-list": "This is the name of a type of values that may be assigned to a parameter.",
+ "validator-type-wmsoverlay": "This is the name of a type of values that may be assigned to a parameter. WMS stands for [[wikipedia:en:Web_Map_Service|Web Map Service]].",
+ "validator-type-jsonfile": "{{Identical|Text}}"
+}
diff --git a/www/wiki/extensions/Maps/i18n/ro.json b/www/wiki/extensions/Maps/i18n/ro.json
new file mode 100644
index 00000000..88911cdb
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ro.json
@@ -0,0 +1,96 @@
+{
+ "@metadata": {
+ "authors": [
+ "Firilacroco",
+ "KlaudiuMihaila",
+ "Minisarm",
+ "Stelistcristi",
+ "Macofe"
+ ]
+ },
+ "maps-desc": "Permite încorporarea hărților dinamice în paginile wiki, precum și geocodificarea adreselor ori efectuarea altor operațiuni de ordin geografic",
+ "right-geocode": "Geocod",
+ "maps_map": "Hartă",
+ "maps-loading-map": "Se încarcă harta...",
+ "maps-load-failed": "Imposibil de încărcat harta!",
+ "maps-markers": "Marcatori",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "Marcaje pentru filtre",
+ "maps-others": "altele",
+ "maps-ns-layer": "Strat",
+ "maps-layer-property": "Proprietate",
+ "maps-layer-value": "Valoare",
+ "maps-layer-errors": "Erori",
+ "maps-error-invalid-layertype": "Nu există straturi de tip „$1”. Se acceptă doar {{PLURAL:$3|acest tip|aceste tipuri}}: $2",
+ "maps-error-no-layertype": "Trebuie să specificați tipul stratului. Se acceptă doar {{PLURAL:$2|acest tip|aceste tipuri}}: $1",
+ "validation-error-invalid-layer": "Parametrul $1 trebuie să fie un strat valabil.",
+ "validation-error-invalid-layers": "Parametrul $1 trebuie să fie una sau mai multe straturi valide.",
+ "maps-layer-of-type": "Strat de tip $1",
+ "maps-finddestination-par-location": "Amplasare inițială.",
+ "maps-displaymap-par-circles": "Cercuri de afișat",
+ "maps-displaymap-par-lines": "Linii de afișat",
+ "maps-displaymap-par-maxzoom": "Nivelul maxim de apropiere",
+ "maps-displaymap-par-minzoom": "Nivelul minim de apropiere",
+ "maps-displaymap-par-polygons": "Poligoane de afișat",
+ "maps-displaymap-par-rectangles": "Dreptunghiuri de afișat",
+ "validation-error-invalid-location": "Parametrul $1 trebuie să fie o locaţie validă.",
+ "validation-error-invalid-locations": "Parametrul $1 trebuie să fie una sau mai multe locaţii valide.",
+ "validation-error-invalid-width": "Parametrul $1 trebuie să fie o lățime validă.",
+ "validation-error-invalid-height": "Parametrul $1 trebuie să fie o înălțime validă.",
+ "validation-error-invalid-distance": "Parametrul $1 trebuie să reprezinte o distanță validă.",
+ "validation-error-invalid-distances": "Parametrul $1 trebuie să fie una sau mai multe distanțe valide.",
+ "validation-error-invalid-image": "Parametrul $1 trebuie să fie o imagine validă.",
+ "validation-error-invalid-images": "Parametrul $1 trebuie să fie una sau mai multe imagini valide.",
+ "validation-error-invalid-goverlay": "Parametrul $1 trebuie să fie o suprapunere validă.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "V",
+ "maps-latitude": "Latitudine:",
+ "maps-longitude": "Longitudine:",
+ "maps-invalid-coordinates": "Valoarea $1 nu a fost recunoscută ca un set valid de coordonate.",
+ "maps_coordinates_missing": "Nu au fost furnizate coordonate pentru hartă.",
+ "maps_geocoding_failed": "{{PLURAL:$2|Următoarea|Următoarele}} {{PLURAL:$2|adresă|adrese}} nu {{PLURAL:$2|a|au}} putut fi {{PLURAL:$2|geocodificată|geocodificate}}: $1.\nHarta nu poate fi afișată.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|Următoarea|Următoarele}} {{PLURAL:$2|adresă|adrese}} nu {{PLURAL:$2|a|au}} putut fi {{PLURAL:$2|geocodificată|geocodificate}} și {{PLURAL:$2|a|au}} fost {{PLURAL:$2|omisă|omise}} de pe hartă:\n$1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|Următorul|Următoarele}} {{PLURAL:$2|set|seturi}} de coordonate nu {{PLURAL:$2|a|au}} putut fi {{PLURAL:$2|recunoscut|recunoscute}}: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|Următorul|Următoarele}} {{PLURAL:$2|set|seturi}} de coordonate nu {{PLURAL:$2|a|au}} putut fi {{PLURAL:$2|recunoscut|recunoscute}} și {{PLURAL:$2|a|au}} fost {{PLURAL:$2|omis|omise}}: $1",
+ "maps_map_cannot_be_displayed": "Harta nu poate fi afișată.",
+ "maps-geocoder-not-available": "Opțiunea de geocodare pentru Hărți nu este disponibilă. Locația dumneavoastră nu a putut fi geocodată.",
+ "maps_click_to_activate": "Apăsați pentru a activa harta",
+ "maps_centred_on": "Hartă centrată la $1, $2.",
+ "mapeditor": "Editor de hărți",
+ "specialpages-group-maps": "Hărți",
+ "mapeditor-none-text": "Nimic",
+ "mapeditor-done-button": "Realizat",
+ "mapeditor-remove-button": "Elimină",
+ "mapeditor-import-button2": "Importă",
+ "mapeditor-export-button": "Exportare în cod wiki",
+ "mapeditor-import-button": "Importare din cod wiki",
+ "mapeditor-select-button": "Selectează acest poligon",
+ "mapeditor-mapparam-button": "Modifică parametrii hărții",
+ "mapeditor-clear-button": "Curăță harta",
+ "mapeditor-code-title": "Cod wiki",
+ "mapeditor-import-title": "Importare cod wiki",
+ "mapeditor-form-title": "Modificare detalii",
+ "mapeditor-link-title-switcher-popup-text": "Fereastră popup cu text",
+ "mapeditor-link-title-switcher-link-text": "Legătură",
+ "mapeditor-form-field-title": "Titlu",
+ "mapeditor-form-field-text": "Text",
+ "mapeditor-form-field-link": "Legătură",
+ "mapeditor-form-field-icon": "Pictogramă",
+ "mapeditor-form-field-group": "Grup",
+ "mapeditor-form-field-inlinelabel": "Etichetă în linie",
+ "mapeditor-form-field-strokecolor": "Culoarea conturului",
+ "mapeditor-form-field-strokeopacity": "Opacitatea conturului",
+ "mapeditor-form-field-strokeweight": "Grosimea conturului",
+ "mapeditor-form-field-fillcolor": "Culoarea umplerii",
+ "mapeditor-form-field-fillopcaity": "Opacitatea umplerii",
+ "mapeditor-form-field-showonhover": "Arată numai când mausul este deasupra",
+ "mapeditor-mapparam-title": "Modificare parametrii hartă",
+ "mapeditor-mapparam-defoption": "-Alegeți parametrul-",
+ "mapeditor-imageoverlay-button": "Adaugă suprapunerea de imagine",
+ "mapeditor-form-field-image": "Imagine",
+ "mapeditor-imageoverlay-title": "Detalii suprapunere imagine",
+ "mapeditor-form-field-visitedicon": "Pictogramă vizitată",
+ "semanticmaps-forminput-locations": "Locuri"
+}
diff --git a/www/wiki/extensions/Maps/i18n/roa-tara.json b/www/wiki/extensions/Maps/i18n/roa-tara.json
new file mode 100644
index 00000000..5fa0db84
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/roa-tara.json
@@ -0,0 +1,65 @@
+{
+ "@metadata": {
+ "authors": [
+ "Joetaras",
+ "Macofe"
+ ]
+ },
+ "maps-desc": "Abbilite 'u 'ngapsulamende de le mappe dinameche jndr'à le pàggene de Uicchi, geocodificate e indirizzate a otre operaziune sciugrafeche",
+ "right-geocode": "Geocodece",
+ "maps_map": "Mappe",
+ "maps-loading-map": "Stoche a careche 'a mappe...",
+ "maps-load-failed": "Non ge pozze carecà 'a mappe!",
+ "maps-markers": "Marcature",
+ "maps-copycoords-prompt": "CTRL+C, INVIE",
+ "maps-others": "otre",
+ "maps-ns-layer": "Strate",
+ "maps-layer-property": "Probbietà",
+ "maps-layer-value": "Valore",
+ "maps-layer-errors": "Errore",
+ "maps-displaymap-par-circles": "Circole da fà 'ndrucà",
+ "maps-displaymap-par-maxzoom": "Levèlle de zoom massime",
+ "maps-displaymap-par-minzoom": "Levèlle de zoom minime",
+ "maps-displaymap-par-polygons": "Poligoni da fà vedè",
+ "maps-displaymap-par-rectangles": "Rettangole da fà vedè",
+ "validation-error-invalid-location": "'U parametre \"$1\" adda essere 'na posiziona valide.",
+ "validation-error-invalid-locations": "'U parametre \"$1\" adda essere une o cchiù posiziune valide.",
+ "validation-error-invalid-width": "'U parametre \"$1\" adda essere 'na larghezza valide.",
+ "validation-error-invalid-height": "'U parametre \"$1\" adda essere 'n'altezza valide.",
+ "validation-error-invalid-distance": "'U parametre \"$1\" adda essere 'na ditanza valide.",
+ "validation-error-invalid-distances": "'U parametre \"$1\" adda essere une o cchiù distanze valide.",
+ "validation-error-invalid-image": "'U parametre \"$1\" adda essere 'n'immaggine valide.",
+ "validation-error-invalid-images": "'U parametre \"$1\" adda essere une o cchiù immaggine valide.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "E",
+ "maps-abb-south": "S",
+ "maps-abb-west": "O",
+ "maps-latitude": "Latitudine:",
+ "maps-longitude": "Longitudine:",
+ "specialpages-group-maps": "Mappe",
+ "mapeditor-none-text": "Ninde",
+ "mapeditor-done-button": "Fatte",
+ "mapeditor-remove-button": "Live",
+ "mapeditor-import-button2": "'Mborte",
+ "mapeditor-link-title-switcher-link-text": "Collegamende",
+ "mapeditor-form-field-title": "Titole",
+ "mapeditor-form-field-text": "Teste",
+ "mapeditor-form-field-link": "Collegamende",
+ "mapeditor-form-field-icon": "Icone",
+ "mapeditor-form-field-group": "Gruppe",
+ "mapeditor-form-field-image": "Immaggine",
+ "semanticmaps-kml-link": "'Ndruche 'u file KML",
+ "semanticmaps-default-kml-pagelink": "Vide 'a pàgene $1",
+ "semanticmaps-latitude": "Latitudine: $1",
+ "semanticmaps-longitude": "Longitudine: $1",
+ "semanticmaps-altitude": "Iertezze: $1",
+ "semanticmaps-forminput-locations": "Luèche",
+ "validator-type-mapsline": "Linèe sciugrafeche",
+ "validator-type-mapsline-list": "Elenghe de linèe",
+ "validator-type-mapslocation": "Posizione sciugrafeche",
+ "validator-type-mapslocation-list": "Elenghe de le posiziune",
+ "validator-type-mapsrectangle": "Rettangole sciugrafeche",
+ "validator-type-mapsrectangle-list": "Elenghe de le rettangole",
+ "validator-type-mapspolygon": "Poligone sciugrafeche",
+ "validator-type-mapspolygon-list": "Elenghe de le poligone sciugrafece"
+}
diff --git a/www/wiki/extensions/Maps/i18n/ru.json b/www/wiki/extensions/Maps/i18n/ru.json
new file mode 100644
index 00000000..ac387d8e
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ru.json
@@ -0,0 +1,251 @@
+{
+ "@metadata": {
+ "authors": [
+ "Adata80",
+ "Base",
+ "DCamer",
+ "Haffman",
+ "KPu3uC B Poccuu",
+ "Kaganer",
+ "Kalan",
+ "Lockal",
+ "MaxSem",
+ "McDutchie",
+ "Okras",
+ "Yuriy Apostol",
+ "Александр Сигачёв",
+ "Macofe",
+ "Perevod16",
+ "Alexandr Efremov",
+ "Cat1987",
+ "Туллук",
+ "Pastakhov",
+ "Putnik",
+ "Smigles",
+ "Patrick Star",
+ "Happy13241",
+ "Movses",
+ "Ole Yves",
+ "Stjn",
+ "Vlad5250",
+ "Shaleych",
+ "NBS",
+ "Diralik"
+ ]
+ },
+ "maps-desc": "Позволяет встраивать динамические карты в вики-страницы, геокодировать адреса и выполнять другие географические действия",
+ "right-geocode": "Геокодирование",
+ "action-geocode": "выполнение геокодирования в этой вики",
+ "maps_map": "Карта",
+ "maps-tracking-category": "Страницы с картой, сгенерированной расширением Maps",
+ "maps-loading-map": "Идёт загрузка карты…",
+ "maps-load-failed": "Невозможно загрузить карту!",
+ "maps-markers": "Маркеры",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "Фильтр маркеров",
+ "maps-others": "другие",
+ "maps-kml-parsing-failed": "Не удалось выполнить разбор одного или нескольких KML-файлов, обычно вследствие отказа в получении или некорректного XML.",
+ "maps-ns-layer": "Слой",
+ "maps-ns-layer-talk": "Обсуждение слоя",
+ "maps-layer-property": "Свойство",
+ "maps-layer-value": "Значение",
+ "maps-layer-errors": "Ошибки",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Недопустимое определение|Недопустимые определения}}",
+ "maps-layerdef-invalid-fatal": "Фатальное недопустимое определение",
+ "maps-layerdef-wrong-namespace": "Определения слоёв действительны только на страницах пространства имён «$1»",
+ "maps-layerdef-equal-layer-name": "Имена слоёв должны быть уникальными в пределах одной и той же страницы слоёв. «$1» уже используется другим слоем.",
+ "maps-layerpage-usage": "Страницы с картами, использующие слой «$1»",
+ "maps-layerpage-nousage": "Нет страниц, использующих этот слой в данный момент.",
+ "maps-error-invalid-layertype": "Не существует слоя типа «$1». {{PLURAL:$3|1=Поддерживается только следующий тип|Поддерживаются только следующие типы}}: $2",
+ "maps-error-no-layertype": "Вам нужно указать тип слоя. {{PLURAL:$2|1=Поддерживается только следующий тип|Поддерживаются следующие типы}}: $1",
+ "validation-error-invalid-layer": "Параметр $1 должен быть корректным слоем.",
+ "validation-error-invalid-layers": "Параметр $1 должен содержать один или несколько корректных слоёв.",
+ "validation-error-no-non-numeric": "Параметр «$1» должен быть нецифровой строкой.",
+ "validation-error-no-non-numerics": "Параметр «$1» должен быть одной или несколькими нецифровыми строками.",
+ "maps-layer-of-type": "Слой типа $1",
+ "maps-layer-of-type-and-name": "Слой «$2» типа «$1»",
+ "maps-layer-type-supported-by": "Этот тип слоя может быть использован {{PLURAL:$2|1=только с картографической службой|только со следующими картографическими службами:}} $1",
+ "maps-coordinates-description": "Перехватчик синтаксического анализатора для форматирования координат из любого и в любой поддерживаемый формат.",
+ "maps-displaymap-description": "Отображение географических карт без каких-либо вики-маркеров на них.",
+ "maps-distance-description": "Преобразование расстояния, выраженного в определенных поддерживаемых единицах, в его эквивалент в других единицах.",
+ "maps-finddestination-description": "Найти место назначения от заданной начальной точки (может быть в любом формате из поддерживаемых), начальное направление и расстояние.",
+ "maps-geocode-description": "Включает геокодирование адресов. Иными словами, преобразует понятные человеку названия мест в наборы координат. Поддерживается несколько сервисов геокодирования, которые не следует путать с картографическими сервисами.",
+ "maps-geodistance-description": "Рассчитать географическое расстояние между двумя точками, из любого и в любой поддерживаемый формат.",
+ "maps-mapsdoc-description": "Показывает таблицу с параметрами для указанного картографического сервиса вместе с их значениями по умолчанию и описаниями.",
+ "maps-layerdefinition-description": "Описывает пользовательский слой, который может отображаться с другими функциями карты.",
+ "maps-mapsdoc-par-service": "Картографический сервис, для которого отображать документацию по параметрам.",
+ "maps-mapsdoc-par-language": "Язык, на котором отображается документация. Если перевод не доступен, вместо него будет использоваться английский язык.",
+ "maps-coordinates-par-location": "Координаты, которые требуется отформатировать.",
+ "maps-coordinates-par-format": "Целевой формат координат.",
+ "maps-coordinates-par-directional": "Указывает, должны ли координаты выводиться непосредственно или нет.",
+ "maps-par-scrollwheelzoom": "Отображает, включена ли прокрутка мышью.",
+ "maps-distance-par-distance": "Расстояние для преобразования в эквивалентное в указанных единицах.",
+ "maps-distance-par-decimals": "Максимальное количество цифр дробной части в результирующем значении.",
+ "maps-distance-par-unit": "Единицы, в которых будет отображено расстояние.",
+ "maps-finddestination-par-location": "Исходное положение.",
+ "maps-finddestination-par-bearing": "Начальное направление.",
+ "maps-finddestination-par-distance": "Протяжённость путешествия.",
+ "maps-finddestination-par-format": "Формат, в котором отображается пункт назначения.",
+ "maps-finddestination-par-directional": "Указывает, должен ли формат назначения быть направленным или нет.",
+ "maps-geocode-par-location": "Адрес, который вы желаете геокодировать.",
+ "maps-geocode-par-format": "Формат получаемых координат.",
+ "maps-geocode-par-directional": "Указывает, должны ли координаты выводиться непосредственно или нет.",
+ "maps-geodistance-par-location1": "Первая точка в наборе для расчёта расстояния.",
+ "maps-geodistance-par-location2": "Вторая точка в наборе для расчёта расстояния.",
+ "maps-geodistance-par-unit": "Единица измерения расстояния.",
+ "maps-geodistance-par-decimals": "Наибольшее количество цифр в дробной части результата.",
+ "maps-displaymap-par-mappingservice": "Позволяет выбрать сервис карт, который будет использоваться.",
+ "maps-displaymap-par-coordinates": "Одно или несколько мест для отображения на карте. Они будут обозначаться маркером.",
+ "maps-displaymap-par-visitedicon": "Имя файла для изображения, которое будет использоваться для значков-маркеров после того, как на сами маркеры нажмут",
+ "maps-displaymap-par-zoom": "Позволяет задать уровень масштабирования карты.\nКогда не указан, и на карте присутствуют несколько маркеров, будет выбран наиболее подходящий масштаб, а не тот, что настроен по умолчанию.",
+ "maps-displaymap-par-centre": "Позволяет задать координаты центра карты для display_point.\nПозволяет использовать и адреса, и координаты.\nКогда это свойство не задано, карта будет центрироваться сама по предоставленному маркеру или между предоставленных маркеров.",
+ "maps-displaymap-par-title": "Позволяет задать текст, который будет отображаться во всплывающих окнах всех маркеров, у которых нет определенного заголовка.\nПри использовании совместно с подписью, название будет выделено жирным шрифтом и подчеркнуто.",
+ "maps-displaymap-par-label": "Позволяет задать текст, который будет отображаться во всплывающих окнах всех маркеров, у которых нет определенных подписей.",
+ "maps-displaymap-par-icon": "Позволяет установить значок, используемый для всех маркеров.",
+ "maps-displaymap-par-circles": "Кружки для отображения",
+ "maps-displaymap-par-copycoords": "Показывать ли при нажатии на место диалоговое окно, из которого могут быть скопированы его координаты",
+ "maps-displaymap-par-lines": "Линии для отображения",
+ "maps-displaymap-par-maxzoom": "Максимальный уровень масштабирования",
+ "maps-displaymap-par-minzoom": "Минимальный уровень масштабирования",
+ "maps-displaymap-par-polygons": "Полигоны для отображения",
+ "maps-displaymap-par-rectangles": "Прямоугольники для отображения",
+ "maps-displaymap-par-static": "Сделать карту статичной",
+ "maps-displaymap-par-wmsoverlay": "Использовать слой WMS",
+ "maps-displaymap-par-geojson": "URL файла или название страницы, содержащей данные GeoJSON",
+ "maps-fullscreen-button": "Переключить полноэкранный режим",
+ "maps-fullscreen-button-tooltip": "Посмотреть карту в полноэкранном или встроенном режиме.",
+ "validation-error-invalid-location": "Параметр $1 должен быть корректным местоположением.",
+ "validation-error-invalid-locations": "Параметр $1 должен содержать одно или несколько корректных местоположений.",
+ "validation-error-invalid-width": "Параметр $1 должен быть корректной шириной.",
+ "validation-error-invalid-height": "Параметр $1 должен быть корректной высотой.",
+ "validation-error-invalid-distance": "Параметр $1 должен быть корректным расстоянием.",
+ "validation-error-invalid-distances": "Параметр $1 должен содержать одно или несколько корректных расстояний.",
+ "validation-error-invalid-image": "Параметр $1 должен быть корректным изображением.",
+ "validation-error-invalid-images": "Параметр $1 должен содержать одно или несколько корректных изображений.",
+ "validation-error-invalid-goverlay": "Параметр $1 должен быть корректным наложением.",
+ "validation-error-invalid-goverlays": "Параметр $1 должен содержать одно или несколько корректных наложений.",
+ "maps-abb-north": "С",
+ "maps-abb-east": "В",
+ "maps-abb-south": "Ю",
+ "maps-abb-west": "З",
+ "maps-latitude": "Широта:",
+ "maps-longitude": "Долгота:",
+ "maps-invalid-coordinates": "Значение $1 не признано допустимым набором координат.",
+ "maps_coordinates_missing": "Не указаны координаты для карты.",
+ "maps_geocoding_failed": "{{PLURAL:$2|1=Следующий адрес не может быть геокодирован|Следующие адреса не могут быть геокодированы}}: $1.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|1=Следующий адрес не может быть геокодирован и был удалён|Следующие адреса не могут быть геокодированы и были удалены}} с карты:\n$1",
+ "maps_unrecognized_coords": "{{PLURAL:$2|1=Следующая пара координат не была опознана|Следующие координаты не были опознаны}}: $1.",
+ "maps_unrecognized_coords_for": "{{PLURAL:$2|1=Следующая пара координат не была опознана, она не показана|Следующие координаты не были опознаны, они не показаны}} на карте:\n$1",
+ "maps_map_cannot_be_displayed": "Карта не может быть показана.",
+ "maps-geocoder-not-available": "Функция геокодирования карт недоступна, ваше местоположение не может быть геокодировано.",
+ "maps_leaflet": "Листовка",
+ "maps-leaflet-par-defzoom": "Позволяет задавать масштаб карты по умолчанию.",
+ "maps-leaflet-par-layers": "Слои, которые будут доступны при выборе. Первый слой будет отображаться при загрузке карты.",
+ "maps-leaflet-par-overlaylayers": "Слои подложки, которые будут показаны при загрузке карты.",
+ "maps-leaflet-par-maxclusterradius": "Максимальный радиус, который кластер будет покрывать от центрального маркера (в пикселях).",
+ "maps-leaflet-par-clusterspiderfy": "Когда вы нажимаете на кластер на нижнем уровне масштаба, то он разворачивается таким образом, чтобы вы могли увидеть все его маркеры.",
+ "maps_click_to_activate": "Нажмите для активации карты",
+ "maps_centred_on": "Центр карты — $1, $2.",
+ "maps-par-mappingservice": "Позволяет выбрать сервис карт, который будет использоваться.",
+ "maps-par-resizable": "Даёт возможность изменять размер карты, потащив её за правый нижний угол.",
+ "maps-par-searchmarkers": "Позволяет искать определённые маркеры с помощью поля, встроенного в карту.",
+ "maps-par-zoom": "Уровень масштабирования карты. Для карт с маркерами это будет по умолчанию наиболее приближенный уровень, при котором всё ещё будут видны все маркеры.",
+ "maps-par-width": "Позволяет задать ширину карты. По умолчанию единицей измерения будет считаться пиксель, но можно явно указать одну из следующих единиц: px, ex, em, %.",
+ "maps-par-height": "Позволяет задать высоту карты. По умолчанию единицей измерения будет считаться пиксель, но можно явно указать одну из следующих единиц: px, ex, em, %.",
+ "maps-par-centre": "Расположение на карте, по которому она должна быть отцентрована",
+ "maps-par-enable-fullscreen": "Включить кнопку полноэкранного режима",
+ "maps-par-kml": "KML файлы для загрузки на карту.",
+ "maps-par-markercluster": "Позволяет объединить несколько находящихся рядом маркеров в один маркер",
+ "maps-googlemaps3-incompatbrowser": "Ваш браузер несовместим с Google Maps v3.",
+ "maps-googlemaps3-par-imageoverlays": "Позволяет добавить изображение, которое будет показано в определённом месте карты.",
+ "maps-googlemaps3-par-type": "Тип карты для начального отображения.",
+ "maps-googlemaps3-par-types": "Типы карты, которые будут доступны через элемент управления типом карты.",
+ "maps-googlemaps3-par-layers": "Специальные слои для загрузки на карту.",
+ "maps-googlemaps3-par-controls": "Элементы управления для размещения на карте.",
+ "maps-googlemaps3-par-zoomstyle": "Стиль элемента управления масштабом.",
+ "maps-googlemaps3-par-typestyle": "Стиль элемента управления типа.",
+ "maps-googlemaps3-par-autoinfowindows": "Автоматически открывает все информационные окна после загрузки страницы.",
+ "maps-googlemaps3-par-gkml": "KML файлы, хранящиеся в Google для загрузки на карту.",
+ "maps-googlemaps3-par-kmlrezoom": "Перемасштабировать карту после загрузки слоёв KML.",
+ "maps-googlemaps3-par-poi": "Показать достопримечательности.",
+ "maps-googlemaps3-par-clustergridsize": "Размер сетки кластера в пикселях.",
+ "maps-par-clustermaxzoom": "Максимальный уровень увеличения, при котором может существовать кластер.",
+ "maps-par-clusterzoomonclick": "Является ли увеличение кластера поведением по умолчанию при нажатии.",
+ "maps-par-maxclusterradius": "Максимальный радиус, охватываемый кластером.",
+ "maps-googlemaps3-par-clusteraveragecenter": "Должен ли центр каждого кластера быть средним по всем маркерам в нём.",
+ "maps-googlemaps3-par-clusterminsize": "Минимальное число маркеров в кластере, начиная с которого маркеры будут скрыты, а вместо них будет показано количество маркеров.",
+ "mapeditor": "Редактор карт",
+ "specialpages-group-maps": "Карты",
+ "mapeditor-parser-error": "Произошла ошибка обработки метаданных. Введённые данные проигнорированы.",
+ "mapeditor-none-text": "Пусто",
+ "mapeditor-done-button": "Готово",
+ "mapeditor-remove-button": "Удалить",
+ "mapeditor-import-button2": "Импортировать",
+ "mapeditor-export-button": "Экспортировать в вики-текст",
+ "mapeditor-import-button": "Импортировать из викитекста",
+ "mapeditor-select-button": "Выберите этот полигон",
+ "mapeditor-mapparam-button": "Править параметры карты",
+ "mapeditor-clear-button": "Очистить карту",
+ "mapeditor-code-title": "Вики-текст",
+ "mapeditor-import-title": "Импорт викитекста",
+ "mapeditor-import-note": "Обратите внимание, что обработчик ожидает очень строго отформатированный вики-текст. Вики-текст, введённый здесь, должен точно соответствовать тому вики-тексту, который был сгенерирован при экспорте.",
+ "mapeditor-form-title": "Правка деталей",
+ "mapeditor-link-title-switcher-popup-text": "Всплывающая подсказка",
+ "mapeditor-link-title-switcher-link-text": "Ссылка",
+ "mapeditor-form-field-title": "Название",
+ "mapeditor-form-field-text": "Текст",
+ "mapeditor-form-field-link": "Ссылка",
+ "mapeditor-form-field-icon": "Иконка",
+ "mapeditor-form-field-group": "Группа",
+ "mapeditor-form-field-inlinelabel": "Текстовая метка",
+ "mapeditor-form-field-strokecolor": "Цвет контура",
+ "mapeditor-form-field-strokeopacity": "Прозрачность контура",
+ "mapeditor-form-field-strokeweight": "Толщина контура",
+ "mapeditor-form-field-fillcolor": "Цвет заливки",
+ "mapeditor-form-field-fillopcaity": "Прозрачность заливки",
+ "mapeditor-form-field-showonhover": "Показывать только при наведении",
+ "mapeditor-mapparam-title": "Править параметры карты",
+ "mapeditor-mapparam-defoption": "-Выберите параметр-",
+ "mapeditor-imageoverlay-button": "Добавить наложение изображения",
+ "mapeditor-form-field-image": "Изображение",
+ "mapeditor-imageoverlay-title": "Изображение",
+ "mapeditor-form-field-visitedicon": "Иконка для посещённого",
+ "semanticmaps-unrecognizeddistance": "Значение $1 — это недопустимое расстояние.",
+ "semanticmaps-kml-link": "Просмотреть файл KML",
+ "semanticmaps-default-kml-pagelink": "Просмотреть страницу $1",
+ "semanticmaps-latitude": "Широта: $1",
+ "semanticmaps-longitude": "Долгота: $1",
+ "semanticmaps-altitude": "Высота: $1",
+ "semanticmaps-forminput-locations": "Места",
+ "semanticmaps-par-staticlocations": "Список мест для добавления на карту вместе с запрашиваемыми данными. Например, к display_points можно добавить название, описание и значок, используя тильду ~ в качестве разделителя.",
+ "semanticmaps-par-showtitle": "Показывать или нет заголовок в окне информации маркера. Его отключение часто бывает полезно при использовании шаблона для форматирования содержимого окна информация.",
+ "semanticmaps-par-hidenamespace": "Показывать название пространства имён в информационном окне маркера",
+ "semanticmaps-par-centre": "Центр карты. Если не задан, карта автоматически выберет оптимальный центр, позволяющий отобразить все маркеры на карте.",
+ "semanticmaps-par-template": "Шаблон для форматирования содержимого окна информация.",
+ "semanticmaps-par-geocodecontrol": "Показать панель управления геокодированием.",
+ "semanticmaps-par-activeicon": "Значок, который будет отображаться вместо стандартного маркера в случаях, когда активная страница совпадает с результатом запроса",
+ "semanticmaps-par-pagelabel": "Если задано значение «yes» («да»), все маркеры будут иметь «inlineLabel» со ссылкой на страницу, содержащую координаты для маркера",
+ "semanticmaps-par-ajaxcoordproperty": "Имя свойства координат, которое используется для построения ajax-запроса.",
+ "semanticmaps-par-ajaxquery": "Второй запрос, который отправляется через ajax для извлечения дополнительных координат.",
+ "semanticmaps-par-userparam": "Значение, которое передается за каждый вызов шаблона, если шаблон используется",
+ "semanticmaps-kml-text": "Текст, связанный с каждой страницы. Переопределяется дополнительными запрашиваемыми свойствами, если таковые имеются.",
+ "semanticmaps-kml-title": "Заголовок по умолчанию для результатов",
+ "semanticmaps-kml-linkabsolute": "Ссылки должны быть абсолютными (а не относительными)",
+ "semanticmaps-kml-pagelinktext": "Текст, используемый для ссылки на страницу, в которой $1 будет заменён названием страницы.",
+ "semanticmaps-shapes-improperformat": "Неправильное форматирование $1. Обратитесь к документации по форматированию.",
+ "semanticmaps-shapes-missingshape": "Фигуры для $1 не найдены. Пожалуйста, обратитесь к документации по доступным фигурам",
+ "validator-type-mapscircle": "Географический круг",
+ "validator-type-mapscircle-list": "Список кругов",
+ "validator-type-mapsimageoverlay": "Наложение изображений",
+ "validator-type-mapsimageoverlay-list": "Список наложений изображений",
+ "validator-type-mapsline": "Географическая линия",
+ "validator-type-mapsline-list": "Список линий",
+ "validator-type-mapslocation": "Географическое положение",
+ "validator-type-mapslocation-list": "Список местоположений",
+ "validator-type-mapsrectangle": "Географический прямоугольник",
+ "validator-type-mapsrectangle-list": "Список прямоугольников",
+ "validator-type-mapspolygon": "Географический полигон",
+ "validator-type-mapspolygon-list": "Перечень географических полигонов",
+ "validator-type-wmsoverlay": "Оверлей веб-карты",
+ "validator-type-jsonfile": "текст"
+}
diff --git a/www/wiki/extensions/Maps/i18n/rue.json b/www/wiki/extensions/Maps/i18n/rue.json
new file mode 100644
index 00000000..e5ab3801
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/rue.json
@@ -0,0 +1,13 @@
+{
+ "@metadata": {
+ "authors": [
+ "Gazeb"
+ ]
+ },
+ "maps_map": "Мапа",
+ "maps-loading-map": "Награваня мапы. . .",
+ "maps-abb-north": "С",
+ "maps-abb-east": "В",
+ "maps-abb-south": "Ю",
+ "maps-abb-west": "З"
+}
diff --git a/www/wiki/extensions/Maps/i18n/sco.json b/www/wiki/extensions/Maps/i18n/sco.json
new file mode 100644
index 00000000..9503059b
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/sco.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "John Reid"
+ ]
+ },
+ "maps-geodistance-par-geoservice": "The mapping service this parser function is uised thegather wi.\nThis can affect the default geocoding service value.",
+ "maps-displaymap-par-visitedicon": "The filename o aen eemage tae be uised fer maurker icons efter the oreeginal maurkers hae been clap't"
+}
diff --git a/www/wiki/extensions/Maps/i18n/si.json b/www/wiki/extensions/Maps/i18n/si.json
new file mode 100644
index 00000000..03bef201
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/si.json
@@ -0,0 +1,109 @@
+{
+ "@metadata": {
+ "authors": [
+ "තඹරු විජේසේකර",
+ "පසිඳු කාවින්ද"
+ ]
+ },
+ "right-geocode": "භූකේතය",
+ "maps_map": "සිතියම",
+ "maps-loading-map": "සිතියම පුරණය වෙමින් පවතී...",
+ "maps-load-failed": "සිතියම පූරණය කල නොහැක!",
+ "maps-markers": "සලකුණු කාරක",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "පෙරහන් සලකුණු කාරක",
+ "maps-others": "අනෙකුන්",
+ "maps-ns-layer": "ස්තරය",
+ "maps-ns-layer-talk": "ස්තර කතබහ",
+ "maps-layer-property": "ගුණාංගය",
+ "maps-layer-value": "අගය",
+ "maps-layer-errors": "දෝෂ",
+ "validation-error-invalid-layer": "$1 පරාමිතිය වලංගු ස්ථරයක් විය යුතුය.",
+ "validation-error-invalid-layers": "$1 පරාමිතිය වලංගු එක් හෝ තවත් ස්ථරයක් විය යුතුය.",
+ "maps-layer-of-type": "$1 වර්ගයේ ස්තරය",
+ "maps-coordinates-par-location": "ඔබට ආකෘතිකරණය කල යුතු ඛණ්ඩාංක.",
+ "maps-coordinates-par-format": "ඛණ්ඩාංක සඳහා ඉලක්කගත ආකෘතිය.",
+ "maps-finddestination-par-location": "ආරම්භක ස්ථානය.",
+ "maps-finddestination-par-bearing": "ආරම්භක දිගංශය.",
+ "maps-finddestination-par-distance": "ගමන් කිරීමට ඇති දුර.",
+ "maps-finddestination-par-format": "ගමනාන්තය ප්‍රතිදානය කල යුතු ආකෘතිය.",
+ "maps-geocode-par-location": "ඔබට භූකේතීකරණය කල යුතු ලිපිනය.",
+ "maps-geocode-par-format": "එලිත ඛණ්ඩාංක සඳහා ආකෘතිය.",
+ "maps-displaymap-par-circles": "පෙන්වීමට ඇති වක්‍ර",
+ "maps-displaymap-par-lines": "පෙන්වීමට ඇති රේඛා",
+ "maps-displaymap-par-maxzoom": "උපරිම විශාලන මට්ටම",
+ "maps-displaymap-par-minzoom": "අවම විශාලන මට්ටම",
+ "maps-displaymap-par-polygons": "පෙන්වීමට ඇති බහුඅස‍්‍ර",
+ "maps-displaymap-par-rectangles": "පෙන්වීමට ඇති ඍජුකෝණාශ්‍ර",
+ "maps-displaymap-par-static": "සිතියම ස්ථිතික විය යුතු වුවහොත්",
+ "maps-displaymap-par-wmsoverlay": "WMS වසාලනය භාවිතා කරන්න",
+ "validation-error-invalid-location": "$1 පරාමිතිය වලංගු ස්ථානයක් විය යුතුය.",
+ "validation-error-invalid-locations": "$1 පරාමිතිය එක් හෝ තවත් වලංගු ස්ථාන විය යුතුය.",
+ "validation-error-invalid-width": "$1 පරාමිතිය වලංගු පළලක් විය යුතුය.",
+ "validation-error-invalid-height": "$1 පරාමිතිය වලංගු උසක් විය යුතුය.",
+ "validation-error-invalid-distance": "$1 පරාමිතිය වලංගු දුරක් විය යුතුය.",
+ "validation-error-invalid-distances": "$1 පරාමිතිය එක් හෝ තවත් වලංගු දුරක් විය යුතුය.",
+ "validation-error-invalid-image": "$1 පරාමිතිය වලංගු පින්තූරයක් විය යුතුය.",
+ "validation-error-invalid-images": "$1 පරාමිතිය එක් හෝ තවත් වලංගු පින්තූරයක් විය යුතුය.",
+ "validation-error-invalid-goverlay": "$1 පරාමිතිය වලංගු වසාලනයක් විය යුතුය.",
+ "validation-error-invalid-goverlays": "$1 පරාමිතිය එක් හෝ තවත් වලංගු වසාලනයක් විය යුතුය.",
+ "maps-abb-north": "උ",
+ "maps-abb-east": "නැ",
+ "maps-abb-south": "ද",
+ "maps-abb-west": "බ",
+ "maps-latitude": "අක්ෂාංශය:",
+ "maps-longitude": "දේශාංශය:",
+ "maps_coordinates_missing": "සිතියම සඳහා ඛණ්ඩාංක කිසිවක් ඉදිරිපත් කොට නොමැත.",
+ "maps_map_cannot_be_displayed": "සිතියම සංදර්ශනය කල නොහැක.",
+ "maps_click_to_activate": "සිතියම සක්‍රිය කිරීම සඳහා ක්ලික් කරන්න",
+ "maps_centred_on": "$1 හිදී සිතියම මධ්‍යගත වේ, $2.",
+ "maps-par-kml": "සිතියම මත පැටවිය යුතු KML ගොනු.",
+ "maps-googlemaps3-par-type": "ආරම්භක වශයෙන් පෙන්විය යුතු සිතියම් වර්ගය.",
+ "maps-googlemaps3-par-layers": "සිතියම මත පැටවිය යුතු විශේෂ ස්ථර.",
+ "maps-googlemaps3-par-zoomstyle": "විශාලන පාලකයේ ශෛලිය.",
+ "maps-googlemaps3-par-typestyle": "වර්ග පාලකයේ ශෛලිය.",
+ "maps-googlemaps3-par-poi": "අභිරුචි ලක්ෂ්‍ය පෙන්වන්න.",
+ "mapeditor": "සිතියම් සංස්කාරක",
+ "specialpages-group-maps": "සිතියම්",
+ "mapeditor-none-text": "කිසිවක් නොමැත",
+ "mapeditor-done-button": "හරි",
+ "mapeditor-remove-button": "ඉවත් කරන්න",
+ "mapeditor-import-button2": "ආයාත කරන්න",
+ "mapeditor-export-button": "විකිකේතය වෙත නිර්යාත කරන්න",
+ "mapeditor-import-button": "විකිකේතය වෙතින් ආයාත කරන්න",
+ "mapeditor-select-button": "මෙම බහුඅස‍්‍රය තෝරාගන්න",
+ "mapeditor-mapparam-button": "සිතියම් පරාමිතීන් සංස්කරණය කරන්න",
+ "mapeditor-clear-button": "සිතියම හිස් කරන්න",
+ "mapeditor-code-title": "විකි කේතය",
+ "mapeditor-import-title": "විකිකේතය ආයාත කරන්න",
+ "mapeditor-form-title": "විස්තර සංස්කරණය කරන්න",
+ "mapeditor-link-title-switcher-popup-text": "පාඨයක් සමඟ Popup",
+ "mapeditor-link-title-switcher-link-text": "සබැඳුම",
+ "mapeditor-form-field-title": "ශීර්ෂය",
+ "mapeditor-form-field-text": "පෙළ",
+ "mapeditor-form-field-link": "සබැඳුම",
+ "mapeditor-form-field-icon": "අයිකනය",
+ "mapeditor-form-field-group": "කාණ්ඩය",
+ "mapeditor-form-field-inlinelabel": "එක් තල ලේබලය",
+ "mapeditor-form-field-strokecolor": "ආඝාත වර්ණය",
+ "mapeditor-form-field-strokeopacity": "ආඝාත පාරාන්ධතාව",
+ "mapeditor-form-field-strokeweight": "ආඝාත බර",
+ "mapeditor-form-field-fillcolor": "පිරවුම් වර්ණය",
+ "mapeditor-form-field-fillopcaity": "පිරවුම් පාරාන්ධතාව",
+ "mapeditor-form-field-showonhover": "ගැවසෙන විට පමණක් පෙන්වන්න",
+ "mapeditor-mapparam-title": "සිතියම් පරාමිතීන් සංස්කරණය කරන්න",
+ "mapeditor-mapparam-defoption": "-පරාමිතිය තෝරාගන්න-",
+ "mapeditor-imageoverlay-button": "පින්තූර වසාලනයක් එක් කරන්න",
+ "mapeditor-form-field-image": "පිංතූරය",
+ "mapeditor-imageoverlay-title": "පින්තූර වසාලන විස්තර",
+ "mapeditor-form-field-visitedicon": "ගොඩ වැදුණු අයිකනය",
+ "semanticmaps-unrecognizeddistance": "$1 අගය වලංගු දුර ප්‍රමාණයක් නොවේ.",
+ "semanticmaps-kml-link": "KML ගොනුව නරඹන්න",
+ "semanticmaps-default-kml-pagelink": "$1 පිටුව නරඹන්න",
+ "semanticmaps-latitude": "අක්ෂාංශය: $1",
+ "semanticmaps-longitude": "දේශාංශය: $1",
+ "semanticmaps-altitude": "උන්නතාංශය: $1",
+ "semanticmaps-forminput-locations": "ස්ථාන",
+ "semanticmaps-par-geocodecontrol": "භූකේතීකරණ පාලකය පෙන්වන්න.",
+ "semanticmaps-kml-title": "ප්‍රතිඑල සඳහා සාමාන්‍ය ශීර්ෂය"
+}
diff --git a/www/wiki/extensions/Maps/i18n/sk.json b/www/wiki/extensions/Maps/i18n/sk.json
new file mode 100644
index 00000000..a8a7284a
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/sk.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Helix84",
+ "Macofe"
+ ]
+ },
+ "maps-desc": "Umožňuje vkladať dynamické mapy do wiki stránok, geokódovať adresy a ďalšie geografické operácie",
+ "maps_map": "Mapa",
+ "maps-abb-north": "S",
+ "maps-abb-east": "V",
+ "maps-abb-south": "J",
+ "maps-abb-west": "Z",
+ "maps-latitude": "Zem. dĺžka:",
+ "maps-longitude": "Zem. šírka:",
+ "maps_coordinates_missing": "Neboli poskytnuté žiadne súradnice.",
+ "maps_geocoding_failed": "Nebolo možné určiť súradnice {{PLURAL:$2|nasledovnej adresy|nasledovných adries}}: $1.",
+ "maps_geocoding_failed_for": "Nebolo možné určiť súradnice {{PLURAL:$2|nasledovnej adresy|nasledovných adries}} a {{PLURAL:$2|bola vynechaná|boli vynechané}} z mapy: $1."
+}
diff --git a/www/wiki/extensions/Maps/i18n/sl.json b/www/wiki/extensions/Maps/i18n/sl.json
new file mode 100644
index 00000000..da19374e
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/sl.json
@@ -0,0 +1,28 @@
+{
+ "@metadata": {
+ "authors": [
+ "Dbc334",
+ "Eleassar",
+ "Lesko987",
+ "Macofe"
+ ]
+ },
+ "maps-desc": "Omogoča vključevanje dinamičnih zemljevidov na wikistrani, geokodiranje naslovov in druge geografske posege",
+ "right-geocode": "Geokodiranje",
+ "maps_map": "Zemljevid",
+ "maps-loading-map": "Nakladanje zemljevida...",
+ "maps-load-failed": "Zemljevid ni bilo mogoče naložiti.",
+ "maps-kml-parsing-failed": "Razčlenjevanje ene ali več datotek KML ni uspelo. Navadno je vzrok neuspešen priklic ali slabo oblikovan XML.",
+ "maps-fullscreen-button": "Vklopi celozaslonski prikaz",
+ "maps-fullscreen-button-tooltip": "Celozaslonski ali vključeni prikaz zemljevida.",
+ "maps-googlemaps3-par-enable-fullscreen": "Omogoči gumb za celozaslonski prikaz",
+ "maps-abb-north": "S",
+ "maps-abb-east": "V",
+ "maps-abb-south": "J",
+ "maps-abb-west": "Z",
+ "maps-latitude": "Zemljepisna širina:",
+ "maps-longitude": "Zemljepisna dolžina:",
+ "maps_googlemaps3": "Google Maps v3",
+ "maps_openlayers": "OpenLayers",
+ "maps_osm": "OpenStreetMap"
+}
diff --git a/www/wiki/extensions/Maps/i18n/sq.json b/www/wiki/extensions/Maps/i18n/sq.json
new file mode 100644
index 00000000..8a5cafe2
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/sq.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ammartivari"
+ ]
+ },
+ "mapeditor": "Redaktor hartash"
+}
diff --git a/www/wiki/extensions/Maps/i18n/sr-ec.json b/www/wiki/extensions/Maps/i18n/sr-ec.json
new file mode 100644
index 00000000..863eef89
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/sr-ec.json
@@ -0,0 +1,28 @@
+{
+ "@metadata": {
+ "authors": [
+ "Milicevic01",
+ "Rancher",
+ "Михајло Анђелковић"
+ ]
+ },
+ "maps_map": "Мапа",
+ "maps-loading-map": "Учитавање мапе...",
+ "validation-error-invalid-location": "Параметар $1 мора бити валидна локација",
+ "validation-error-invalid-locations": "Параметар $1 мора бити једна или више важећих локација.",
+ "validation-error-invalid-width": "Параметар $1 мора бити валидна ширина.",
+ "validation-error-invalid-height": "Параметар $1 мора бити валидна висина.",
+ "validation-error-invalid-distance": "Параметар $1 мора бити валидно растојање.",
+ "validation-error-invalid-distances": "Параметар $1 мора бити једно или више валидних растојања.",
+ "validation-error-invalid-image": "Параметар $1 мора бити валидна слика.",
+ "validation-error-invalid-images": "Параметар $1 мора бити једна или више валидних слика.",
+ "maps-abb-north": "С",
+ "maps-abb-east": "И",
+ "maps-abb-south": "Ј",
+ "maps-abb-west": "З",
+ "maps-latitude": "Географска ширина:",
+ "maps-longitude": "Географска дужина:",
+ "maps_googlemaps3": "Гугл мапе, треће издање",
+ "maps_openlayers": "Опенлејерс",
+ "maps_osm": "OpenStreetMap"
+}
diff --git a/www/wiki/extensions/Maps/i18n/sr-el.json b/www/wiki/extensions/Maps/i18n/sr-el.json
new file mode 100644
index 00000000..733e43fd
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/sr-el.json
@@ -0,0 +1,26 @@
+{
+ "@metadata": {
+ "authors": [
+ "Milicevic01"
+ ]
+ },
+ "maps_map": "Mapa",
+ "maps-loading-map": "Učitavanje mape...",
+ "validation-error-invalid-location": "Parametar $1 mora biti validna lokacija",
+ "validation-error-invalid-locations": "Parametar $1 mora biti jedna ili više važećih lokacija.",
+ "validation-error-invalid-width": "Parametar $1 mora biti validna širina.",
+ "validation-error-invalid-height": "Parametar $1 mora biti validna visina.",
+ "validation-error-invalid-distance": "Parametar $1 mora biti validno rastojanje.",
+ "validation-error-invalid-distances": "Parametar $1 mora biti jedno ili više validnih rastojanja.",
+ "validation-error-invalid-image": "Parametar $1 mora biti validna slika.",
+ "validation-error-invalid-images": "Parametar $1 mora biti jedna ili više validnih slika.",
+ "maps-abb-north": "S",
+ "maps-abb-east": "I",
+ "maps-abb-south": "J",
+ "maps-abb-west": "Z",
+ "maps-latitude": "Geografska širina:",
+ "maps-longitude": "Geografska dužina:",
+ "maps_googlemaps3": "Gugl mape, treće izdanje",
+ "maps_openlayers": "Openlejers",
+ "maps_osm": "OpenStreetMap"
+}
diff --git a/www/wiki/extensions/Maps/i18n/sv.json b/www/wiki/extensions/Maps/i18n/sv.json
new file mode 100644
index 00000000..0e40304e
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/sv.json
@@ -0,0 +1,232 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ainali",
+ "Dafer45",
+ "Fader",
+ "Fluff",
+ "Jopparn",
+ "Lokal Profil",
+ "Per",
+ "Rotsee",
+ "WikiPhoenix",
+ "Macofe",
+ "Martinwiss"
+ ]
+ },
+ "maps-desc": "Ger möjlighet att bädda in dynamiska kartor i wiki-sidor, geokoding av adresser och andra geografiska åtgärder",
+ "right-geocode": "Geokod",
+ "action-geocode": "utför geokoding på denna wiki",
+ "maps_map": "Karta",
+ "maps-tracking-category": "Sidor med en karta som renderats med Karttillägget",
+ "maps-loading-map": "Laddar karta ...",
+ "maps-load-failed": "Kunde inte läsa in kartan!",
+ "maps-markers": "Markörer",
+ "maps-copycoords-prompt": "Ctrl-C, Enter",
+ "maps-searchmarkers-text": "Filtrera markörer",
+ "maps-others": "andra",
+ "maps-kml-parsing-failed": "Misslyckades med att analysera en eller flera KML-filer, oftast på grund av hämtningsfel eller felaktigt XML.",
+ "maps-ns-layer": "Lager",
+ "maps-ns-layer-talk": "Lagerdiskussion",
+ "maps-layer-property": "Egenskap",
+ "maps-layer-value": "Värde",
+ "maps-layer-errors": "Fel",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Ogiltig definition|Ogiltiga definitioner}}",
+ "maps-layerdef-invalid-fatal": "Katastrofalt ogiltig definition",
+ "maps-layerdef-wrong-namespace": "Lagerdefinitioner är enbart giltiga i namnrymden \"$1\"",
+ "maps-layerdef-equal-layer-name": "Lagernamn måste vara unika inom samma lagersida. \"$1\" används redan av ett annat lager.",
+ "maps-layerpage-usage": "Sidor med kartor som använder lager \"$1\"",
+ "maps-layerpage-nousage": "Inga sidor använder för närvarande detta lager",
+ "maps-error-invalid-layertype": "Det finns inga lager av typen \"$1\". Endast {{PLURAL:$3|denna typ|denna typer}} stöds: $2",
+ "maps-error-no-layertype": "Du måste ange lagertypen. {{PLURAL:$2|Endast denna typ|Dessa typer}} stöds: $1",
+ "validation-error-invalid-layer": "Parameter \"$1\" måste vara ett giltig lager.",
+ "validation-error-invalid-layers": "Parameter \"$1\" måste vara ett eller flera giltiga lager.",
+ "validation-error-no-non-numeric": "Parameter \"$1\" måste vara en icke-numerisk sträng.",
+ "validation-error-no-non-numerics": "Parameter \"$1\" måste vara en eller fler icke-numeriska strängar.",
+ "maps-layer-of-type": "Lager av typen \"$1\"",
+ "maps-layer-of-type-and-name": "Lager \"$2\" av typen \"$1\"",
+ "maps-layer-type-supported-by": "Denna lagertyp kan endast användas med {{PLURAL:$2|kartläggningstjänsten $1|dessa kartläggningstjänster: $1}}.",
+ "maps-coordinates-description": "Tolk-hook för att formatera koordinater, från och till något av de format som stöds.",
+ "maps-displaymap-description": "Visa geografiska kartor utan några wiki-definierade markörer på dem.",
+ "maps-distance-description": "Konvertera ett avstånd mätt i en giltig enhet till en annan enhet.",
+ "maps-finddestination-description": "Hitta ett mål utifrån en given startpunkt (på något giltigt format), en inledande bäring och ett avstånd.",
+ "maps-geocode-description": "Aktiverar geokodning av adresser, med andra ord, konvertering av mänskligt läsbara platser till en uppsättning koordinater. det finns stöd för flera geokodningstjänster, som inte bör förväxlas med karttjänster.",
+ "maps-geodistance-description": "Beräkna det geografiska avståndet mellan två punkter, från och till något av de stödda formaten.",
+ "maps-mapsdoc-description": "Visa en tabell med parametrarna för en angiven karttjänst tillsammans med deras standardvärden och beskrivningar.",
+ "maps-layerdefinition-description": "Beskriver ett anpassat lager som kan visas med andra Kartfunktioner.",
+ "maps-mapsdoc-par-service": "Kartläggningstjänsten för att visa parameterdokumentation.",
+ "maps-mapsdoc-par-language": "Språket som du vill visa dokumentationen i. Om det finns någon sådan översättning kommer engelska att användas istället.",
+ "maps-coordinates-par-location": "Koordinaterna du vill formatera.",
+ "maps-coordinates-par-format": "Målformatet för koordinaterna.",
+ "maps-coordinates-par-directional": "Anger om koordinaterna ska matas ut riktat eller inte.",
+ "maps-par-scrollwheelzoom": "Indikerar om musrullning borde vara aktiverat eller inte.",
+ "maps-distance-par-distance": "Avståndet att konvertera till är lika med en bestämd enhet.",
+ "maps-distance-par-decimals": "Det maximala antalet decimalsiffror som ska användas i det resulterande värdet.",
+ "maps-distance-par-unit": "Enheten för utmatning av avståndet.",
+ "maps-finddestination-par-location": "Startplats",
+ "maps-finddestination-par-bearing": "Inledande bäring.",
+ "maps-finddestination-par-distance": "Reseavståndet.",
+ "maps-finddestination-par-format": "Formatet för utmatning av destinationen.",
+ "maps-finddestination-par-directional": "Anger om formatet på destinationen ska vara riktat eller inte.",
+ "maps-geocode-par-location": "Adressen som du vill geokoda.",
+ "maps-geocode-par-format": "Formatet för de resulterande koordinaterna.",
+ "maps-geocode-par-directional": "Anger om koordinaterna ska matas ut riktat eller inte.",
+ "maps-geodistance-par-location1": "Den första punkten i setet för att beräkna avståndet mellan.",
+ "maps-geodistance-par-location2": "Den andra punkten i setet för att beräkna avståndet mellan.",
+ "maps-geodistance-par-unit": "Enheten för utmatning av avståndet.",
+ "maps-geodistance-par-decimals": "Det maximala antalet decimalsiffror som ska användas i det resulterande värdet.",
+ "maps-displaymap-par-mappingservice": "Tillåter att karttjänsten som kommer att användas för att skapa kartan anges.",
+ "maps-displaymap-par-coordinates": "En eller fler platser att markera på kartan. De kommer att anges med en markör.",
+ "maps-displaymap-par-visitedicon": "Filnamnet för en bild som ska användas för markörikoner efter att de ursprungliga markörerna har klickats",
+ "maps-displaymap-par-zoom": "Tillåter inställning av zoomnivån för kartan.\nNär det inte tillhandahålls och det finns flera markörer på kartan, används den bäst passande zoomnivån, inte det konfigurerbara standardvärdet.",
+ "maps-displaymap-par-centre": "Tillåter inställning av koordinaterna för kartans centrum för display_point(s).\nAccepterar både adresser och koordinater.\nNär den här egenskapen inte finns, kommer kartan att centrera sig på den tillhandahållna markören, eller mellan tillhandahållna markörer.",
+ "maps-displaymap-par-title": "Gör det möjligt att ange text som ska visas i popup-fönster för alla markörer som saknar en särskild titel.\nNär detta används tillsammans med en etikett kommer titeln att vara fet och understruken.",
+ "maps-displaymap-par-label": "Gör det möjligt att ange text som ska visas i popup-fönster för alla markörer som saknar en särskild etikett.",
+ "maps-displaymap-par-icon": "Tillåter inställning av ikonerna som används av markörerna.",
+ "maps-displaymap-par-circles": "Cirklar att visa",
+ "maps-displaymap-par-copycoords": "Visa en dialogruta när du klickar på en plats som dess koordinater kan kopieras från",
+ "maps-displaymap-par-lines": "Rader att visa",
+ "maps-displaymap-par-maxzoom": "Maximala zoomnivån",
+ "maps-displaymap-par-minzoom": "Minimala zoomnivån",
+ "maps-displaymap-par-polygons": "Polygoner att visa",
+ "maps-displaymap-par-rectangles": "Rektanglar att visa",
+ "maps-displaymap-par-static": "Gör kartan statisk",
+ "maps-displaymap-par-wmsoverlay": "Använd ett WMS-överdrag",
+ "maps-displaymap-par-geojson": "Webbadressen till en fil eller namnet på sidan som innehåller GeoJSON-data",
+ "maps-fullscreen-button": "Växla fullskärmsläge",
+ "maps-fullscreen-button-tooltip": "Visa kartan i helskärm eller som inbäddad.",
+ "validation-error-invalid-location": "Parameter $1 måste vara en giltig plats.",
+ "validation-error-invalid-locations": "Parameter $1 måste vara en eller flera giltiga platser.",
+ "validation-error-invalid-width": "Parameter $1 måste vara en giltig bredd.",
+ "validation-error-invalid-height": "Parameter $1 måste vara en giltig höjd.",
+ "validation-error-invalid-distance": "Parameter $1 måste vara ett giltigt avstånd.",
+ "validation-error-invalid-distances": "Parameter $1 måste vara en eller flera giltiga avstånd.",
+ "validation-error-invalid-image": "Parameter $1 måste vara en giltig bild.",
+ "validation-error-invalid-images": "Parameter $1 måste vara en eller flera giltiga bilder.",
+ "validation-error-invalid-goverlay": "Parameter $1 måste vara ett giltigt lager.",
+ "validation-error-invalid-goverlays": "Parameter $1 måste vara en eller flera giltiga lager.",
+ "maps-abb-north": "N",
+ "maps-abb-east": "Ö",
+ "maps-abb-south": "S",
+ "maps-abb-west": "V",
+ "maps-latitude": "Breddgrad:",
+ "maps-longitude": "Längdgrad:",
+ "maps-invalid-coordinates": "Värdet $1 identifierades inte som en giltig uppsättning koordinater.",
+ "maps_coordinates_missing": "Inga koordinater angivna för kartan.",
+ "maps_geocoding_failed": "Följande {{PLURAL:$2|adress|adresser}} kunde inte geokodas: $1.\nKartan kan inte visas.",
+ "maps_geocoding_failed_for": "Följande {{PLURAL:$2|adress|adresser}}kunde inte geokodas och {{PLURAL:$2|har|har}} uteslutits från kartan: $1",
+ "maps_unrecognized_coords": "Följande {{PLURAL:$2|koordinat|koordinater}} kändes inte igen: $1.",
+ "maps_unrecognized_coords_for": "Följande {{PLURAL:$2|koordinat|koordinater}} kändes inte igen och {{PLURAL:$2|har|har}} utelämnats från kartan:\n$1",
+ "maps_map_cannot_be_displayed": "Kartan kan inte visas.",
+ "maps-geocoder-not-available": "Geokodningsfunktionen av Maps är inte tillgänglig. Din plats kan inte geokodas.",
+ "maps_leaflet": "Leaflet",
+ "maps-leaflet-par-defzoom": "Gör det möjligt att ändra standardzoomnivån för kartan.",
+ "maps-leaflet-par-layers": "Lagret som kommer att visas när kartan läser in.",
+ "maps-leaflet-par-overlaylayers": "Överläggslagret som kommer att visas när kartan läser in.",
+ "maps-leaflet-par-maxclusterradius": "Den maximala radien som ett kluster kommer att täcka från centrummarkören (i bildpunkter)",
+ "maps-leaflet-par-clusterspiderfy": "När du klickar på ett kluster på den lägsta zoomnivån sprider vi ut den så du kan se alla dess markörer.",
+ "maps_click_to_activate": "Klicka för att aktivera karta",
+ "maps_centred_on": "Karta centrerad på $1, $2.",
+ "maps-par-mappingservice": "Tillåter att karttjänsten som kommer att användas för att skapa kartan anges.",
+ "maps-par-resizable": "Gör att kartans storlek kan ändras genom att dra i dess nedre högra hörnet.",
+ "maps-par-searchmarkers": "Gör det möjligt att söka efter specifika markörer via ett fält som är inbäddat i kartan.",
+ "maps-par-zoom": "Zoomnivån för kartan. För kartor med markörer kommer som standard den mest inzoomade nivå som visar alla markörer att användas.",
+ "maps-par-width": "Tillåter att kartans bredd ställs in. Som standard kommer pixlar att antas som enhet, men du kan uttryckligen ange en av dessa enheter: px, ex, em, %.",
+ "maps-par-height": "Tillåter att kartans höjd ställs in. Som standard kommer pixlar att antas som enhet, men du kan uttryckligen ange en av dessa enheter: px, ex, em, %.",
+ "maps-par-centre": "Platsen där kartan ska vara centrerad",
+ "maps-par-enable-fullscreen": "Aktivera helskärmsknappen",
+ "maps-par-kml": "KML-filer att ladda upp på kartan.",
+ "maps-par-markercluster": "Gör det möjligt att sammanfoga flera närliggande markörer till en enda markör",
+ "maps-googlemaps3-incompatbrowser": "Din webbläsare är inte kompatibel med Google Maps v3.",
+ "maps-googlemaps3-par-imageoverlays": "Gör det möjligt att lägga till en bild att visas på den specifika platsen på kartan.",
+ "maps-googlemaps3-par-type": "Karttyp att visa initialt",
+ "maps-googlemaps3-par-types": "De karttyper som kommer att finnas tillgänglig via typ-reglaget.",
+ "maps-googlemaps3-par-layers": "Särskilda lager att ladda på kartan.",
+ "maps-googlemaps3-par-controls": "Kontroller att placera på kartan.",
+ "maps-googlemaps3-par-zoomstyle": "Stilen för zoomreglaget.",
+ "maps-googlemaps3-par-typestyle": "Stilen för typreglaget.",
+ "maps-googlemaps3-par-autoinfowindows": "Öppna automatiskt alla informationsfönster när sidan har lästs in.",
+ "maps-googlemaps3-par-gkml": "KML-filer, tillhandahållna av Google, att ladda på kartan.",
+ "maps-googlemaps3-par-kmlrezoom": "Zooma tillbaka till kartan efter att KML-lager har lästs in.",
+ "maps-googlemaps3-par-poi": "Visa platser av intresse.",
+ "maps-googlemaps3-par-clustergridsize": "Rutnätsstorleken för ett kluster i bildpunkter.",
+ "maps-par-clustermaxzoom": "Den maximala zoomnivån där kluster kan finnas.",
+ "maps-par-clusterzoomonclick": "Om standardbeteendet för att klicka på ett kluster är att zooma in på det.",
+ "maps-par-maxclusterradius": "Den maximala radien som ett kluster kommer att täcka.",
+ "maps-googlemaps3-par-clusteraveragecenter": "Om centrum för varje kluster bör vara medelvärdet för alla markörer i klustret.",
+ "maps-googlemaps3-par-clusterminsize": "Det minimala antalet markörer i ett kluster innan markörerna döljs och ett sifferantal visas.",
+ "mapeditor": "Kartredigerare",
+ "specialpages-group-maps": "Kartor",
+ "mapeditor-parser-error": "Ett fel uppstod när metadata tolkades. Ignorerar data från användaren.",
+ "mapeditor-none-text": "Ingen",
+ "mapeditor-done-button": "Klar",
+ "mapeditor-remove-button": "Ta bort",
+ "mapeditor-import-button2": "Importera",
+ "mapeditor-export-button": "Exportera till wikikod",
+ "mapeditor-import-button": "Importera från wikikod",
+ "mapeditor-select-button": "Välj denna polygon",
+ "mapeditor-mapparam-button": "Redigera kartparametrar",
+ "mapeditor-clear-button": "Rensa karta",
+ "mapeditor-code-title": "Wikikod",
+ "mapeditor-import-title": "Importera wikikod",
+ "mapeditor-import-note": "Observera att tolken förväntar sig ett mycket strikt format på wiki-koden. Koden som anges här ska matcha kod som produceras av exportfunktionen.",
+ "mapeditor-form-title": "Redigera detaljer",
+ "mapeditor-link-title-switcher-popup-text": "Poppupp med text",
+ "mapeditor-link-title-switcher-link-text": "Länk",
+ "mapeditor-form-field-title": "Titel",
+ "mapeditor-form-field-text": "Text",
+ "mapeditor-form-field-link": "Länk",
+ "mapeditor-form-field-icon": "Ikon",
+ "mapeditor-form-field-group": "Grupp",
+ "mapeditor-form-field-inlinelabel": "Infogad etikett",
+ "mapeditor-form-field-strokecolor": "Linjefärg",
+ "mapeditor-form-field-strokeopacity": "Linjegenomskinlighet",
+ "mapeditor-form-field-strokeweight": "Linjebredd",
+ "mapeditor-form-field-fillcolor": "Fyllningsfärg",
+ "mapeditor-form-field-fillopcaity": "Fyllningsgenomskinlighet",
+ "mapeditor-form-field-showonhover": "Visa endast vid hovring",
+ "mapeditor-mapparam-title": "Redigera kartparametrar",
+ "mapeditor-mapparam-defoption": "-Välj parameter-",
+ "mapeditor-imageoverlay-button": "Lägg till bildöverdrag",
+ "mapeditor-form-field-image": "Bild",
+ "mapeditor-imageoverlay-title": "Bildöverdragsdetaljer",
+ "mapeditor-form-field-visitedicon": "Besökt ikon",
+ "semanticmaps-unrecognizeddistance": "Värdet $1 är inte ett giltigt avstånd.",
+ "semanticmaps-kml-link": "Visa KML-filen",
+ "semanticmaps-default-kml-pagelink": "Visa sida $1",
+ "semanticmaps-latitude": "Breddgrad: $1",
+ "semanticmaps-longitude": "Längdgrad: $1",
+ "semanticmaps-altitude": "Höjd över havet: $1",
+ "semanticmaps-forminput-locations": "Platser",
+ "semanticmaps-par-staticlocations": "En lista med platser som man kan placera på kartan tillsammans med efterfrågad data. Precis som för display_points, så kan du lägga till en titel, en beskrivning och en ikon för varje plats med hjälp av tilde \"~\" som avgränsare.",
+ "semanticmaps-par-showtitle": "Visa en titel i markörens informationsruta eller inte. Det är ofta lämpligt att inte använda denna funktion när en mall används för informationsrutans innehåll.",
+ "semanticmaps-par-hidenamespace": "Visa namnrymdens titel i markörens informationsruta.",
+ "semanticmaps-par-centre": "Kartans mitt. Om inte angiven så kommer kartan automatiskt att hitta markörernas mittpunkt.",
+ "semanticmaps-par-template": "En mall som ska användas för informationsrutorna.",
+ "semanticmaps-par-geocodecontrol": "Visa formulär för geokodning.",
+ "semanticmaps-par-activeicon": "Ikon som bör visas istället för standardmarkören när den aktiva sidan är samma som resultatet från förfrågan",
+ "semanticmaps-par-pagelabel": "När satt till \"yes\" (ja), kommer alla markörer att ha en \"inlineLabel\" med en länk till sidan som innehåller koordinaterna för den markören",
+ "semanticmaps-par-ajaxcoordproperty": "Namn på koordinatsegenskapen som kommer att användas för att bygga ajax-frågan.",
+ "semanticmaps-par-ajaxquery": "En andra fråga som skickas via ajax för att hämta ytterligare koordinater.",
+ "semanticmaps-par-userparam": "Ett värde som skickas till varje mall, om mallar används",
+ "semanticmaps-kml-text": "Texten som hör ihop med varje sida. Om det finns efterfrågade egenskaper så tar de överhand.",
+ "semanticmaps-kml-title": "Förvald titel för resultaten",
+ "semanticmaps-kml-linkabsolute": "Ska länkar vara absoluta (i motsats till relativa)",
+ "semanticmaps-kml-pagelinktext": "Texten som ska användas för länkar till sidan, där $1 kommer att bytas ut med sidtiteln",
+ "semanticmaps-shapes-improperformat": "Felaktig formatering av $1. Se dokumentationen för formatering",
+ "semanticmaps-shapes-missingshape": "Inga former hittade för $1. Se dokumentationen för tillgängliga former",
+ "validator-type-mapscircle": "Geografisk cirkel",
+ "validator-type-mapscircle-list": "Lista över cirklar",
+ "validator-type-mapsimageoverlay": "Bildöverlägg",
+ "validator-type-mapsimageoverlay-list": "Lista över bildöverlägg",
+ "validator-type-mapsline": "Geografisk linje",
+ "validator-type-mapsline-list": "Lista över linjer",
+ "validator-type-mapslocation": "Geografisk plats",
+ "validator-type-mapslocation-list": "Lista över platser",
+ "validator-type-mapsrectangle": "Geografisk rektangel",
+ "validator-type-mapsrectangle-list": "Lista över rektanglar",
+ "validator-type-mapspolygon": "Geografisk polygon",
+ "validator-type-mapspolygon-list": "Lista över geografiska polygoner",
+ "validator-type-wmsoverlay": "Webbmappstjänst-överlägg",
+ "validator-type-jsonfile": "text"
+}
diff --git a/www/wiki/extensions/Maps/i18n/sw.json b/www/wiki/extensions/Maps/i18n/sw.json
new file mode 100644
index 00000000..84fbcfa8
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/sw.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Kipala"
+ ]
+ },
+ "maps-googlemaps3-par-imageoverlays": "Inaruhusu kuongeza picha mahali panapoonyeshwa"
+}
diff --git a/www/wiki/extensions/Maps/i18n/ta.json b/www/wiki/extensions/Maps/i18n/ta.json
new file mode 100644
index 00000000..789c1307
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ta.json
@@ -0,0 +1,63 @@
+{
+ "@metadata": {
+ "authors": [
+ "Karthi.dr",
+ "Shanmugamp7",
+ "மதனாஹரன்"
+ ]
+ },
+ "maps_map": "வரைபடம்",
+ "maps-loading-map": "வரைபடத்தை ஏற்றுகிறது....",
+ "maps-load-failed": "வரைபடத்தை ஏற்ற இயலவில்லை!",
+ "maps-copycoords-prompt": "கட்டுப்பாட்டு விசை+C, உள்வழி விசை",
+ "maps-others": "ஏனையவை",
+ "maps-layer-property": "உடமை",
+ "maps-layer-value": "மதிப்பு",
+ "maps-layer-errors": "பிழைகள்",
+ "maps-finddestination-par-distance": "பயணிக்க வேண்டிய தூரம்.",
+ "maps-displaymap-par-maxzoom": "அதிகபட்ச பெரிதாக்குதல் அளவு",
+ "maps-displaymap-par-minzoom": "குறைந்தபட்ச பெரிதாக்குதல் அளவு",
+ "validation-error-invalid-location": "அளவுரு $1 ஒரு செல்லத்தக்க இடமாக இருக்க வேண்டும்.",
+ "validation-error-invalid-locations": "அளவுரு $1 ஒன்று அல்லது அதற்கு மேற்பட்ட செல்லத்தக்க இடங்களாக இருக்க வேண்டும்.",
+ "validation-error-invalid-width": "அளவுரு $1 ஒரு செல்லத்தக்க அகலமாக இருக்க வேண்டும்.",
+ "validation-error-invalid-height": "அளவுரு $1 ஒரு செல்லத்தக்க உயரமாக இருக்க வேண்டும்.",
+ "validation-error-invalid-distance": "அளவுரு $1 ஒரு செல்லத்தக்க தூரமாக இருக்க வேண்டும்.",
+ "validation-error-invalid-distances": "அளவுரு $1 ஒன்று அல்லது அதற்கு மேற்பட்ட செல்லத்தக்க தூரங்களாக இருக்க வேண்டும்.",
+ "validation-error-invalid-image": "அளவுரு $1 ஒரு செல்லத்தக்க படிமமாக இருக்க வேண்டும்.",
+ "validation-error-invalid-images": "அளவுரு $1 ஒன்று அல்லது அதற்கு மேற்பட்ட படிமங்களாக இருக்க வேண்டும்.",
+ "maps-abb-north": "வ",
+ "maps-abb-east": "கி",
+ "maps-abb-south": "தெ",
+ "maps-abb-west": "மே",
+ "maps-latitude": "அட்சரேகை:",
+ "maps-longitude": "தீர்க்க ரேகை:",
+ "maps_map_cannot_be_displayed": "வரைபடத்தைக் காட்ட இயலவில்லை.",
+ "mapeditor": "வரைபட தொகுப்பாளர்",
+ "specialpages-group-maps": "வரைபடங்கள்",
+ "mapeditor-parser-error": "மேல்நிலைத் தரவைப் பாகுபடுத்தலில் பிழை ஏற்பட்டுள்ளது. பயனர் உள்ளீடு புறக்கணிக்கப்படுகிறது.",
+ "mapeditor-none-text": "எதுவுமில்லை",
+ "mapeditor-done-button": "முடிந்தது",
+ "mapeditor-remove-button": "நீக்குக",
+ "mapeditor-import-button2": "இறக்குமதி",
+ "mapeditor-export-button": "விக்கி குறியீட்டிற்கு ஏற்றுமதி செய்",
+ "mapeditor-import-button": "விக்கி குறியீட்டிலிருந்து இறக்குமதி செய்",
+ "mapeditor-mapparam-button": "வரைபட அளவுருக்களை தொகுக்கவும்",
+ "mapeditor-clear-button": "வரைபடத்தை வெறுமையாக்கு",
+ "mapeditor-code-title": "விக்கி குறியீடு",
+ "mapeditor-import-title": "விக்கி குறியீடு இறக்குமதி",
+ "mapeditor-import-note": "பாகுபடுத்தி ( parser) மிகவும் சரியான விக்கி குறியீடு வடிவமைப்பை எதிர்பார்க்கும் என்பதை குறித்துக் கொள்ளவும். இங்கு உள்ளிடப்பட்ட குறியீடு ஏற்றுமதி செயல்பாடு மூலம் வெளியிடப்பட்ட குறியீட்டுடன் ஒத்துப்போக வேண்டும்.",
+ "mapeditor-form-title": "தொகுப்பு விவரங்கள்",
+ "mapeditor-link-title-switcher-popup-text": "உரையுடன் உலாவல் சட்டம்",
+ "mapeditor-link-title-switcher-link-text": "இணைப்பு",
+ "mapeditor-form-field-title": "தலைப்பு",
+ "mapeditor-form-field-text": "உரை",
+ "mapeditor-form-field-link": "இணைப்பு",
+ "mapeditor-form-field-icon": "படவுரு",
+ "mapeditor-form-field-group": "குழு",
+ "mapeditor-form-field-image": "படம்",
+ "semanticmaps-default-kml-pagelink": "பக்கம் $1 ஐ காண்க",
+ "semanticmaps-latitude": "அட்சரேகை: $1",
+ "semanticmaps-longitude": "தீர்க்கரேகை: $1",
+ "semanticmaps-altitude": "குத்துயரம்: $1",
+ "semanticmaps-forminput-locations": "இடங்கள்"
+}
diff --git a/www/wiki/extensions/Maps/i18n/te.json b/www/wiki/extensions/Maps/i18n/te.json
new file mode 100644
index 00000000..d0ceb85d
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/te.json
@@ -0,0 +1,25 @@
+{
+ "@metadata": {
+ "authors": [
+ "Veeven"
+ ]
+ },
+ "maps_map": "పటం",
+ "maps-others": "ఇతరాలు",
+ "maps-layer-property": "లక్షణం",
+ "maps-layer-value": "విలువ",
+ "maps-layer-errors": "పొరపాట్లు",
+ "maps-abb-north": "ఉ",
+ "maps-abb-east": "తూ",
+ "maps-abb-south": "ద",
+ "maps-abb-west": "ప",
+ "maps-latitude": "అక్షాంశం:",
+ "maps-longitude": "రేఖాంశం:",
+ "specialpages-group-maps": "పటములు",
+ "mapeditor-none-text": "ఏమీలేదు",
+ "mapeditor-link-title-switcher-link-text": "లంకె",
+ "mapeditor-form-field-title": "శీర్షిక",
+ "mapeditor-form-field-text": "పాఠ్యం",
+ "mapeditor-form-field-link": "లంకె",
+ "mapeditor-form-field-icon": "ప్రతీకం"
+}
diff --git a/www/wiki/extensions/Maps/i18n/th.json b/www/wiki/extensions/Maps/i18n/th.json
new file mode 100644
index 00000000..046e9bb0
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/th.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Woraponboonkerd"
+ ]
+ },
+ "maps-desc": "ให้ความสามารถในการแสดงพิกัดในแผนที่ และที่อยู่ที่เป็นรหัสทางภูมิศาสตร์([http://mapping.referata.com/wiki/Maps_examples demo]).\n<br />บริการแผนที่ที่มีอยู่: $1",
+ "maps_coordinates_missing": "ไม่ได้กำหนดพิกัดของแผนที่มาให้"
+}
diff --git a/www/wiki/extensions/Maps/i18n/tl.json b/www/wiki/extensions/Maps/i18n/tl.json
new file mode 100644
index 00000000..c08811e0
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/tl.json
@@ -0,0 +1,158 @@
+{
+ "@metadata": {
+ "authors": [
+ "AnakngAraw",
+ "Jojit fb"
+ ]
+ },
+ "maps-desc": "Nagpapagana ng pagbabaon ng gumagalaw na mga mapa papaloob sa mga pahina ng wiki, pagkokodigong pangheograpiya ng mga tirahan at ibang mga gawaing pangheograpiya. ([http://mapping.referata.com/wiki/Examples mga pagpapatunghay])\n\nkakayahang ipakita ang dato ng tugmaang-pampook sa loob ng mga mapa, at mga triahan ([http://mapping.referata.com/wiki/Maps_examples mga pagpapatunghay])",
+ "right-geocode": "Kodigong pangheograpiya",
+ "maps_map": "Mapa",
+ "maps-loading-map": "Ikinakarga ang mapa...",
+ "maps-load-failed": "Hindi maikarga ang mapa!",
+ "maps-markers": "Mga palatandaan",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "Mga pangmarka ng pansala",
+ "maps-others": "iba pa",
+ "maps-ns-layer": "Patong",
+ "maps-ns-layer-talk": "Usapan sa patong",
+ "maps-layer-property": "Ari-arian",
+ "maps-layer-value": "Halaga",
+ "maps-layer-errors": "Mga kamalian",
+ "maps-error-invalid-layertype": "Walang mga patong ng uring \"$1\". Tanging {{PLURAL:$3|ganitong uri|ganitong mga uri}} lamang ang tinatangkilik: $2",
+ "maps-error-no-layertype": "Kailangan mong tukuyin ang uri ng patong. {{PLURAL:$2|Ganitong uri lamang ang|Ganitong mga uri ang}} tinatangkilik: $1",
+ "validation-error-invalid-layer": "Ang parametrong $1 ay dapat na isang tanggap na patong.",
+ "validation-error-invalid-layers": "Ang parametrong $1 ay dapat na isa o mahigit pang katanggap-tanggap na mga patong.",
+ "maps-layer-of-type": "Patong ng uring $1",
+ "maps-layer-type-supported-by": "Ang ganitong uri ng patong ay {{PLURAL:$2|magagamit lamang sa $1 na palingkuran ng pagmamapa|magagamit lamang sa ganitong mga palingkuran ng pagmamapa: $1}}.",
+ "maps-coordinates-description": "Kawint ng banghay upang maianyo ang mga tugmaang-pampook, magmula at papunta sa anuman sa tinatangkilik na mga anyo.",
+ "maps-displaymap-description": "Ipakita ang mga mapang pangheograpiya na walang anumang tinukoy na pangwiking mga pananda sa ibabaw nila.",
+ "maps-distance-description": "Palitan ang isang kalayuan na ginagamit ang isang partikular na sinusuportahang yunit papunta sa katumbas nito na ginagamit ang isa pang yunit.",
+ "maps-finddestination-description": "Maghanap ng isang patutunguhan na binigyan ng tuldok ng pagsisimula (na maaaring nasa loob ng anuman sa tinatangkilik na mga anyo), isang paunang kapupuntahan at isang layo.",
+ "maps-geocode-description": "Nagpapagana ng pagsasakodigo na pangheograpiya ng mga tirahan, na sa ibang pananalita ay ang pagpapaglit ng mga kinalalagyan na mababasa ng tao upang maging mga pangkat ng mga tugmaang pampook. Mayroong suporta para sa ilang mga paglilingkod na pangkodigo ng heograpiya, na hindi dapat ikalito sa mga paglilingkod ng pagmamapa.",
+ "maps-geodistance-description": "Tuusin ang pangheograpiyang layo sa pagitan ng dalawang mga tuldok, magmula at papunta sa anumang tinatangkilik na mga anyo.",
+ "maps-mapsdoc-description": "Magpakita ng isang talahanayan na mayroong mga parametro para sa isang tinukoy na palingkuran ng pagmamapa na kasama ang kanilang likas na nakatakdang mga halaga at mga paglalarawan.",
+ "maps-mapsdoc-par-service": "Ang palingkuran ng pagmamapa na pagpapakitaan ng kasulatan ng parametro.",
+ "maps-mapsdoc-par-language": "Ang wikang pagpapakitaan ng kasulatan. Kung walang ganiyang salinwikang makukuha, Ingles sa halip niyan ang gagamitin.",
+ "maps-coordinates-par-location": "Ang mga tugmaang-pampook na nais mong iayos ang anyo.",
+ "maps-coordinates-par-format": "Ang pinupukol na anyo para sa mga tugmaang-pampook.",
+ "maps-coordinates-par-directional": "Nagpapahiwatig kung ang mga tugmaang-pampook ay dapat na ilabas bilang pangpatutunguhan o hindi.",
+ "maps-distance-par-distance": "Ang layo na gagawing katumbas nito na may tinukoy na yunit.",
+ "maps-distance-par-decimals": "Ang pinakamataas na bilang ng kakarampot na mga tambilang na gagamitin sa kalalabasang halaga.",
+ "maps-distance-par-unit": "Ang yunit na kalalabasan ng layo.",
+ "maps-finddestination-par-location": "Ang paunang kinalalagyan.",
+ "maps-finddestination-par-bearing": "Ang paunang patutunguhan.",
+ "maps-finddestination-par-distance": "Ang layo ng lalakbayin.",
+ "maps-finddestination-par-format": "Ang anyo kung paano ilalabas ang patutunguhan.",
+ "maps-finddestination-par-directional": "Nagpapahiwatig kung ang anyo ng patutunguhan ay dapat na maka kapupuntahan o hindi.",
+ "maps-geocode-par-location": "Ang tirahan na nais mong ikodigong pangheograpiya.",
+ "maps-geocode-par-format": "Ang anyo para sa kinalalabasang mga tugmaang pampook.",
+ "maps-geocode-par-directional": "Nagpapahiwatig kung ang mga tugmaang-pampook ay dapat na ilabas bilang pangpatutunguhan o hindi.",
+ "maps-geodistance-par-location1": "Ang unang tuldok sa loob ng isang pangkat upang tuusin ang layo sa pagitan.",
+ "maps-geodistance-par-location2": "Ang pangalawang tuldok sa loob ng isang pangkat upang tuusin ang layo sa pagitan.",
+ "maps-geodistance-par-unit": "Ang yunit na kinapapalooban ng kinalalabasan ng layo.",
+ "maps-geodistance-par-decimals": "Ang pinakamataas na bilang ng kakarampot na mga tambilang na gagamitin sa kalalabasang halaga.",
+ "maps-displaymap-par-mappingservice": "Nagpapahintulot na maitakda ang palingkuran ng pagmamapa na gagamitin upang likhain ang mapa.",
+ "maps-displaymap-par-coordinates": "Ang kinalalagyan na paunang pagpapagitnaan ng mapa.",
+ "maps-displaymap-par-zoom": "Nagpapahintulot sa pagtatakda ng antas ng paglapit ng mapa.\nKapag hindi ibinigay at mayroong maramihang mga pananda sa ibabaw ng mapa, kukunin ang pinakaangkop na paglapit, hindi ang maiaayos na likas na katakdaan.",
+ "maps-displaymap-par-centre": "Nagbibigay-daan sa pagtatakda ng mga tugmaang pampook ng gitna ng mapa para sa (mga) tuldok_na_ipapakita.\nTumatanggap kapwa ng mga tirahan o mga tugmaang pampook.\nKapag hindi ibinigay ang pag-aaring ito, igigitna ng mapa ang kanyang sarili sa ibinigay na pangmarka, o sa gitna ng ibinigay na mga pananda.",
+ "maps-displaymap-par-title": "Nagpapahintulot sa pagtatakda ng teksto na ipapakita sa loob ng mga pagsulpot ng lahat ng mga pangmarka na walang tiyak na pamagat.\nKapag ginamit na kasama ng katatakan, ang pamagat ay magiging makapal at mayroong salungguhit.",
+ "maps-displaymap-par-label": "Nagpapahintulot sa pagtatakda ng teksto na ipapakita sa loob ng mga pagsulpot ng lahat ng mga pangmarka na walang tiyak na katatakan.",
+ "maps-displaymap-par-icon": "Nagpapahintulot sa pagtatakda ng ginagamit na kinatawang larawang para sa lahat ng mga pananda.",
+ "validation-error-invalid-location": "Ang parametrong $1 ay dapat na isang tanggap na lokasyon.",
+ "validation-error-invalid-locations": "Ang parametrong $1 ay dapat na isa o mahigit pang tanggap na mga lokasyon.",
+ "validation-error-invalid-width": "Ang parametrong $1 ay dapat na isang tanggap na lapad.",
+ "validation-error-invalid-height": "Ang parametrong $1 ay dapat na isang tanggap na taas.",
+ "validation-error-invalid-distance": "Ang parametrong $1 ay dapat na isang tanggap na layo.",
+ "validation-error-invalid-distances": "Ang parametrong $1 ay dapat na isa o mahigit pang tanggap na mga layo.",
+ "validation-error-invalid-image": "Ang parametrong $1 ay dapat na isang katanggap-tanggap na larawan.",
+ "validation-error-invalid-images": "Ang parametrong $1 ay dapat na isa o mahigit pang katanggap-tanggap na mga larawan.",
+ "validation-error-invalid-goverlay": "Ang parametrong $1 ay dapat na isang tanggap na patong.",
+ "validation-error-invalid-goverlays": "Ang parametrong $1 ay dapat na isa o mahigit pang katanggap-tanggap na mga patong.",
+ "maps-abb-north": "H",
+ "maps-abb-east": "S",
+ "maps-abb-south": "T",
+ "maps-abb-west": "K",
+ "maps-latitude": "Latitud:",
+ "maps-longitude": "Longhitud:",
+ "maps-invalid-coordinates": "Hindi kinilala ang halagang $1 bilang isang tanggap na pangkat ng mga tugmaang-pampook.",
+ "maps_coordinates_missing": "Walang mga tugmaang-pampook na ibinigay para sa mapa.",
+ "maps_geocoding_failed": "Hindi mageokodigo ang sumusunod na {{PLURAL:$2|tirahan|mga tirahan}}: $1.",
+ "maps_geocoding_failed_for": "Hindi mageokodigo ang sumusunod na {{PLURAL:$2|tirahan|mga tirahan}} at {{PLURAL:$2|tinanggal|mga tinanggal}} na mula sa mapa:\n$1",
+ "maps_unrecognized_coords": "Hindi kinilala ang sumusunod na {{PLURAL:$2|tugmaan|mga tugmaan}}: $1.",
+ "maps_unrecognized_coords_for": "Hindi nakilala ang sumusunod na {{PLURAL:$2|tugmaang pampook|mga tugmaang pampook}} at {{PLURAL:$2|inalis|mga inalis}} na mula sa mapa:\n$1",
+ "maps_map_cannot_be_displayed": "Hindi maipapakita ang mapa.",
+ "maps-geocoder-not-available": "Wala ang katangiang-kasangkapang pang-geokodigo ng Mga Mapa. Hindi mageokodigo ang lokasyon mo.",
+ "maps_googlemaps3": "Google Maps v3",
+ "maps_click_to_activate": "Pindutin upang mabuhay ang mapa",
+ "maps_centred_on": "Nakagitna ang mapa sa $1, $2.",
+ "maps-par-resizable": "Nakagagawang mababago ang sukat ng mapa sa pamamagitan ng pagkaladkad doon sa pang-ibabang kanang sulok nito.",
+ "maps-par-zoom": "Ang antas ng paglapit para sa mapa. Para sa mga mapang mayroong mga pangmarka, ito ay likas na nakatakda sa pinaka malapit na antas ng pagkakatutok na nagpapakita pa rin ng lahat ng mga pangmarka.",
+ "maps-par-width": "Nagpapahintulot ng pagtatakda ng lapad ng mapa. Ayon sa likas na pagkakatakda, ang mga piksel ay ipapalagay bilang yunit, subalit maaari mong maliwanag na tukuyin ang isa sa mga yunit na ito: px, ex, em, %.",
+ "maps-par-height": "Nagpapahintulot ng pagtatakda ng taas ng mapa. Ayon sa likas na pagkakatakda, ang mga piksel ay ipapalagay bilang yunit, subalit maaari mong maliwanag na tukuyin ang isa sa mga yunit na ito: px, ex, em, %.",
+ "maps-par-kml": "Mga talaksan ng Wikang Pangmarka ng Butas ng Susian (Keyhole Markup Language, KML) na ikakarga sa ibabaw ng mapa.",
+ "maps-googlemaps3-incompatbrowser": "Ang pantingin-tingin mo ay hindi katambal ng Mga Mapa ng Google v3.",
+ "maps-googlemaps3-par-type": "Ang uri ng mapa na unang ipapakita.",
+ "maps-googlemaps3-par-types": "Ang mga uri ng mapa na magiging makukuha sa pamamagitan ng pantaban ng uri.",
+ "maps-googlemaps3-par-layers": "Natatanging mga patong na ikakarga sa ibabaw ng mapa.",
+ "maps-googlemaps3-par-controls": "Ang mga pantaban na ilalagay sa ibabaw ng mapa.",
+ "maps-googlemaps3-par-zoomstyle": "Ang estilo ng pantaban ng paglapit.",
+ "maps-googlemaps3-par-typestyle": "Ang estilo ng pantaban ng uri.",
+ "maps-googlemaps3-par-autoinfowindows": "Kusang buksan ang lahat ng mga dungawan ng kabatiran pagkaraang maikarga ang pahina.",
+ "maps-googlemaps3-par-gkml": "Mga talaksan ng Wikang Pangmarka ng Butas ng Susian (Keyhole Markup Language, KML) na pinasisinayahan ng Google upang ikarga sa ibabaw ng mapa.",
+ "maps-googlemaps3-par-kmlrezoom": "Muling ilapit ang mapa pagkaraang maikarga na ang mga patong ng Wika ng Pangmarka ng Butas ng Susian (Keyhole Markup Language, KML).",
+ "maps-googlemaps3-par-poi": "Ipakita ang mga tuldok na mapagtutuunan ng pansin.",
+ "mapeditor": "Patnugot ng mapa",
+ "specialpages-group-maps": "Mga mapa",
+ "mapeditor-parser-error": "Naganap ang isang kamalian noong binabanghay ang metadato. Hindi papansinin ang ipinasok ng tagagamit.",
+ "mapeditor-none-text": "Wala",
+ "mapeditor-done-button": "Nagawa na",
+ "mapeditor-remove-button": "Tanggalin",
+ "mapeditor-import-button2": "Angkatin",
+ "mapeditor-export-button": "Iluwas na papunta sa kodigo ng wiki",
+ "mapeditor-import-button": "Angkatin magmula sa kodigo ng wiki",
+ "mapeditor-mapparam-button": "Baguhin ang mga parametro ng mapa",
+ "mapeditor-clear-button": "Hawiin ang mapa",
+ "mapeditor-code-title": "Kodigo ng wiki",
+ "mapeditor-import-title": "Angkatin ang kodigo ng wiki",
+ "mapeditor-import-note": "Paki tandaan na ang pambanghay ay umaasa ng isang napaka higpit na kaanyuan sa kodigo ng wiki. Ang ipinasok na kodigo rito ay dapat na tumugma sa kodigong inilibas ng panunungkulan ng pagluluwas.",
+ "mapeditor-form-title": "Baguhin ang mga detalye",
+ "mapeditor-link-title-switcher-popup-text": "Pagsulpot na mayroong teksto",
+ "mapeditor-link-title-switcher-link-text": "Link",
+ "mapeditor-form-field-title": "Pamagat",
+ "mapeditor-form-field-text": "Teksto",
+ "mapeditor-form-field-link": "Link",
+ "mapeditor-form-field-icon": "Kinatawang larawan",
+ "mapeditor-form-field-group": "Pangkat",
+ "mapeditor-form-field-inlinelabel": "Katatakang nasa guhit",
+ "mapeditor-form-field-strokecolor": "Kulay ng hagod",
+ "mapeditor-form-field-strokeopacity": "Kalabuan ng hagod",
+ "mapeditor-form-field-strokeweight": "Bigat ng hagod",
+ "mapeditor-form-field-fillcolor": "Kulay ng buhos",
+ "mapeditor-form-field-fillopcaity": "Kalabuan ng buhos",
+ "mapeditor-form-field-showonhover": "Ipakita lamang sa pag-aligid",
+ "mapeditor-mapparam-title": "Baguhin ang mga parametro ng mapa",
+ "mapeditor-mapparam-defoption": "-Piliin ang parametro-",
+ "mapeditor-imageoverlay-button": "Idagdag ang kalupkop ng larawan",
+ "mapeditor-form-field-image": "Larawan",
+ "mapeditor-imageoverlay-title": "Mga detalye ng kalupkop ng larawan",
+ "mapeditor-form-field-visitedicon": "Kinatawang larawan ng pagka nadalaw",
+ "semanticmaps-unrecognizeddistance": "Hindi isang tanggap na layo ang halagang $1.",
+ "semanticmaps-kml-link": "Tingnan ang talaksang KML",
+ "semanticmaps-kml": "KML",
+ "semanticmaps-default-kml-pagelink": "Tingnan ang pahinang $1",
+ "semanticmaps-latitude": "Latitud: $1",
+ "semanticmaps-longitude": "Longhitud: $1",
+ "semanticmaps-altitude": "Altitud: $1",
+ "semanticmaps-forminput-locations": "Mga kinalalagyan",
+ "semanticmaps-par-staticlocations": "Isang listahan ng mga lokasyon na idaragdag sa mapa na kasama ng inusisang dato. Katulad ng sa tuldok_ng_pagpapakita, makapagdaragdag ka ng isang pamagat, paglalarawan at kinatawang larawan sa bawat lokasyon na ginagamit ang tilde \"~\" bilang panghiwalay.",
+ "semanticmaps-par-showtitle": "Magpapakita o hindi magpapakita ng isang pamagat sa loob ng pangmarkang bintana ng impormasyon. Ang hindi pagpapagana nito ay kadasalang mas kapakipakinabang kapag gumagamit ng isang suleras upang maiayos ang nilalaman ng bintana ng kabatiran.",
+ "semanticmaps-par-hidenamespace": "Ipakita o huwag ipakita ang pamagat ng puwang na pampangalan sa loob ng pangmarkang bintana ng impormasyon.",
+ "semanticmaps-par-centre": "Ang gitna ng mapa. Kapag hindi ibinigay, kusang pipiliin ng mapa ang pinakamabuting gitna na pagpapakitaan ng lahat ng mga pangmarka",
+ "semanticmaps-par-template": "Isang suleras na gagamit upang iayos ang mga nilalaman ng bintana ng kabatiran.",
+ "semanticmaps-par-geocodecontrol": "Ipakita ang pantaban ng pagkokodigong pangheograpiya.",
+ "semanticmaps-kml-text": "Umuugnay ang teksto sa bawat isang pahina. Pinangingibabawan ng karagdagang sinisiyasat na mga katangiang angkin kung mayroon.",
+ "semanticmaps-kml-title": "Ang likas na nakatakdang pamagat para sa mga resulta",
+ "semanticmaps-kml-linkabsolute": "Kung ang mga kawing ay magiging lubos o hindi (iyong nauukol)",
+ "semanticmaps-kml-pagelinktext": "Ang tekstong gagamitin para sa mga kawing sa pahina, kung saan ang $1 ay mapapalitan ng pamagat ng pahina"
+}
diff --git a/www/wiki/extensions/Maps/i18n/tr.json b/www/wiki/extensions/Maps/i18n/tr.json
new file mode 100644
index 00000000..1548a801
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/tr.json
@@ -0,0 +1,84 @@
+{
+ "@metadata": {
+ "authors": [
+ "Emperyan",
+ "Joseph",
+ "Manco Capac",
+ "Meelo",
+ "Trncmvsr",
+ "Vito Genovese",
+ "McAang",
+ "Hbseren",
+ "Sadrettin",
+ "Incelemeelemani"
+ ]
+ },
+ "right-geocode": "Coğrafi kod",
+ "maps_map": "Harita",
+ "maps-loading-map": "Harita yükleniyor...",
+ "maps-load-failed": "Harita yüklenemiyor!",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-others": "diğerleri",
+ "maps-ns-layer": "Katman",
+ "maps-ns-layer-talk": "Katman tartışması",
+ "maps-layer-property": "Özellik",
+ "maps-layer-value": "Değer",
+ "maps-layer-errors": "Hatalar",
+ "maps-layerdef-invalid": "Hatalı tanımlama{{PLURAL:$1||lar}}",
+ "validation-error-invalid-location": "$1 parametresi geçerli bir konum olmalıdır.",
+ "validation-error-invalid-locations": "$1 parametresi geçerli bir (veya daha fazla) konum olmalıdır.",
+ "validation-error-invalid-width": "$1 parametresi geçerli bir genişlik olmalıdır.",
+ "validation-error-invalid-height": "$1 parametresi geçerli bir yükseklik olmalıdır.",
+ "validation-error-invalid-distance": "$1 parametresi geçerli bir mesafe değeri olmalıdır.",
+ "validation-error-invalid-distances": "$1 parametresi geçerli bir (veya daha fazla) mesafe değeri olmalıdır.",
+ "validation-error-invalid-image": "$1 parametresi geçerli bir resim olmalıdır.",
+ "validation-error-invalid-images": "$1 parametresi geçerli bir (veya daha fazla) resim olmalıdır.",
+ "maps-abb-north": "K",
+ "maps-abb-east": "D",
+ "maps-abb-south": "G",
+ "maps-abb-west": "B",
+ "maps-latitude": "Enlem:",
+ "maps-longitude": "Boylam:",
+ "maps-invalid-coordinates": "$1 değeri geçerli bir koordinat olarak algılanmadı.",
+ "maps_coordinates_missing": "Harita için koordinat girilmedi.",
+ "maps_geocoding_failed": "Takip eden {{PLURAL:$2|adres|adresler}} coğrafi olarak kodlanamadı: $1",
+ "maps_unrecognized_coords": "Şu {{PLURAL:$2|koordinat|koordinatlar}} tanınamadı: $1",
+ "maps_map_cannot_be_displayed": "Harita görüntülenemiyor.",
+ "maps-geocoder-not-available": "Haritanın coğrafi kodlama özelliği etkin değil. Konumunuz kodlanamıyor.",
+ "maps-leaflet-par-defzoom": "Haritanın varsayılan yakınlaştırma düzeyini ayarlamaya olanak sağlar.",
+ "maps_click_to_activate": "Haritayı etkinleştirmek için tıkla",
+ "maps_centred_on": "Harita, $1 $2 koordinatlarında ortalandı.",
+ "mapeditor": "Harita düzenleyici",
+ "specialpages-group-maps": "Haritalar",
+ "mapeditor-parser-error": "Metaveri ayrıştırılırken bir hata oluştu. Kullanıcı girişi yok sayılıyor.",
+ "mapeditor-none-text": "Hiçbiri",
+ "mapeditor-done-button": "Yapıldı",
+ "mapeditor-remove-button": "Kaldır",
+ "mapeditor-import-button2": "İçe aktar",
+ "mapeditor-export-button": "Viki kodu dışa aktar",
+ "mapeditor-import-button": "Viki kodunu içe aktar",
+ "mapeditor-select-button": "Bu çokgeni seçin",
+ "mapeditor-mapparam-button": "Harita parametrelerini düzenle",
+ "mapeditor-clear-button": "Haritayı temizle",
+ "mapeditor-code-title": "Viki kodu",
+ "mapeditor-import-title": "Viki kodunu içe aktar",
+ "mapeditor-form-title": "Ayrıntıları düzenle",
+ "mapeditor-form-field-title": "Başlık",
+ "mapeditor-form-field-text": "Metin",
+ "mapeditor-form-field-link": "Bağlantı",
+ "mapeditor-form-field-icon": "Simge",
+ "mapeditor-form-field-group": "Grup",
+ "mapeditor-form-field-inlinelabel": "Satır içi etiket",
+ "mapeditor-form-field-strokecolor": "Çizgi rengi",
+ "mapeditor-form-field-strokeopacity": "Çizgi saydamlığı",
+ "mapeditor-form-field-strokeweight": "Çizgi kalınlığı",
+ "mapeditor-form-field-fillcolor": "Dolgu rengi",
+ "mapeditor-form-field-fillopcaity": "Dolgu saydamlığı",
+ "mapeditor-form-field-showonhover": "Yalnızca vurgulu olanı göster",
+ "mapeditor-mapparam-title": "Harita parametrelerini düzenle",
+ "mapeditor-mapparam-defoption": "-Parametre seçiniz-",
+ "mapeditor-imageoverlay-button": "Resim katmanı ekle",
+ "mapeditor-form-field-image": "Resim",
+ "mapeditor-imageoverlay-title": "Resim katmanı ayrıntıları",
+ "semanticmaps-forminput-locations": "Konumlar"
+}
diff --git a/www/wiki/extensions/Maps/i18n/tzm.json b/www/wiki/extensions/Maps/i18n/tzm.json
new file mode 100644
index 00000000..164afe5a
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/tzm.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Tifinaghes"
+ ]
+ },
+ "maps_map": "ⵜⴰⴽⵔⵟⴰ"
+}
diff --git a/www/wiki/extensions/Maps/i18n/uk.json b/www/wiki/extensions/Maps/i18n/uk.json
new file mode 100644
index 00000000..8cadcaec
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/uk.json
@@ -0,0 +1,232 @@
+{
+ "@metadata": {
+ "authors": [
+ "Andriykopanytsia",
+ "Arturyatsko",
+ "Base",
+ "Ата",
+ "Тест",
+ "Olion",
+ "Piramidion",
+ "Alex Khimich",
+ "SteveR",
+ "Movses",
+ "Vlad5250"
+ ]
+ },
+ "maps-desc": "Дає змогу вбудовування динамічних карт у сторінки вікі, геокодування адрес та інші географічні операції",
+ "right-geocode": "геокод",
+ "action-geocode": "виконання геокодування у цій вікі",
+ "maps_map": "Мапа",
+ "maps-tracking-category": "Сторінки з картою, створеною розширенням карти",
+ "maps-loading-map": "Завантаження мапи...",
+ "maps-load-failed": "Неможливо завантажити карту!",
+ "maps-markers": "Відмітки",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "Фільтрувати маркери",
+ "maps-others": "інші",
+ "maps-kml-parsing-failed": "Помилка аналізу одного або декількох файлів kml, як правило, через отриманий несправний або некоректний XML.",
+ "maps-ns-layer": "Шар",
+ "maps-ns-layer-talk": "Обговорення шару",
+ "maps-layer-property": "Властивість",
+ "maps-layer-value": "Значення",
+ "maps-layer-errors": "Помилки",
+ "maps-layerdef-invalid": "{{PLURAL:$1|Недопустиме визначення|Недопустимі визначення}}",
+ "maps-layerdef-invalid-fatal": "Фатальне недопустиме визначення",
+ "maps-layerdef-wrong-namespace": "Визначення шарів дійсні тільки на сторінках простору імен «$1»",
+ "maps-layerdef-equal-layer-name": "Назви шарів мають бути унікальними у межах однієї сторінки шарів. \"$1\" вже використано іншим шаром.",
+ "maps-layerpage-usage": "Сторінки з картами, які використовують шар \"$1\"",
+ "maps-layerpage-nousage": "Немає сторінок з цим шаром на даний момент.",
+ "maps-error-invalid-layertype": "Не існують шари типу «$1». {{PLURAL:$3|1=Підтримується лише наступний тип|Підтримуються лише наступні типи}}: $2",
+ "maps-error-no-layertype": "Вам потрібно вказати тип шару. {{PLURAL:$2|1=Тільки такий тип|Такі типи}} підтримуються: $1",
+ "validation-error-invalid-layer": "Параметр $1 повинен бути коректним шаром.",
+ "validation-error-invalid-layers": "Параметри $1 повинні містити один чи більше коректних шарів.",
+ "validation-error-no-non-numeric": "Параметр \"$1\" повинен бути нечисловим рядком.",
+ "validation-error-no-non-numerics": "Параметр \"$1\" повинен бути одним або декількома нечисловими рядками.",
+ "maps-layer-of-type": "Шар типу $1",
+ "maps-layer-of-type-and-name": "Шар \"$2\" типу \"$1\"",
+ "maps-layer-type-supported-by": "Цей тип шару може бути використано {{PLURAL:$2|1=лише із картографічним сервісом|лише із наступними картографічними сервісами:}} $1",
+ "maps-coordinates-description": "Перехопиник парсеру для форматування координат із будь-якого у будьякий формат, що підтримується.",
+ "maps-displaymap-description": "Відображення географічних карт без жодних вікі-маркерів на них.",
+ "maps-distance-description": "Перетворення відстані, вираженої в одних одиницях, у їх еквівалент у інших одиницях.",
+ "maps-finddestination-description": "Знаходить місце призначення від заданої початкової точки (може бути у будь якому із підтримуваних форматів), початкового нарямку і відстані.",
+ "maps-geocode-description": "Дозволяє геокодування адрес, іншими словами перетворює зрозумілі для людини розташування у набори координат. Підтримується декілька сервісів геокодування, які не варто плутати з сервісами картографування.",
+ "maps-geodistance-description": "Розрахувати географічну відстань між двома точками, з будь-якого і у будь-який підтримуваний формат.",
+ "maps-mapsdoc-description": "Відобразити таблицю параметрів вказаного сервісу картографування з їх стандартними значеннями і описом.",
+ "maps-layerdefinition-description": "Описує користувацький шар, який може бути відображений з іншими функціями карти.",
+ "maps-mapsdoc-par-service": "Сервіс картографування, для якого відобразиться документація параметрів.",
+ "maps-mapsdoc-par-language": "Мова, якою показувати документацію. Якщо перекладу на таку немає, буде використано англійську.",
+ "maps-coordinates-par-location": "Координати, які Ви бажаєте відформатувати.",
+ "maps-coordinates-par-format": "Цільовий формат координат.",
+ "maps-coordinates-par-directional": "Вказує на те, чи координати повинні бути виведені безпосередньо, чи ні.",
+ "maps-par-scrollwheelzoom": "Вказує, чи увімкнена прокрутка мишею.",
+ "maps-distance-par-distance": "Відстань для перетворення на еквівалентну у вказаних одиницях.",
+ "maps-distance-par-decimals": "Максимальна кількість цифр дробової частини у значенні результату.",
+ "maps-distance-par-unit": "Одиниці, в яких буде наведено відстань.",
+ "maps-finddestination-par-location": "Початкове розташування.",
+ "maps-finddestination-par-bearing": "Початковий напрямок.",
+ "maps-finddestination-par-distance": "Протяжність подорожі.",
+ "maps-finddestination-par-format": "Формат, у якому буде виведено місце призначення.",
+ "maps-finddestination-par-directional": "Вказує, чи має формат місця призначення має бути спрямованим, чи ні.",
+ "maps-geocode-par-location": "Адреса, яку Ви хочете геокодувати.",
+ "maps-geocode-par-format": "Формат кінцевих координат.",
+ "maps-geocode-par-directional": "Вказує, якщо координати повинні бути виведені безпосередньо чи ні.",
+ "maps-geodistance-par-location1": "Перша точка у наборі для розрахунку відстані.",
+ "maps-geodistance-par-location2": "Друга точка у наборі для розрахунку відстані.",
+ "maps-geodistance-par-unit": "Одиниця вимірювання відстані.",
+ "maps-geodistance-par-decimals": "Найбільша кількість цифр у дробовій частині результату.",
+ "maps-displaymap-par-mappingservice": "Дозволяє налаштувати картографічний сервіс, який буде використовуватися для генерації карти.",
+ "maps-displaymap-par-coordinates": "Один або більше об'єктів для відображення на карті. Вони будуть помічені маркером.",
+ "maps-displaymap-par-visitedicon": "Назва файлу зображення, яке буде використовуватися для позначення значків після того, як самі маркери були натиснені",
+ "maps-displaymap-par-zoom": "Дозволяє встановити рівень масштабування карти.\nКоли не передбачено і кілька маркери присутні на карті, буде вибрано найкращий і найбільш відповідний масштаб, який не конфігурується за замовчуванням.",
+ "maps-displaymap-par-centre": "Дозволяє задати координати центру карти для display_point(s).\nПриймає обидві адреси і координати.\nКоли ця властивість не передбачена, карта центруватиметься сама на наданому маркері або між обома маркерами.",
+ "maps-displaymap-par-title": "Дозволяє задати текст, який буде відображатися у спливних вікнах всіх маркерів, які не мають конкретної назви.\nКоли використовується спільно з міткою, заголовок буде виділений напівжирним підкресленим шрифтом.",
+ "maps-displaymap-par-label": "Дозволяє задати текст, який буде відображатися у спливних вікнах всіх маркерів, які не мають певної мітки.",
+ "maps-displaymap-par-icon": "Дозволяє встановити значок, використовуваний для всіх маркерів.",
+ "maps-displaymap-par-circles": "Кола для відображення",
+ "maps-displaymap-par-copycoords": "Показувати при натисканні на точку мапи діалогове вікно, з якого можна скопіювати координати розташування",
+ "maps-displaymap-par-lines": "Лінії для відображення",
+ "maps-displaymap-par-maxzoom": "Максимальний рівень наближення",
+ "maps-displaymap-par-minzoom": "Мінімальний рівень наближення",
+ "maps-displaymap-par-polygons": "Багатокутники для показу",
+ "maps-displaymap-par-rectangles": "Прямокутники для показу",
+ "maps-displaymap-par-static": "Зробити карту статичною",
+ "maps-displaymap-par-wmsoverlay": "Використовувати накладення WMS",
+ "maps-displaymap-par-geojson": "URL файлу або назва сторінки, яка містить дані GeoJSON",
+ "maps-fullscreen-button": "Перемкнути повноекранний режим",
+ "maps-fullscreen-button-tooltip": "Переглянути мапу на весь екран або як вбудовану.",
+ "validation-error-invalid-location": "Параметр $1 повинен бути коректним місцем розташування.",
+ "validation-error-invalid-locations": "Параметр $1 повинен бути одним або більше коректних місць розташування.",
+ "validation-error-invalid-width": "Параметр $1 повинен бути коректною шириною.",
+ "validation-error-invalid-height": "Параметр $1 повинен бути коректною висотою.",
+ "validation-error-invalid-distance": "Параметр $1 повинен бути дійсною відстанню.",
+ "validation-error-invalid-distances": "Параметр $1 повинен бути однією або більше коректними відстаннями.",
+ "validation-error-invalid-image": "Параметр $1 повинен бути коректним зображенням.",
+ "validation-error-invalid-images": "Параметр $1 повинен бути одним або більше коректними зображеннями.",
+ "validation-error-invalid-goverlay": "Параметр $1 повинен бути коректним накладенням.",
+ "validation-error-invalid-goverlays": "Параметр $1 повинен бути одним або більше коректних накладень.",
+ "maps-abb-north": "Пн",
+ "maps-abb-east": "Сх",
+ "maps-abb-south": "Пд",
+ "maps-abb-west": "Зх",
+ "maps-latitude": "Широта:",
+ "maps-longitude": "Довгота:",
+ "maps-invalid-coordinates": "Значення $1 не є дійсним набором координат.",
+ "maps_coordinates_missing": "Не вказані координати для мапи.",
+ "maps_geocoding_failed": "{{PLURAL:$2|1=Ця адреса не може бути геокодована|Ці адреси не можуть бути геокодовані}}: $1.\nМапа не може бути відображена.",
+ "maps_geocoding_failed_for": "{{PLURAL:$2|1=Наступна адреса не може бути геокодована та була видалена|Наступні адреси не можуть бути геокодовані та були видалені}} з мапи:\n$1",
+ "maps_unrecognized_coords": "Ці {{PLURAL:$2|1=координати|координати}} не були розпізнані: $1.",
+ "maps_unrecognized_coords_for": "Наступні координати не були розпізнані, {{PLURAL:$2|1=вони|вони}} не показані на мапі:\n$1",
+ "maps_map_cannot_be_displayed": "Мапа не може бути відображена.",
+ "maps-geocoder-not-available": "Функція геокодування мап недоступна. Ваше місце розташування не може бути геокодоване.",
+ "maps_leaflet": "Leaflet",
+ "maps-leaflet-par-defzoom": "Дозволяє задавати масштаб карти за замовчуванням.",
+ "maps-leaflet-par-layers": "Шари, які будуть доступні в селекторі шару. Перший шар буде показано під час завантаження карти.",
+ "maps-leaflet-par-overlaylayers": "Накладення шарів, що будуть показані, коли завантажиться мапа.",
+ "maps-leaflet-par-maxclusterradius": "Максимальний радіус, що буде покритий кластером від центрального маркера (в пікселях).",
+ "maps-leaflet-par-clusterspiderfy": "При клацанні на кластер при нижньому рівні масштабу ми розгортаємо його так, щоб Ви могли побачити всі його маркери.",
+ "maps_click_to_activate": "Натисність, щоб активувати мапу",
+ "maps_centred_on": "Центр мапи — $1, $2.",
+ "maps-par-mappingservice": "Дозволяє налаштувати картографічний сервіс, який буде використовуватися для генерації карти.",
+ "maps-par-resizable": "Робить карту змінного розміру, перетяганням в правому нижньому куті.",
+ "maps-par-searchmarkers": "Дозволяє шукати специфічні маркери через поле, вбудоване в карту.",
+ "maps-par-zoom": "Рівень масштабування карти. Для карт з маркерами він типово буде найменшим рівнем, який показує всі маркери.",
+ "maps-par-width": "Дозволяє задати ширину карти. За замовчуванням пікселі прийняті як одиниці, але ви можете вибрати одну з цих одиниць: px, ex, em, %.",
+ "maps-par-height": "Дозволяє задати висоту карти. За замовчуванням пікселі прийняті як одиниці, але ви можете вибрати одну з цих одиниць: px, ex, em.",
+ "maps-par-centre": "Місцевість відносно якої карта має центруватися",
+ "maps-par-enable-fullscreen": "Увімкнути кнопку повноекранного режиму",
+ "maps-par-kml": "Файли KML для завантаження на карту.",
+ "maps-par-markercluster": "Дозволяє об'єднувати декілька прилеглих маркерів в один маркер",
+ "maps-googlemaps3-incompatbrowser": "Веб-переглядач несумісний із Картами Google версії 3.",
+ "maps-googlemaps3-par-imageoverlays": "Дозволяє додати зображення, яке буде показано в зазначеному місці на карті.",
+ "maps-googlemaps3-par-type": "Тим карти, який буде показуватись спочатку.",
+ "maps-googlemaps3-par-types": "Типи карти, які будуть доступні через керування типом.",
+ "maps-googlemaps3-par-layers": "Спеціальні шари для завантаження у карту.",
+ "maps-googlemaps3-par-controls": "Елементи керування, які буде розміщено на карті.",
+ "maps-googlemaps3-par-zoomstyle": "Стиль елементу керування масштабом.",
+ "maps-googlemaps3-par-typestyle": "Стиль елементу керування типом.",
+ "maps-googlemaps3-par-autoinfowindows": "Автоматично відкрити всі інформаційні вікна, після завантаження сторінки.",
+ "maps-googlemaps3-par-gkml": "Файли KML, розташовані на серверах Google для завантаження на карту.",
+ "maps-googlemaps3-par-kmlrezoom": "Масштабування карти після завантаження KML шарів.",
+ "maps-googlemaps3-par-poi": "Показати визначні пам'ятки.",
+ "maps-googlemaps3-par-clustergridsize": "Розмір сітки кластера в пікселях.",
+ "maps-par-clustermaxzoom": "Максимальний рівень наближення, де кластери можуть існувати.",
+ "maps-par-clusterzoomonclick": "Чи є збільшення до кластеру поведінкою за замовчуванням при натисканні на нього.",
+ "maps-par-maxclusterradius": "Максимальний радіус, який буде покритий кластером.",
+ "maps-googlemaps3-par-clusteraveragecenter": "Чи повинен центр кожного кластеру бути середнім усіх міток у ньому.",
+ "maps-googlemaps3-par-clusterminsize": "Мінімальне число міток у кластері, починаючи з якого мітки сховані, а показано їх число.",
+ "mapeditor": "Редактор карт",
+ "specialpages-group-maps": "Карти",
+ "mapeditor-parser-error": "Сталася помилка при обробці метаданих. Ігноруються введені дані.",
+ "mapeditor-none-text": "Нічого",
+ "mapeditor-done-button": "Готово",
+ "mapeditor-remove-button": "Вилучити",
+ "mapeditor-import-button2": "Імпортувати",
+ "mapeditor-export-button": "Експортувати у вікітекст",
+ "mapeditor-import-button": "Імпортувати із вікітексту",
+ "mapeditor-select-button": "Обрати багатокутник",
+ "mapeditor-mapparam-button": "Редагувати параметри карти",
+ "mapeditor-clear-button": "Очистити карту",
+ "mapeditor-code-title": "Вікітекст",
+ "mapeditor-import-title": "Імпорт вікітексту",
+ "mapeditor-import-note": "Зверніть увагу, що обробник очікує дуже строго відформатований вікітекст. Вікітекст, введений тут, повинен точно відповідати тому вікітексту, який був згенерований при експорті.",
+ "mapeditor-form-title": "Редагуння деталей",
+ "mapeditor-link-title-switcher-popup-text": "Вспливаюча підказка",
+ "mapeditor-link-title-switcher-link-text": "Посилання",
+ "mapeditor-form-field-title": "Назва",
+ "mapeditor-form-field-text": "Текст",
+ "mapeditor-form-field-link": "Посилання",
+ "mapeditor-form-field-icon": "Іконка",
+ "mapeditor-form-field-group": "Група",
+ "mapeditor-form-field-inlinelabel": "Текстова мітка",
+ "mapeditor-form-field-strokecolor": "Колір контуру",
+ "mapeditor-form-field-strokeopacity": "Прозорість контуру",
+ "mapeditor-form-field-strokeweight": "Товщина контуру",
+ "mapeditor-form-field-fillcolor": "Колір заливки",
+ "mapeditor-form-field-fillopcaity": "Прозорість заливки",
+ "mapeditor-form-field-showonhover": "Показувати лише при наведенні",
+ "mapeditor-mapparam-title": "Редагувати параметри карти",
+ "mapeditor-mapparam-defoption": "-Оберіть параметр-",
+ "mapeditor-imageoverlay-button": "Додати накладення зображення",
+ "mapeditor-form-field-image": "Зображення",
+ "mapeditor-imageoverlay-title": "Деталі накладання зображення",
+ "mapeditor-form-field-visitedicon": "Іконка для відвіданого",
+ "semanticmaps-unrecognizeddistance": "Значення $1 є недопустимою відстанню.",
+ "semanticmaps-kml-link": "Переглянути KML-файл",
+ "semanticmaps-default-kml-pagelink": "Переглянути сторінку $1",
+ "semanticmaps-latitude": "Широта: $1",
+ "semanticmaps-longitude": "Довгота: $1",
+ "semanticmaps-altitude": "Висота над рівнем моря: $1",
+ "semanticmaps-forminput-locations": "Місця",
+ "semanticmaps-par-staticlocations": "Перелік місць для додавання на карту разом із запитуваними даними. Наприклад, до display_points можна додати назву, опис та піктограму, використовуючи тильду „~“ в якості роздільника.",
+ "semanticmaps-par-showtitle": "Показує чи не показує заголовок в інформаційному вікні маркера. Вимикання цього корисне при використанні шаблону для форматування інформаційного вмісту вікна.",
+ "semanticmaps-par-hidenamespace": "Показувати заголовок простору назв у інформаційному вікні маркера",
+ "semanticmaps-par-centre": "Центр карти. Якщо не задано, то карта автоматично вибере оптимальний центр, який дозволяє відобразити всі маркери на карті.",
+ "semanticmaps-par-template": "Шаблон для форматування вмісту інформаційного вікна.",
+ "semanticmaps-par-geocodecontrol": "Показати управління геокодуваням.",
+ "semanticmaps-par-activeicon": "Піктограма, яка буде відображатися замість типового маркера, коли активна сторінка рівна результату запиту",
+ "semanticmaps-par-pagelabel": "Коли задано \"так\", то усі маркери матимуть \"inlineLabel\" з посиланням на сторінку, яка містить координати для маркера",
+ "semanticmaps-par-ajaxcoordproperty": "Назва властивості координат, яка використовується для побудови ajax-запиту.",
+ "semanticmaps-par-ajaxquery": "Другий запит, що надсилається через ajax для отримання додаткових координат.",
+ "semanticmaps-par-userparam": "Значення, що передається в кожен виклик шаблону, якщо шаблон використовується",
+ "semanticmaps-kml-text": "Текст, пов'язаний з кожною сторінкою. Нехтується запитаними додатковими властивостями, якщо такі є.",
+ "semanticmaps-kml-title": "Типовий заголовок для результатів",
+ "semanticmaps-kml-linkabsolute": "Посилання мають бути абсолютними (на відміну від відносних)",
+ "semanticmaps-kml-pagelinktext": "Текст, який використовується для посилань на сторінку, в яких $1 буде замінено на назву сторінки",
+ "semanticmaps-shapes-improperformat": "Неправильне форматування з $1. Будь ласка, дивіться документацію для форматування",
+ "semanticmaps-shapes-missingshape": "Немає форм для $1, див. документацію за доступними формами",
+ "validator-type-mapscircle": "Географічне коло",
+ "validator-type-mapscircle-list": "Список кіл",
+ "validator-type-mapsimageoverlay": "Накладення зображення",
+ "validator-type-mapsimageoverlay-list": "Список накладень зображень",
+ "validator-type-mapsline": "Географічна лінія",
+ "validator-type-mapsline-list": "Список ліній",
+ "validator-type-mapslocation": "Географічна місцевість",
+ "validator-type-mapslocation-list": "Список місцевостей",
+ "validator-type-mapsrectangle": "Географічний прямокутник",
+ "validator-type-mapsrectangle-list": "Список прямокутників",
+ "validator-type-mapspolygon": "Географічний багатокутник",
+ "validator-type-mapspolygon-list": "Список географічних багатокутників",
+ "validator-type-wmsoverlay": "Накладення Web Map Service",
+ "validator-type-jsonfile": "текст"
+}
diff --git a/www/wiki/extensions/Maps/i18n/ur.json b/www/wiki/extensions/Maps/i18n/ur.json
new file mode 100644
index 00000000..17305d09
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/ur.json
@@ -0,0 +1,44 @@
+{
+ "@metadata": {
+ "authors": [
+ "Tahir mq",
+ "පසිඳු කාවින්ද"
+ ]
+ },
+ "maps_map": "نقشہ",
+ "maps-loading-map": "نقشہ لوڈ ہو رہا ہے...",
+ "maps-markers": "مارکر",
+ "maps-searchmarkers-text": "فلٹر مارکر",
+ "maps-others": "دوسروں",
+ "maps-ns-layer": "تہہ",
+ "maps-layer-property": "پراپرٹی",
+ "maps-layer-value": "قدر",
+ "maps-layer-errors": "غلطیاں",
+ "maps-mapsdoc-par-service": "کے لئے پیرامیٹر کی دستاویزات کو ظاہر کرنے کے لئے تعریفیں سروس.",
+ "maps-coordinates-par-location": "نقاط آپ تشکيل کرنے کے لئے چاہتے ہیں.",
+ "maps-distance-par-distance": "ایک مخصوص یونٹ کے ساتھ اس کے برابر کرنے کے لئے تبدیل کرنے کے لئے مسافت.",
+ "maps-latitude": "عرض البلد:",
+ "maps-longitude": "طول البلد:",
+ "maps_map_cannot_be_displayed": "نقشے کو دکھایا جائے نہیں کر سکتے ہیں.",
+ "maps_click_to_activate": "نقشہ کو چالو کرنے کے لئے کلک کریں",
+ "maps-googlemaps3-par-type": "ابتدائی طور پر ظاہر کرنے کے لئے نقشہ قسم.",
+ "maps-googlemaps3-par-controls": "نقشے پر جگہ کنٹرول.",
+ "maps-googlemaps3-par-zoomstyle": "زوم کے کنٹرول کے انداز.",
+ "maps-googlemaps3-par-typestyle": "قسم کے کنٹرول کے انداز.",
+ "maps-googlemaps3-par-poi": "دلچسپی کے پوائنٹس دکھائیں ۔",
+ "maps-openlayers-par-layers": "کی تہوں ہے کہ پرت کے رکھیں میں دستیاب ہو جائے گا. کے پرت کے سب سے پہلے دکھایا جائے گا جب نقشہ بوجھ.",
+ "maps-osm-par-photos": "شو کی تصاویر",
+ "mapeditor": "ایڈیٹر کا نقشہ",
+ "specialpages-group-maps": "نقشہ جات",
+ "mapeditor-none-text": "کوئی بھی نہیں",
+ "mapeditor-done-button": "کیا کیا",
+ "mapeditor-remove-button": "حذف کریں",
+ "mapeditor-import-button2": "درآمد",
+ "mapeditor-clear-button": "واضح نقشہ",
+ "mapeditor-form-title": "تفصیلات میں ترمیم کریں",
+ "mapeditor-form-field-title": "عنوان",
+ "mapeditor-form-field-text": "ٹیکسٹ",
+ "mapeditor-form-field-link": "لنک",
+ "mapeditor-form-field-group": "گروپ",
+ "mapeditor-form-field-image": "تصویر"
+}
diff --git a/www/wiki/extensions/Maps/i18n/vep.json b/www/wiki/extensions/Maps/i18n/vep.json
new file mode 100644
index 00000000..604c2257
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/vep.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": [],
+ "maps-abb-north": "Pohj.",
+ "maps-abb-east": "Päivl.",
+ "maps-abb-south": "Suvi",
+ "maps-abb-west": "Päivn.",
+ "maps-latitude": "Leveduz:",
+ "maps-longitude": "Piduz:"
+}
diff --git a/www/wiki/extensions/Maps/i18n/vi.json b/www/wiki/extensions/Maps/i18n/vi.json
new file mode 100644
index 00000000..fa1873d0
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/vi.json
@@ -0,0 +1,107 @@
+{
+ "@metadata": {
+ "authors": [
+ "Baonguyen21022003",
+ "Minh Nguyen",
+ "Vinhtantran",
+ "Withoutaname",
+ "Dinhxuanduyet",
+ "Macofe",
+ "Nhatminh01"
+ ]
+ },
+ "maps-desc": "Cung cấp khả năng nhúng bản đồ động vào trang wiki, mã hóa địa lý các địa chỉ, và thực hiện các tác vụ địa lý khác",
+ "right-geocode": "Mã hóa địa lý",
+ "maps_map": "Bản đồ",
+ "maps-loading-map": "Đang tải bản đồ…",
+ "maps-load-failed": "Không thể tải bản đồ!",
+ "maps-markers": "Chú thích",
+ "maps-copycoords-prompt": "Ctrl+C, Enter (⌘C ↩ trên Mac)",
+ "maps-others": "khác",
+ "maps-ns-layer": "Lớp",
+ "maps-ns-layer-talk": "Thảo luận Lớp",
+ "maps-layer-property": "Thuộc tính",
+ "maps-layer-value": "Giá trị",
+ "maps-layer-errors": "Lỗi",
+ "maps-error-invalid-layertype": "Không có lớp nào kiểu “$1”. Chỉ có {{PLURAL:$3|loại|các loại}} này được hỗ trợ: $2",
+ "maps-error-no-layertype": "Cần phải định rõ kiểu lớp. {{PLURAL:$2|Kiểu|Các kiểu}} này được hỗ trợ: $1",
+ "validation-error-invalid-layer": "Tham số $1 phải là một lớp hợp lệ.",
+ "validation-error-invalid-layers": "Tham số $1 phải là một hoặc nhiều lớp hợp lệ.",
+ "maps-layer-of-type": "Lớp kiểu $1",
+ "maps-layer-type-supported-by": "{{PLURAL:$2|Có thể|Chỉ có thể}} sử dụng kiểu lớp này với {{PLURAL:$2|dịch vụ bản đồ $1|các dịch vụ bản đồ: $1}}.",
+ "maps-coordinates-par-location": "Tọa độ để định dạng hóa.",
+ "maps-coordinates-par-format": "Định dạng cho ra tọa độ.",
+ "maps-coordinates-par-directional": "Nên cho ra hướng với tọa độ hay không.",
+ "maps-distance-par-decimals": "Đa số chữ số thập phân trong giá trị kết quả.",
+ "maps-distance-par-unit": "Đơn vị của tầm xa được cho ra.",
+ "maps-finddestination-par-location": "Vị trí ban đầu.",
+ "maps-finddestination-par-bearing": "Hướng ban đầu.",
+ "maps-finddestination-par-distance": "Tầm xa để chạy.",
+ "maps-finddestination-par-format": "Định dạng cho ra nơi đến",
+ "maps-geocode-par-location": "Địa chỉ để mã hóa địa lý.",
+ "maps-displaymap-par-wmsoverlay": "Sử dụng lớp phủ WMS",
+ "validation-error-invalid-location": "Tham số $1 phải là một vị trí hợp lệ.",
+ "validation-error-invalid-locations": "Tham số $1 phải là một hoặc nhiều vị trí hợp lệ.",
+ "validation-error-invalid-width": "Tham số $1 phải là một chiều rộng hợp lệ.",
+ "validation-error-invalid-height": "Tham số $1 phải là một chiều cao hợp lệ.",
+ "validation-error-invalid-distance": "Tham số $1 phải là một tầm hợp lệ.",
+ "validation-error-invalid-distances": "Tham số $1 phải là một hoặc nhiều vị trí hợp lệ.",
+ "validation-error-invalid-image": "Tham số $1 phải là một hình ảnh hợp lệ.",
+ "validation-error-invalid-images": "Tham số $1 phải là một hoặc nhiều hình ảnh hợp lệ.",
+ "validation-error-invalid-goverlay": "Tham số $1 phải là một lớp phủ hợp lệ.",
+ "validation-error-invalid-goverlays": "Tham số $1 phải là một hoặc nhiều lấp hợp lệ.",
+ "maps-abb-north": "B",
+ "maps-abb-east": "Đ",
+ "maps-abb-south": "N",
+ "maps-abb-west": "T",
+ "maps-latitude": "Vĩ độ:",
+ "maps-longitude": "Kinh độ:",
+ "maps-invalid-coordinates": "Giá trị $1 không được nhận ra là tọa độ hợp lệ.",
+ "maps_coordinates_missing": "Chưa định rõ tọa độ cho bản đồ.",
+ "maps_geocoding_failed": "Không thể tính ra mã địa lý của {{PLURAL:$2|địa chỉ|các địa chỉ}} sau: $1.\nKhông thể hiển thị bản đồ.",
+ "maps_geocoding_failed_for": "Không thể tính ra mã địa lý của {{PLURAL:$2|địa chỉ|các địa chỉ}} sau nên bản đồ bỏ qua nó:\n$1",
+ "maps_unrecognized_coords": "Không thể nhận ra {{PLURAL:$2|tọa độ|các tọa độ}} sau: $1.",
+ "maps_unrecognized_coords_for": "Không thể nhận ra {{PLURAL:$2|tọa độ|các tọa độ}} sau nên bản đồ bỏ qua nó:\n$1",
+ "maps_map_cannot_be_displayed": "Không thể hiển thị bản đồ.",
+ "maps-geocoder-not-available": "Không thể mã hóa vị trí của bạn vì tính năng mã hóa địa lý của Bản đồ không có sẵn.",
+ "maps_click_to_activate": "Nhấn chuột vào bản đồ để kích hoạt",
+ "maps_centred_on": "Bản đồ với trung tậm tại $1, $2.",
+ "mapeditor": "Sửa đổi bản đồ",
+ "specialpages-group-maps": "Bản đồ",
+ "mapeditor-none-text": "Không có",
+ "mapeditor-done-button": "Lưu",
+ "mapeditor-remove-button": "Dời",
+ "mapeditor-import-button2": "Nhập",
+ "mapeditor-export-button": "Xuất ra mã wiki",
+ "mapeditor-import-button": "Nhập từ mã wiki",
+ "mapeditor-mapparam-button": "Sửa tham số bản đồ",
+ "mapeditor-clear-button": "Xóa bản đồ",
+ "mapeditor-code-title": "Mã wiki",
+ "mapeditor-import-title": "Nhập mã wiki",
+ "mapeditor-form-title": "Sửa chi tiết",
+ "mapeditor-link-title-switcher-popup-text": "Cửa sổ nổi lên có văn bản",
+ "mapeditor-link-title-switcher-link-text": "Liên kết",
+ "mapeditor-form-field-title": "Tên",
+ "mapeditor-form-field-text": "Văn bản",
+ "mapeditor-form-field-link": "Liên kết",
+ "mapeditor-form-field-icon": "Hình tượng",
+ "mapeditor-form-field-group": "Nhóm",
+ "mapeditor-form-field-inlinelabel": "Nhãn nội dòng",
+ "mapeditor-form-field-strokecolor": "Màu nét",
+ "mapeditor-form-field-fillcolor": "Màu tô đậm",
+ "mapeditor-mapparam-title": "Sửa tham số bản đồ",
+ "mapeditor-mapparam-defoption": "—Chọn tham số—",
+ "mapeditor-imageoverlay-button": "Thêm lớp phủ hình ảnh",
+ "mapeditor-form-field-image": "Hình ảnh",
+ "mapeditor-imageoverlay-title": "Chi tiết lớp phủ hình ảnh",
+ "semanticmaps-unrecognizeddistance": "Giá trị $1 không phải là tầm hợp lệ.",
+ "semanticmaps-kml-link": "Xem tập tin KML",
+ "semanticmaps-default-kml-pagelink": "Xem trang $1",
+ "semanticmaps-forminput-locations": "Các vị trí",
+ "semanticmaps-par-staticlocations": "Danh sách các vị trí để thêm vào bản đồ cùng với dữ liệu được truy vấn. Giống như với display_points, bạn có thể đặt tên, miêu tả, và hình tượng cho mỗi đánh dấu bằng cách phân tách dùng dấu ngã (~).",
+ "semanticmaps-par-showtitle": "Tên tùy chọn của cửa sổ thông tin đánh dấu. Có thể để trống để định dạng nội dung cửa sổ thông tin dùng bản mẫu.",
+ "semanticmaps-par-centre": "Trung tâm của bản đồ. Nếu không có, bản đồ sẽ tự động chọn trung tâm tối ưu bao gồm tất cả các dấu trên bản đồ.",
+ "semanticmaps-par-template": "Bản đồ dùng để định dạng nội dung của cửa sổ thông tin.",
+ "semanticmaps-par-geocodecontrol": "Hiện điều khiển mã hóa địa lý.",
+ "validator-type-jsonfile": "chữ"
+}
diff --git a/www/wiki/extensions/Maps/i18n/vo.json b/www/wiki/extensions/Maps/i18n/vo.json
new file mode 100644
index 00000000..484b0641
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/vo.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": [],
+ "maps-abb-north": "N",
+ "maps-abb-east": "L",
+ "maps-abb-south": "S",
+ "maps-abb-west": "V",
+ "maps-latitude": "Videt:",
+ "maps-longitude": "Lunet:"
+}
diff --git a/www/wiki/extensions/Maps/i18n/wa.json b/www/wiki/extensions/Maps/i18n/wa.json
new file mode 100644
index 00000000..30675972
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/wa.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Srtxg"
+ ]
+ },
+ "specialpages-group-maps": "Mapes"
+}
diff --git a/www/wiki/extensions/Maps/i18n/yi.json b/www/wiki/extensions/Maps/i18n/yi.json
new file mode 100644
index 00000000..481c569c
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/yi.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "පසිඳු කාවින්ද"
+ ]
+ },
+ "maps-layer-value": "ווערט",
+ "mapeditor-remove-button": "אַראָפּנעמען",
+ "mapeditor-import-button2": "אימפארט",
+ "mapeditor-form-field-group": "גרופע"
+}
diff --git a/www/wiki/extensions/Maps/i18n/zh-hans.json b/www/wiki/extensions/Maps/i18n/zh-hans.json
new file mode 100644
index 00000000..13fc0e63
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/zh-hans.json
@@ -0,0 +1,235 @@
+{
+ "@metadata": {
+ "authors": [
+ "Hydra",
+ "Hzy980512",
+ "Kuailong",
+ "Liuxinyu970226",
+ "PhiLiP",
+ "Qiyue2001",
+ "Xiaomingyan",
+ "Yfdyh000",
+ "Byfserag",
+ "Linforest",
+ "阿pp",
+ "Hanyanbo98",
+ "夢蝶葬花",
+ "Angrydog001"
+ ]
+ },
+ "maps-desc": "允许嵌入动态地图到wiki页面,将地址转换为地理编码,及进行其他地理学操作",
+ "right-geocode": "地理编码",
+ "action-geocode": "在此wiki进行地理编码",
+ "maps_map": "地图",
+ "maps-tracking-category": "有由地图扩展描绘地图的页面",
+ "maps-loading-map": "正在载入地图...",
+ "maps-load-failed": "无法加载地图!",
+ "maps-markers": "标记",
+ "maps-copycoords-prompt": "CTRL+C, ENTER",
+ "maps-searchmarkers-text": "筛选标记",
+ "maps-others": "其他",
+ "maps-kml-parsing-failed": "解析一个或多个KML文件失败。这通常是因为检索失败或格式不正确的XML。",
+ "maps-ns-layer": "图层",
+ "maps-ns-layer-talk": "图层讨论",
+ "maps-layer-property": "属性",
+ "maps-layer-value": "值",
+ "maps-layer-errors": "错误",
+ "maps-layerdef-invalid": "$1个无效定义",
+ "maps-layerdef-invalid-fatal": "致命的无效定义",
+ "maps-layerdef-wrong-namespace": "图层定义仅在“$1”名字空间的页面有效",
+ "maps-layerdef-equal-layer-name": "图层名称在相同图层页面中必须唯一。“$1”已被另一图层使用。",
+ "maps-layerpage-usage": "带有使用图层“$1”的地图页面",
+ "maps-layerpage-nousage": "目前没有页面使用此图层。",
+ "maps-error-invalid-layertype": "没有类型“$1”的图层。只支持{{PLURAL:$3|此|这些}}类型:$2",
+ "maps-error-no-layertype": "您需要指定图层类型。只支持{{PLURAL:$2|此|这些}}类型:$1",
+ "validation-error-invalid-layer": "参数“$1”必须是一个有效图层。",
+ "validation-error-invalid-layers": "参数“$1”必须是一个或多个有效图层。",
+ "validation-error-no-non-numeric": "参数“$1”必须是一个非数值字符串。",
+ "validation-error-no-non-numerics": "参数“$1”必须是一个或多个非数值字符串。",
+ "maps-layer-of-type": "图层类型“$1”",
+ "maps-layer-of-type-and-name": "类型“$1”的图层“$2”",
+ "maps-layer-type-supported-by": "此图层类型{{PLURAL:$2|只能与$1映射服务一起使用|可与这些映射服务一起使用:$1}}。",
+ "maps-coordinates-description": "要格式化坐标的解析器钩,来自并转换至任何支持的格式。",
+ "maps-displaymap-description": "显示地理地图,而不需要在上面显示任何wiki定义的标记。",
+ "maps-distance-description": "将使用某个受支持单位的距离转换为其使用其他单位的等值。",
+ "maps-finddestination-description": "提供起点(可以是任意支持的格式)、起始方位角和一段距离来查找目的地。",
+ "maps-geocode-description": "启用地址的地理编码,换句话说,将人类可读的位置变成一组坐标。这支持几种地理编码服务,而这不应与映射服务相混淆。",
+ "maps-geodistance-description": "计算两点之间的地理距离,可使用任何支持的格式作为起点和终点。",
+ "maps-mapsdoc-description": "显示带参数的表格,用以显示指定的映射服务与其默认值和描述。",
+ "maps-layerdefinition-description": "描述一个可以通过其他地图功能显示的自定义图册。",
+ "maps-mapsdoc-par-service": "显示参数文档的映射服务。",
+ "maps-mapsdoc-par-language": "显示文档的语言。如果没有翻译可用,将使用英语。",
+ "maps-coordinates-par-location": "您希望格式化的坐标。",
+ "maps-coordinates-par-format": "目标坐标的格式。",
+ "maps-coordinates-par-directional": "指示坐标是否应输出方向。",
+ "maps-par-scrollwheelzoom": "表明是否应启用鼠标滚动。",
+ "maps-distance-par-distance": "要转换至其使用指定单位的等值的距离。",
+ "maps-distance-par-decimals": "结果值中使用的小数位数最大数。",
+ "maps-distance-par-unit": "输出的距离单位。",
+ "maps-finddestination-par-location": "初始位置。",
+ "maps-finddestination-par-bearing": "最初的影响。",
+ "maps-finddestination-par-distance": "旅行的距离。",
+ "maps-finddestination-par-format": "输出目的地的格式。",
+ "maps-finddestination-par-directional": "指示目的地格式是否应有方向性。",
+ "maps-geocode-par-location": "您希望进行地理编码的地址。",
+ "maps-geocode-par-format": "生成坐标的格式。",
+ "maps-geocode-par-directional": "指示坐标是否应输出方向。",
+ "maps-geodistance-par-location1": "要计算间距的集中第一个点。",
+ "maps-geodistance-par-location2": "要计算间距的集中第二个点。",
+ "maps-geodistance-par-unit": "要输出的距离单位。",
+ "maps-geodistance-par-decimals": "结果值中使用的小数位数最大数。",
+ "maps-displaymap-par-mappingservice": "允许设置将被用于生成地图的映射服务。",
+ "maps-displaymap-par-coordinates": "要在地图上显示的一个或多个位置。它们将以标记显示。",
+ "maps-displaymap-par-visitedicon": "原始标记被点击后,要被用作标记图标的图片文件名",
+ "maps-displaymap-par-zoom": "允许设置地图的缩放级别。当未提供并且地图上存在多个标记时,将使用最合适缩放级别而非默认配置。",
+ "maps-displaymap-par-centre": "允许设置地图中心点的坐标以用于display_point。同时接受地址和坐标。当此属性未提供时,地图将集中于提供的标记上,或提供的标记之间。",
+ "maps-displaymap-par-title": "允许设置将在所有没有具体标题的标记弹窗中显示的文本。当与标签一起使用时,标题将被加粗,并加下划线。",
+ "maps-displaymap-par-label": "允许设置将在所有标记中的弹出窗口显示的文本,只要它们不包含特定标签的话。",
+ "maps-displaymap-par-icon": "允许设置图标用于所有标记。",
+ "maps-displaymap-par-circles": "圈起以显示",
+ "maps-displaymap-par-copycoords": "当点击可以复制坐标的位置时显示对话框",
+ "maps-displaymap-par-lines": "要显示的行",
+ "maps-displaymap-par-maxzoom": "最大缩放级别",
+ "maps-displaymap-par-minzoom": "最小缩放级别",
+ "maps-displaymap-par-polygons": "要显示的多边形",
+ "maps-displaymap-par-rectangles": "要显示的矩形",
+ "maps-displaymap-par-static": "使地图静态",
+ "maps-displaymap-par-wmsoverlay": "使用一个WMS覆盖",
+ "maps-displaymap-par-geojson": "GeoJSON文件的URL",
+ "maps-fullscreen-button": "切换全屏显示",
+ "maps-fullscreen-button-tooltip": "全屏查看地图或嵌入。",
+ "validation-error-invalid-location": "参数 $1 必须是一个有效的位置。",
+ "validation-error-invalid-locations": "参数 $1 必须有一个或多个有效的位置。",
+ "validation-error-invalid-width": "参数 $1 必须是一个有效的宽度。",
+ "validation-error-invalid-height": "参数 $1 必须是一个有效的高度。",
+ "validation-error-invalid-distance": "参数“$1”必须是一个有效的距离。",
+ "validation-error-invalid-distances": "参数“$1”必须是一个或多个有效距离。",
+ "validation-error-invalid-image": "参数“$1”必须是一个有效的图片。",
+ "validation-error-invalid-images": "参数“$1”必须是一个或多个有效的图像。",
+ "validation-error-invalid-goverlay": "参数“$1”必须是一个有效的重叠。",
+ "validation-error-invalid-goverlays": "参数“$1”必须是一个或多个有效重叠。",
+ "maps-abb-north": "北",
+ "maps-abb-east": "东",
+ "maps-abb-south": "南",
+ "maps-abb-west": "西",
+ "maps-latitude": "纬度:",
+ "maps-longitude": "经度:",
+ "maps-invalid-coordinates": "值$1未视作有效的坐标值集。",
+ "maps_coordinates_missing": "地图上没有提供坐标。",
+ "maps_geocoding_failed": "以下$2个地址无法编码:$1。",
+ "maps_geocoding_failed_for": "以下$2个地址不能编码并且已在地图中忽略:\n$1",
+ "maps_unrecognized_coords": "以下$2个坐标不被认可:$1。",
+ "maps_unrecognized_coords_for": "以下$2个坐标不被认可并{{PLURAL:$2|已}}被地图忽略:$1。",
+ "maps_map_cannot_be_displayed": "该地图无法显示。",
+ "maps-geocoder-not-available": "地图的地理编码功能不可用。您所在地区不能被编码。",
+ "maps_leaflet": "Leaflet",
+ "maps-leaflet-par-defzoom": "允许设置地图的默认缩放级别。",
+ "maps-leaflet-par-layers": "将在图层选择器中可用的图层。第一个图层将在地图加载时显示。",
+ "maps-leaflet-par-overlaylayers": "在地图加载时将显示的覆盖图层。",
+ "maps-leaflet-par-maxclusterradius": "群集将从中心标记处遮住的最大半径(像素)。",
+ "maps-leaflet-par-clusterspiderfy": "当您在下方缩放级别点击群集时,我们使其蜘蛛化,这样您可以看见它所有的标记。",
+ "maps_click_to_activate": "点击激活地图",
+ "maps_centred_on": "地图居中在$1,$2。",
+ "maps-par-mappingservice": "允许设置将被用于生成地图的映射服务。",
+ "maps-par-resizable": "通过拖放右下角使地图大小可变。",
+ "maps-par-searchmarkers": "允许通过嵌入在地图中的字段搜索特定标记。",
+ "maps-par-zoom": "地图的缩放级别。对于带标记的地图,这将默认放大到仍可显示所有标记的最大级别。",
+ "maps-par-width": "允许设置地图宽度。默认情况下像素将被假定为单位,但您可以明确指定这些单位之一:px、ex、em、%。",
+ "maps-par-height": "允许设置地图高度。默认情况下像素将被假定为单位,但您可以明确指定这些单位之一:px、ex、em、%。",
+ "maps-par-centre": "地图上的位置应该居中",
+ "maps-par-enable-fullscreen": "启用全屏按钮",
+ "maps-par-kml": "加载地图上的KML文件。",
+ "maps-par-markercluster": "允许将多个附近标记合并为一个标记",
+ "maps-googlemaps3-incompatbrowser": "您的浏览器不兼容Google Maps v3。",
+ "maps-googlemaps3-par-imageoverlays": "允许添加图片以在地图的特定地点显示。",
+ "maps-googlemaps3-par-type": "首先显示的地图类型。",
+ "maps-googlemaps3-par-types": "将通过类型控制可用的地图类型。",
+ "maps-googlemaps3-par-layers": "要加载在地图上的特殊图层。",
+ "maps-googlemaps3-par-controls": "要放置地图上的控件。",
+ "maps-googlemaps3-par-zoomstyle": "缩放控制的样式。",
+ "maps-googlemaps3-par-typestyle": "类型控制的样式。",
+ "maps-googlemaps3-par-autoinfowindows": "在页面加载后,自动打开所有信息窗口。",
+ "maps-googlemaps3-par-gkml": "由Google运营的加载地图上的KML文件。",
+ "maps-googlemaps3-par-kmlrezoom": "在KML图层加载后重新缩放地图。",
+ "maps-googlemaps3-par-poi": "显示感兴趣的点。",
+ "maps-googlemaps3-par-clustergridsize": "群集的网格大小(像素)。",
+ "maps-par-clustermaxzoom": "可能存在群集的最大缩放级别。",
+ "maps-par-clusterzoomonclick": "在群集上默认的点击行为是否为放大。",
+ "maps-par-maxclusterradius": "群集将覆盖的半径最大值。",
+ "maps-googlemaps3-par-clusteraveragecenter": "每个集群的中心是否应为集群中所有标记的平均值。",
+ "maps-googlemaps3-par-clusterminsize": "在标记被隐藏并且显示计数时,集群中标记的最小值。",
+ "mapeditor": "地图编辑器",
+ "specialpages-group-maps": "地图",
+ "mapeditor-parser-error": "解析元数据时出错。忽略用户输入。",
+ "mapeditor-none-text": "无",
+ "mapeditor-done-button": "完成",
+ "mapeditor-remove-button": "移除",
+ "mapeditor-import-button2": "导入",
+ "mapeditor-export-button": "汇出到 wiki 代码",
+ "mapeditor-import-button": "从wiki代码导入",
+ "mapeditor-select-button": "选择此多边形",
+ "mapeditor-mapparam-button": "编辑地图参数",
+ "mapeditor-clear-button": "明确地图",
+ "mapeditor-code-title": "维基代码",
+ "mapeditor-import-title": "导入维基代码",
+ "mapeditor-import-note": "请注意解析器期望在wiki代码上应用非常严格的格式。这里输入的代码应匹配由导出功能输出的代码。",
+ "mapeditor-form-title": "编辑详情",
+ "mapeditor-link-title-switcher-popup-text": "带文本的弹出菜单",
+ "mapeditor-link-title-switcher-link-text": "链接",
+ "mapeditor-form-field-title": "标题",
+ "mapeditor-form-field-text": "文字",
+ "mapeditor-form-field-link": "链接",
+ "mapeditor-form-field-icon": "图标",
+ "mapeditor-form-field-group": "组",
+ "mapeditor-form-field-inlinelabel": "内联标签",
+ "mapeditor-form-field-strokecolor": "描边颜色",
+ "mapeditor-form-field-strokeopacity": "描线不透明度",
+ "mapeditor-form-field-strokeweight": "描边粗细",
+ "mapeditor-form-field-fillcolor": "填充颜色",
+ "mapeditor-form-field-fillopcaity": "填充不透明度",
+ "mapeditor-form-field-showonhover": "只在悬停时显示",
+ "mapeditor-mapparam-title": "编辑地图参数",
+ "mapeditor-mapparam-defoption": "-选择参数-",
+ "mapeditor-imageoverlay-button": "加入图像覆盖",
+ "mapeditor-form-field-image": "图片",
+ "mapeditor-imageoverlay-title": "图像覆盖详细信息",
+ "mapeditor-form-field-visitedicon": "已浏览图标",
+ "semanticmaps-unrecognizeddistance": "取值$1不是有效的距离。",
+ "semanticmaps-kml-link": "查看KML文件",
+ "semanticmaps-default-kml-pagelink": "查看页面 $1",
+ "semanticmaps-latitude": "纬度:$1",
+ "semanticmaps-longitude": "经度:$1",
+ "semanticmaps-altitude": "绝对海拔高度:$1",
+ "semanticmaps-forminput-locations": "位置",
+ "semanticmaps-par-staticlocations": "要添加至地图的位置及查询的数据的列表。如同display_points,你可以给每个位置添加标题、说明,用浪纹分隔“~”。",
+ "semanticmaps-par-showtitle": "是否在标记信息窗口之中显示标题。当采用模板对信息窗口内容进行格式编排的时候,关闭此项往往会有所帮助。",
+ "semanticmaps-par-hidenamespace": "在标记信息窗口中显示名字空间标题",
+ "semanticmaps-par-centre": "地图的中心。当未加提供的时候,地图会自动挑选最佳的中心,从而在地图上显示所有的标记。",
+ "semanticmaps-par-template": "用来对信息窗口内容进行格式编排的模板。",
+ "semanticmaps-par-geocodecontrol": "显示地理编码控件。",
+ "semanticmaps-par-activeicon": "当活跃页面等于查询结果时将显示选定标记而不是默认标记",
+ "semanticmaps-par-pagelabel": "当您设置为“yes”时,所有标记将拥有一个“inlineLabel”带有一个链接至包含坐标标记的页面",
+ "semanticmaps-par-ajaxcoordproperty": "用于构造ajax查询的坐标属性名称。",
+ "semanticmaps-par-ajaxquery": "通过ajax发送以取得额外坐标的第二查询。",
+ "semanticmaps-par-userparam": "当模板被使用时,传递到每个模板调用的值",
+ "semanticmaps-kml-text": "与每个页面相关联的文本。会被额外的查询属性(如果有的话)所覆盖。",
+ "semanticmaps-kml-title": "结果的默认标题",
+ "semanticmaps-kml-linkabsolute": "链接究竟应当是绝对地址还是相对地址",
+ "semanticmaps-kml-pagelinktext": "用于那些指向该页面的链接的文本;其中,页面标题将取代$1",
+ "semanticmaps-shapes-improperformat": "$1 的格式不正确。请参阅文档格式",
+ "semanticmaps-shapes-missingshape": "找不到用于$1的形状。请参见文档以查看可用形状",
+ "validator-type-mapscircle": "地理圈",
+ "validator-type-mapscircle-list": "圈列表",
+ "validator-type-mapsimageoverlay": "图片覆盖",
+ "validator-type-mapsimageoverlay-list": "图片覆盖列表",
+ "validator-type-mapsline": "地理线",
+ "validator-type-mapsline-list": "线列表",
+ "validator-type-mapslocation": "地理位置",
+ "validator-type-mapslocation-list": "位置列表",
+ "validator-type-mapsrectangle": "地理矩形",
+ "validator-type-mapsrectangle-list": "矩形列表",
+ "validator-type-mapspolygon": "地理多边形",
+ "validator-type-mapspolygon-list": "地理多边形列表",
+ "validator-type-wmsoverlay": "网络地图服务覆盖",
+ "validator-type-jsonfile": "文本"
+}
diff --git a/www/wiki/extensions/Maps/i18n/zh-hant.json b/www/wiki/extensions/Maps/i18n/zh-hant.json
new file mode 100644
index 00000000..e96f3010
--- /dev/null
+++ b/www/wiki/extensions/Maps/i18n/zh-hant.json
@@ -0,0 +1,235 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cwlin0416",
+ "EagerLin",
+ "Horacewai2",
+ "Justincheng12345",
+ "Mark85296341",
+ "Shirayuki",
+ "Simon Shek",
+ "Waihorace",
+ "LNDDYL",
+ "Macofe",
+ "1233thehongkonger",
+ "Liuxinyu970226",
+ "Oapbtommy",
+ "Kly"
+ ]
+ },
+ "maps-desc": "可讓 Wiki 頁面嵌入動態地圖,支援使用地理編碼標記位置,以及地理相關操作",
+ "right-geocode": "地理編碼",
+ "action-geocode": "在此 wiki 上進行地理編碼",
+ "maps_map": "地圖",
+ "maps-tracking-category": "使用地圖擴充套件顯示地圖的頁面",
+ "maps-loading-map": "讀取地圖中...",
+ "maps-load-failed": "無法讀取地圖!",
+ "maps-markers": "標記",
+ "maps-copycoords-prompt": "CTRL+C,ENTER",
+ "maps-searchmarkers-text": "搜尋標記",
+ "maps-others": "其他",
+ "maps-kml-parsing-failed": "分析一個或多個 KML 檔案失敗,通常因為檔案接收失敗,或者 XML 格式不正確。",
+ "maps-ns-layer": "圖層",
+ "maps-ns-layer-talk": "圖層對話",
+ "maps-layer-property": "屬性",
+ "maps-layer-value": "值",
+ "maps-layer-errors": "錯誤",
+ "maps-layerdef-invalid": "無效的定義 {{PLURAL:$1| |s}}",
+ "maps-layerdef-invalid-fatal": "嚴重無效的定義",
+ "maps-layerdef-wrong-namespace": "圖層定義僅在命名空間 \"$1\" 中的頁面有效",
+ "maps-layerdef-equal-layer-name": "圖層名稱不可與其他圖層頁面相同。\"$1\" 已被其他圖層使用。",
+ "maps-layerpage-usage": "地圖使用圖層 \"$1\" 的頁面",
+ "maps-layerpage-nousage": "目前沒有任何頁面使用此圖層。",
+ "maps-error-invalid-layertype": "不支援圖層類型 \"$1\",僅支援的{{PLURAL:$3|類型|類型}}為:$2",
+ "maps-error-no-layertype": "您必須指定圖層類型,{{PLURAL:$2|僅支援|支援}}的圖層類型為:$1",
+ "validation-error-invalid-layer": "參數 \"$1\" 必須是一個有效的圖層。",
+ "validation-error-invalid-layers": "參數 \"$1\" 必須是一個或多個有效的圖層。",
+ "validation-error-no-non-numeric": "參數 \"$1\" 必須是一個非數字的字串。",
+ "validation-error-no-non-numerics": "參數 \"$1\" 必須是一個或多個非數字的字串。",
+ "maps-layer-of-type": "圖層類型 \"$1\"",
+ "maps-layer-of-type-and-name": "類型為 \"$1\" 的圖層 \"$2\"",
+ "maps-layer-type-supported-by": "此類型圖層{{PLURAL:$2|只可使用 $1 地圖服務|可使用以下地圖服務:$1}}。",
+ "maps-coordinates-description": "用來格式化座標的分析器連結(Hook),可在支援的格式之間轉換。",
+ "maps-displaymap-description": "顯示地理地圖,不顯示任何 Wiki 定義的標記於地圖上。",
+ "maps-distance-description": "可以使用任何支援的單位轉換距離的單位",
+ "maps-finddestination-description": "根據起點 (可使用支援的任何格式)、初始方位與距離找尋目的地。",
+ "maps-geocode-description": "開啟地圖編碼位置,將可閱讀的位置名稱轉換為座標。地圖編碼有許多不同服務可選擇,與地圖服務不同。",
+ "maps-geodistance-description": "計算地圖兩點之間的距離,可使用任何支援的格式作為起點與終點。",
+ "maps-mapsdoc-description": "顯示帶有參數的表格,用於指定的地圖服務與其該預設值及描述。",
+ "maps-layerdefinition-description": "描述自訂圖層,可以顯示其他地圖功能。",
+ "maps-mapsdoc-par-service": "顯示參數文件的地圖服務。",
+ "maps-mapsdoc-par-language": "文件顯示的語言,如果沒有可用翻譯的話,會以英語取代。",
+ "maps-coordinates-par-location": "您希望格式化的座標。",
+ "maps-coordinates-par-format": "座標的目標格式。",
+ "maps-coordinates-par-directional": "指示座標是否應輸出方向。",
+ "maps-par-scrollwheelzoom": "指示是否應啟用滑鼠滾動。",
+ "maps-distance-par-distance": "轉換成與指定單位等值的距離。",
+ "maps-distance-par-decimals": "用於結果值中小數位數的最大值。",
+ "maps-distance-par-unit": "輸出距離的單位。",
+ "maps-finddestination-par-location": "初始位置。",
+ "maps-finddestination-par-bearing": "初始方位。",
+ "maps-finddestination-par-distance": "移動距離。",
+ "maps-finddestination-par-format": "輸出目的地的格式。",
+ "maps-finddestination-par-directional": "指示目的地格式是否要包含方向。",
+ "maps-geocode-par-location": "您想要地理編碼的地址。",
+ "maps-geocode-par-format": "座標結果的格式。",
+ "maps-geocode-par-directional": "指示座標是否應輸出方向。",
+ "maps-geodistance-par-location1": "設定裡用來計算間距的第一個點。",
+ "maps-geodistance-par-location2": "設定裡用來計算間距的第二個點。",
+ "maps-geodistance-par-unit": "輸出距離的單位。",
+ "maps-geodistance-par-decimals": "用於結果值中小數位數的最大值。",
+ "maps-displaymap-par-mappingservice": "允許設定用於產生地圖的地圖服務。",
+ "maps-displaymap-par-coordinates": "顯示在地圖上的一個或多個位置,它們會被指示成標記。",
+ "maps-displaymap-par-visitedicon": "在原有標記被點擊時,用於標記圖標的圖片檔案名稱。",
+ "maps-displaymap-par-zoom": "允許設定地圖的縮放層級。\n若不提供且多個標記有呈現在地圖上時,將會採用最適宜的縮放而非設置的預設值。",
+ "maps-displaymap-par-centre": "允許設定用於 display_point(s) 的地圖中心座標。\n可同時接受地址與坐標。\n當此屬性未提供時,地圖將會在所提供標記裡至中,或是在所提供的標記之間。",
+ "maps-displaymap-par-title": "允許設定顯示於所有標記的彈跳視窗顯示文字,若該標記不包含特定標籤的話。\n當與標籤一同使用時,標題將會以粗體並附上底線呈現。",
+ "maps-displaymap-par-label": "允許設定顯示於所有標記的彈跳視窗顯示文字,若該標記不包含特定標籤的話。",
+ "maps-displaymap-par-icon": "允許設定用於所有標記的圖標。",
+ "maps-displaymap-par-circles": "要顯示的圓形",
+ "maps-displaymap-par-copycoords": "當點擊來自可讓座標複製的位置時顯示對話框",
+ "maps-displaymap-par-lines": "要顯示的直線",
+ "maps-displaymap-par-maxzoom": "縮放層級上限",
+ "maps-displaymap-par-minzoom": "縮放層級下限",
+ "maps-displaymap-par-polygons": "要顯示的多邊形",
+ "maps-displaymap-par-rectangles": "要顯示的矩形",
+ "maps-displaymap-par-static": "讓地圖為靜態",
+ "maps-displaymap-par-wmsoverlay": "使用 WMS 圖層",
+ "maps-displaymap-par-geojson": "包含 GeoJSON 資料的檔案或頁面名稱之 URL",
+ "maps-fullscreen-button": "切換全螢幕",
+ "maps-fullscreen-button-tooltip": "使用全螢幕或嵌入方式檢視地圖。",
+ "validation-error-invalid-location": "參數 \"$1\" 必須是一個有效的位置。",
+ "validation-error-invalid-locations": "參數 \"$1\" 必須有一個或多個有效的位置。",
+ "validation-error-invalid-width": "參數 \"$1\" 必須是一個有效的寬度。",
+ "validation-error-invalid-height": "參數 \"$1\" 必須是一個有效的高度。",
+ "validation-error-invalid-distance": "參數 \"$1\" 必須是一個有效的距離。",
+ "validation-error-invalid-distances": "參數 \"$1\" 必須是一個或多個有效距離。",
+ "validation-error-invalid-image": "參數 \"$1\" 必須是一個有效的圖片。",
+ "validation-error-invalid-images": "參數 \"$1\" 必須是一個或多個有效的圖片。",
+ "validation-error-invalid-goverlay": "參數 \"$1\" 必須是一個有效的圖層。",
+ "validation-error-invalid-goverlays": "參數 \"$1\" 必須是一個或多個有效的圖層。",
+ "maps-abb-north": "北",
+ "maps-abb-east": "東",
+ "maps-abb-south": "南",
+ "maps-abb-west": "西",
+ "maps-latitude": "緯度:",
+ "maps-longitude": "經度:",
+ "maps-invalid-coordinates": "值 $1 不被接受為座標的設定值",
+ "maps_coordinates_missing": "地圖上沒有提供座標。",
+ "maps_geocoding_failed": "以下{{PLURAL:$2|個地址|個地址}}無法作出地理編碼:$1。",
+ "maps_geocoding_failed_for": "以下{{PLURAL:$2|個地址|個地址}}無法作出地理編碼且該{{PLURAL:$2|個地址已|個地址已}}從地圖裡捨去:$1。",
+ "maps_unrecognized_coords": "以下{{PLURAL:$2|個座標|個座標}}不被接受:$1。",
+ "maps_unrecognized_coords_for": "以下{{PLURAL:$2|個座標|個座標}}不被接受且該{{PLURAL:$2|個座標已|個座標已}}從地圖裡捨去:$1。",
+ "maps_map_cannot_be_displayed": "該地圖無法顯示。",
+ "maps-geocoder-not-available": "地圖的地理編碼功能不可用,您所在地區無法編碼。",
+ "maps_leaflet": "Leaflet",
+ "maps-leaflet-par-defzoom": "允許設定地圖的預設縮放層級。",
+ "maps-leaflet-par-layers": "此圖層將於地圖加載時顯示",
+ "maps-leaflet-par-overlaylayers": "此覆蓋圖層將於地圖加載時顯示。",
+ "maps-leaflet-par-maxclusterradius": "叢集自中央標記起會涵蓋的最大半徑(像素)。",
+ "maps-leaflet-par-clusterspiderfy": "當您在下方縮放層級點擊叢集時,我們會將其放射狀化來讓您可以查看所有的標記。",
+ "maps_click_to_activate": "按一下以啟動地圖",
+ "maps_centred_on": "地圖以 $1, $2 為中心。",
+ "maps-par-mappingservice": "允許設定用於產生地圖的地圖服務。",
+ "maps-par-resizable": "透過拖拉到右下角來變化地圖大小。",
+ "maps-par-searchmarkers": "允許透過內嵌在地圖的欄位來搜尋特定標記。",
+ "maps-par-zoom": "用於地圖的縮放層級。可用在帶標記的地圖,預設會放大到最高層級並顯示所有標記。",
+ "maps-par-width": "允許設定地圖寬度。預設會以像素為單位,而您可明確指定以下單位之一:px、ex、em、%。",
+ "maps-par-height": "允許設定地圖寬度。預設會以像素為單位,而您可明確指定以下單位之一:px、ex、em、%。",
+ "maps-par-centre": "在地圖上的位置應置中",
+ "maps-par-enable-fullscreen": "開啟全螢幕的按鈕",
+ "maps-par-kml": "載入到地圖的 KML 檔案。",
+ "maps-par-markercluster": "允許合併多個附近標記成一個標記",
+ "maps-googlemaps3-incompatbrowser": "您的瀏覽器不相容 Google 地圖 v3。",
+ "maps-googlemaps3-par-imageoverlays": "允許添加顯示在地圖特定位置的圖片。",
+ "maps-googlemaps3-par-type": "首要顯示的地圖類型。",
+ "maps-googlemaps3-par-types": "透過類型控制的可用地圖類型。",
+ "maps-googlemaps3-par-layers": "載入到地圖上的特殊圖層。",
+ "maps-googlemaps3-par-controls": "在地圖上的地點控制。",
+ "maps-googlemaps3-par-zoomstyle": "縮放控制的樣式。",
+ "maps-googlemaps3-par-typestyle": "類型控制的樣式。",
+ "maps-googlemaps3-par-autoinfowindows": "在頁面載入後,自動開啟所有訊息視窗。",
+ "maps-googlemaps3-par-gkml": "由 Google 託管載入到地圖的 KML 檔案。",
+ "maps-googlemaps3-par-kmlrezoom": "在 KML 圖層被載入後會重新縮放地圖。",
+ "maps-googlemaps3-par-poi": "顯示興趣點",
+ "maps-googlemaps3-par-clustergridsize": "像素叢集的網格大小。",
+ "maps-par-clustermaxzoom": "叢集裡可能存有的最大縮放層級。",
+ "maps-par-clusterzoomonclick": "在點擊叢集時的是否預設將其縮放。",
+ "maps-par-maxclusterradius": "叢集會涵蓋的最大半徑。",
+ "maps-googlemaps3-par-clusteraveragecenter": "任一叢集中心是否應為該叢集裡所有標記的平均值。",
+ "maps-googlemaps3-par-clusterminsize": "在標記被隱藏以及計數顯示之前,於叢集裡的標記最小值。",
+ "mapeditor": "地圖編輯器",
+ "specialpages-group-maps": "地圖",
+ "mapeditor-parser-error": "分析詮釋資料錯誤,忽略使用者輸入。",
+ "mapeditor-none-text": "無",
+ "mapeditor-done-button": "完成",
+ "mapeditor-remove-button": "移除",
+ "mapeditor-import-button2": "匯入",
+ "mapeditor-export-button": "匯出為 wiki 代碼",
+ "mapeditor-import-button": "自 wiki 代碼匯入",
+ "mapeditor-select-button": "選擇此多邊形",
+ "mapeditor-mapparam-button": "編輯地圖參數",
+ "mapeditor-clear-button": "清空地圖",
+ "mapeditor-code-title": "wiki 代碼",
+ "mapeditor-import-title": "匯入 Wiki 代碼",
+ "mapeditor-import-note": "請注意,解析器在 wiki 代碼裡會要求相當嚴格的格式。在此輸入的代碼應要符合透過解析器功能輸出的代碼內容。",
+ "mapeditor-form-title": "編輯詳細資料",
+ "mapeditor-link-title-switcher-popup-text": "顯示文字訊息",
+ "mapeditor-link-title-switcher-link-text": "連結",
+ "mapeditor-form-field-title": "標題",
+ "mapeditor-form-field-text": "文字",
+ "mapeditor-form-field-link": "連結",
+ "mapeditor-form-field-icon": "圖示",
+ "mapeditor-form-field-group": "群組",
+ "mapeditor-form-field-inlinelabel": "行內標籤",
+ "mapeditor-form-field-strokecolor": "筆畫色彩",
+ "mapeditor-form-field-strokeopacity": "筆畫透明度",
+ "mapeditor-form-field-strokeweight": "筆畫粗細",
+ "mapeditor-form-field-fillcolor": "填滿色彩",
+ "mapeditor-form-field-fillopcaity": "填滿透明度",
+ "mapeditor-form-field-showonhover": "只在滑鼠經過時顯示",
+ "mapeditor-mapparam-title": "編輯地圖參數",
+ "mapeditor-mapparam-defoption": "-請選擇參數-",
+ "mapeditor-imageoverlay-button": "新增圖片圖層",
+ "mapeditor-form-field-image": "圖片",
+ "mapeditor-imageoverlay-title": "圖片圖層詳細資料",
+ "mapeditor-form-field-visitedicon": "已瀏覽的圖示",
+ "semanticmaps-unrecognizeddistance": "數值 $1 不是有效的距離。",
+ "semanticmaps-kml-link": "檢視 KML 檔案",
+ "semanticmaps-default-kml-pagelink": "檢視頁面 $1",
+ "semanticmaps-latitude": "緯度:$1",
+ "semanticmaps-longitude": "經度:$1",
+ "semanticmaps-altitude": "海拔:$1",
+ "semanticmaps-forminput-locations": "位置",
+ "semanticmaps-par-staticlocations": "與查詢的資料一起新增到地圖的位置清單。 如 display_points,您可新增每個位置的標題、描述與圖示使用波浪號 \"~\" 作為分隔符號。",
+ "semanticmaps-par-showtitle": "是否在標記資訊視窗之中顯示標題。當使用模板對資訊視窗內容進行格式編排的時,關閉此選項通常會有所幫助。",
+ "semanticmaps-par-hidenamespace": "在標記資訊視窗中顯示命名空間的標題",
+ "semanticmaps-par-centre": "地圖的中心位置。若不指定,地圖會自動挑選可顯示所有標記於地圖上的最佳中心位置。",
+ "semanticmaps-par-template": "用來對資訊視窗內容進行格式編排的模板。",
+ "semanticmaps-par-geocodecontrol": "顯示地理編碼控制元件。",
+ "semanticmaps-par-activeicon": "當使用的頁面為查詢結果時,用來替代預設標記要顯示的圖示",
+ "semanticmaps-par-pagelabel": "當設為 \"是\" 時,所有的標記會有 \"inlineLabel\" 並連結至含有標記座標的頁面。",
+ "semanticmaps-par-ajaxcoordproperty": "使用於建置 ajax 查詢的座標屬性名稱。",
+ "semanticmaps-par-ajaxquery": "透過 ajax 發送來索取額外座標的第二個查詢。",
+ "semanticmaps-par-userparam": "在使用模板時,傳遞給各模板呼叫時的值",
+ "semanticmaps-kml-text": "與每個頁面關聯的文字。可被額外的查詢屬性所覆蓋 (若有設定)。",
+ "semanticmaps-kml-title": "查詢結果的預設標題",
+ "semanticmaps-kml-linkabsolute": "連結應為絕對位址 (相反於相對位址)",
+ "semanticmaps-kml-pagelinktext": "連結至頁面要使用的文字,文字中的 $1 會取代為頁面標題",
+ "semanticmaps-shapes-improperformat": "$1 的格式有誤,請參考格式說明文件",
+ "semanticmaps-shapes-missingshape": "查無 $1 的形狀。請查看說明文件瞭解可用的形狀",
+ "validator-type-mapscircle": "地理圓形",
+ "validator-type-mapscircle-list": "圓形清單",
+ "validator-type-mapsimageoverlay": "圖片疊加",
+ "validator-type-mapsimageoverlay-list": "圖片疊加清單",
+ "validator-type-mapsline": "地理線形",
+ "validator-type-mapsline-list": "線形清單",
+ "validator-type-mapslocation": "地理座標",
+ "validator-type-mapslocation-list": "位置清單",
+ "validator-type-mapsrectangle": "地理矩形",
+ "validator-type-mapsrectangle-list": "矩形清單",
+ "validator-type-mapspolygon": "地理多邊形",
+ "validator-type-mapspolygon-list": "地理多邊形清單",
+ "validator-type-wmsoverlay": "網路地圖服務疊加",
+ "validator-type-jsonfile": "文字"
+}
diff --git a/www/wiki/extensions/Maps/phpunit.xml.dist b/www/wiki/extensions/Maps/phpunit.xml.dist
new file mode 100644
index 00000000..57a6b7fc
--- /dev/null
+++ b/www/wiki/extensions/Maps/phpunit.xml.dist
@@ -0,0 +1,29 @@
+<phpunit backupGlobals="false"
+ backupStaticAttributes="false"
+ bootstrap="tests/bootstrap.php"
+ cacheTokens="false"
+ colors="true"
+ convertErrorsToExceptions="true"
+ convertNoticesToExceptions="true"
+ convertWarningsToExceptions="true"
+ stopOnError="false"
+ stopOnFailure="false"
+ stopOnIncomplete="false"
+ stopOnSkipped="false">
+ <testsuites>
+ <testsuite name="MapsUnit">
+ <directory>tests/Unit</directory>
+ </testsuite>
+ <testsuite name="MapsIntegration">
+ <directory>tests/Integration</directory>
+ </testsuite>
+ <testsuite name="MapsSystem">
+ <directory>tests/System</directory>
+ </testsuite>
+ </testsuites>
+ <filter>
+ <whitelist addUncoveredFilesFromWhitelist="true">
+ <directory suffix=".php">src</directory>
+ </whitelist>
+ </filter>
+</phpunit>
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/ext.maps.googlemaps3.js b/www/wiki/extensions/Maps/resources/GoogleMaps/ext.maps.googlemaps3.js
new file mode 100644
index 00000000..ce5880c2
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/ext.maps.googlemaps3.js
@@ -0,0 +1,25 @@
+/**
+ * JavaScript for Google Maps v3 maps in the Maps extension.
+ * @see https://www.mediawiki.org/wiki/Extension:Maps
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw <jeroendedauw at gmail dot com>
+ */
+(function( $, mw ) {
+
+ $( document ).ready( function() {
+
+ if ( typeof google === 'undefined' ) {
+ $( '.maps-googlemaps3' ).text( mw.msg( 'maps-googlemaps3-incompatbrowser' ) );
+ }
+ else {
+ $( '.maps-googlemaps3' ).each( function() {
+ var $this = $( this );
+ var map = $this.googlemaps( $.parseJSON( $this.find( 'div').text() ) );
+ window.maps.googlemapsList.push(map);
+ } );
+ }
+
+ } );
+
+})( window.jQuery, mediaWiki );
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/ext.sm.googlemaps3ajax.js b/www/wiki/extensions/Maps/resources/GoogleMaps/ext.sm.googlemaps3ajax.js
new file mode 100644
index 00000000..9f8c658a
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/ext.sm.googlemaps3ajax.js
@@ -0,0 +1,48 @@
+/**
+ * JavaScript for Google Maps v3 maps in the Semantic Maps extension.
+ * @see https://www.mediawiki.org/wiki/Extension:Semantic_Maps
+ *
+ * @licence GNU GPL v2+
+ * @author Peter Grassberger < petertheone@gmail.com >
+ */
+
+
+(function( $, sm ) {
+ var ajaxRequest = null;
+ var mapEvents = ['dragend', 'zoom_changed'];
+
+ $( document ).ready( function() {
+ // todo: find a way to remove setTimeout.
+ setTimeout( function() {
+ if( typeof google === 'undefined' ) {
+ return;
+ }
+ $( window.maps.googlemapsList ).each( function( index, map ) {
+ if( !map.options.ajaxquery || !map.options.ajaxcoordproperty ) {
+ return;
+ }
+ $( mapEvents ).each( function( index, event ) {
+ google.maps.event.addListener( map.map, event, function() {
+ var bounds = map.map.getBounds();
+ var query = sm.buildQueryString(
+ decodeURIComponent( map.options.ajaxquery.replace( /\+/g, ' ' ) ),
+ map.options.ajaxcoordproperty,
+ bounds.getNorthEast().lat(),
+ bounds.getNorthEast().lng(),
+ bounds.getSouthWest().lat(),
+ bounds.getSouthWest().lng()
+ );
+
+ if( ajaxRequest !== null ) {
+ ajaxRequest.abort();
+ }
+ ajaxRequest = sm.ajaxUpdateMarker( map, query, map.options.icon ).done( function() {
+ map.createMarkerCluster();
+ ajaxRequest = null;
+ } );
+ } );
+ } );
+ } );
+ }, 500 );
+ } );
+})( window.jQuery, window.sm );
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ProjectedOverlay.js b/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ProjectedOverlay.js
new file mode 100644
index 00000000..4056c2c3
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ProjectedOverlay.js
@@ -0,0 +1,139 @@
+// Create an overlay on the map from a projected image - Maps v3...
+// Author. John D. Coryat 05/2009
+// USNaviguide LLC - http://www.usnaviguide.com
+// Thanks go to Mile Williams EInsert: http://econym.googlepages.com/einsert.js, Google's GOverlay Example and Bratliff's suggestion...
+// Opacity code from TPhoto: http://gmaps.tommangan.us/addtphoto.html
+// This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// Parameters:
+// map: This Map
+// imageUrl: URL of the image (Mandatory)
+// bounds: Bounds object of image destination (Mandatory)
+// Options:
+// addZoom: Added Zoom factor as a parameter to the imageUrl (include complete parameter, including separater like '?zoom='
+// percentOpacity: Default 50, percent opacity to use when the image is loaded 0-100.
+// id: Default imageUrl, ID of the div
+//
+
+function ProjectedOverlay(map, imageUrl, bounds, opts)
+{
+ google.maps.OverlayView.call(this);
+
+ this.map_ = map;
+ this.url_ = imageUrl ;
+ this.bounds_ = bounds ;
+ this.addZ_ = opts.addZoom || '' ; // Add the zoom to the image as a parameter
+ this.id_ = opts.id || this.url_ ; // Added to allow for multiple images
+ this.percentOpacity_ = opts.percentOpacity || 50 ;
+
+ this.setMap(map);
+}
+
+ProjectedOverlay.prototype = new google.maps.OverlayView();
+
+ProjectedOverlay.prototype.createElement = function()
+{
+ var panes = this.getPanes() ;
+ var div = this.div_ ;
+
+ if (!div)
+ {
+ div = this.div_ = document.createElement("div");
+ div.style.position = "absolute" ;
+ div.setAttribute('id',this.id_) ;
+ this.div_ = div ;
+ this.lastZoom_ = -1 ;
+ if( this.percentOpacity_ )
+ {
+ this.setOpacity(this.percentOpacity_) ;
+ }
+ panes.overlayLayer.appendChild(div);
+ }
+}
+
+// Remove the main DIV from the map pane
+
+ProjectedOverlay.prototype.remove = function()
+{
+ if (this.div_)
+ {
+ this.div_.parentNode.removeChild(this.div_);
+ this.div_ = null;
+ this.setMap(null);
+ }
+}
+
+// Redraw based on the current projection and zoom level...
+
+ProjectedOverlay.prototype.draw = function(firstTime)
+{
+ // Creates the element if it doesn't exist already.
+
+ this.createElement();
+
+ if (!this.div_)
+ {
+ return ;
+ }
+
+ var c1 = this.get('projection').fromLatLngToDivPixel(this.bounds_.getSouthWest());
+ var c2 = this.get('projection').fromLatLngToDivPixel(this.bounds_.getNorthEast());
+
+ if (!c1 || !c2) return;
+
+ // Now position our DIV based on the DIV coordinates of our bounds
+
+ this.div_.style.width = Math.abs(c2.x - c1.x) + "px";
+ this.div_.style.height = Math.abs(c2.y - c1.y) + "px";
+ this.div_.style.left = Math.min(c2.x, c1.x) + "px";
+ this.div_.style.top = Math.min(c2.y, c1.y) + "px";
+
+ // Do the rest only if the zoom has changed...
+
+ if ( this.lastZoom_ == this.map_.getZoom() )
+ {
+ return ;
+ }
+
+ this.lastZoom_ = this.map_.getZoom() ;
+
+ var url = this.url_ ;
+
+ if ( this.addZ_ )
+ {
+ url += this.addZ_ + this.map_.getZoom() ;
+ }
+
+ this.div_.innerHTML = '<img src="' + url + '" width=' + this.div_.style.width + ' height=' + this.div_.style.height + ' >' ;
+}
+
+ProjectedOverlay.prototype.setOpacity=function(opacity)
+{
+ if (opacity < 0)
+ {
+ opacity = 0 ;
+ }
+ if(opacity > 100)
+ {
+ opacity = 100 ;
+ }
+ var c = opacity/100 ;
+
+ if (typeof(this.div_.style.filter) =='string')
+ {
+ this.div_.style.filter = 'alpha(opacity:' + opacity + ')' ;
+ }
+ if (typeof(this.div_.style.KHTMLOpacity) == 'string' )
+ {
+ this.div_.style.KHTMLOpacity = c ;
+ }
+ if (typeof(this.div_.style.MozOpacity) == 'string')
+ {
+ this.div_.style.MozOpacity = c ;
+ }
+ if (typeof(this.div_.style.opacity) == 'string')
+ {
+ this.div_.style.opacity = c ;
+ }
+}
+
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/README b/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/README
new file mode 100644
index 00000000..a82f556e
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/README
@@ -0,0 +1,5 @@
+geoxml3.js and ZipFile.complete.js has been fetched from the googlecode project : http://code.google.com/p/geoxml3/
+from the branch kmz (which seems to be the branch containing the most features)
+http://code.google.com/p/geoxml3/source/browse/#svn%2Fbranches%2Fkmz
+
+The latest version fetched when writing this was rev 79. \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ZipFile.complete.js b/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ZipFile.complete.js
new file mode 100644
index 00000000..d49bb056
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/ZipFile.complete.js
@@ -0,0 +1,2172 @@
+// ZipFile.complete.js
+//
+// 2/17/2012
+//
+// =======================================================
+//
+
+// JSIO.core.js
+// ------------------------------------------------------------------
+//
+// core methods for Javascript IO.
+//
+// =======================================================
+//
+// Copyleft (c) 2010, Dino Chiesa via MS-PL
+// Copyleft (c) 2012, Brendan Byrd via GPL
+//
+// This work is licensed under the GPLv3.
+
+
+(function(){
+ if (typeof JSIO == "object"){
+ var e1 = new Error("JSIO is already defined");
+ e1.source = "JSIO.core.js";
+ throw e1;
+ }
+
+ JSIO = {};
+
+ JSIO.version = "2.0 2012Feb";
+
+ JSIO.throwError = function(msg, source, sub) {
+ var error = new Error("Error: " + msg);
+ error.source = (source || this._typename || 'JSIO') + (sub ? '.'+sub : '');
+ throw error;
+ }
+
+ // Format a number as hex. Quantities over 7ffffff will be displayed properly.
+ JSIO.decimalToHexString = function(number, digits) {
+ if (number < 0) {
+ number = 0xFFFFFFFF + number + 1;
+ }
+ var r1 = number.toString(16).toUpperCase();
+ if (digits) {
+ r1 = "00000000" + r1;
+ r1 = r1.substring(r1.length - digits);
+ }
+ return r1;
+ };
+
+ JSIO.FileType = {
+ Text : 0,
+ Binary : 1,
+ XML : 2,
+ Unknown : 3
+ };
+
+
+ JSIO.guessFileType = function(name) {
+ if (name == "makefile") { return JSIO.FileType.Text; }
+
+ var lastDot = name.lastIndexOf(".");
+ if (lastDot <= 0) { return JSIO.FileType.Unknown; }
+
+ var ext= name.substring(lastDot);
+ if (ext == ".zip") { return JSIO.FileType.Binary; }
+ if (ext == ".xlsx") { return JSIO.FileType.Binary; }
+ if (ext == ".docx") { return JSIO.FileType.Binary; }
+ if (ext == ".dll") { return JSIO.FileType.Binary; }
+ if (ext == ".obj") { return JSIO.FileType.Binary; }
+ if (ext == ".pdb") { return JSIO.FileType.Binary; }
+ if (ext == ".exe") { return JSIO.FileType.Binary; }
+ if (ext == ".kmz") { return JSIO.FileType.Binary; }
+
+ if (ext == ".xml") { return JSIO.FileType.XML; }
+ if (ext == ".xsl") { return JSIO.FileType.XML; }
+ if (ext == ".kml") { return JSIO.FileType.XML; }
+ if (ext == ".csproj") { return JSIO.FileType.XML; }
+ if (ext == ".vbproj") { return JSIO.FileType.XML; }
+ if (ext == ".shfbproj") { return JSIO.FileType.XML; }
+ if (ext == ".resx") { return JSIO.FileType.XML; }
+ if (ext == ".xslt") { return JSIO.FileType.XML; }
+
+ if (ext == ".sln") { return JSIO.FileType.Text; }
+ if (ext == ".htm") { return JSIO.FileType.Text; }
+ if (ext == ".html") { return JSIO.FileType.Text; }
+ if (ext == ".js") { return JSIO.FileType.Text; }
+ if (ext == ".vb") { return JSIO.FileType.Text; }
+ if (ext == ".txt") { return JSIO.FileType.Text; }
+ if (ext == ".rels") { return JSIO.FileType.Text; }
+ if (ext == ".css") { return JSIO.FileType.Text; }
+ if (ext == ".cs") { return JSIO.FileType.Text; }
+ if (ext == ".asp") { return JSIO.FileType.Text; }
+
+ return JSIO.FileType.Unknown;
+ };
+
+ JSIO.stringOfLength = function (charCode, length) {
+ var s3 = "";
+ for (var i = 0; i < length; i++) {
+ s3 += String.fromCharCode(charCode);
+ }
+ return s3;
+ };
+
+ JSIO.formatByteArray = function(b) {
+ var s1 = "0000 ";
+ var s2 = "";
+ for (var i = 0; i < b.length; i++) {
+ if (i !== 0 && i % 16 === 0) {
+ s1 += " " + s2 +"\n" + JSIO.decimalToHexString(i, 4) + " ";
+ s2 = "";
+ }
+ s1 += JSIO.decimalToHexString(b[i], 2) + " ";
+ if (b[i] >=32 && b[i] <= 126) {
+ s2 += String.fromCharCode(b[i]);
+ } else {
+ s2 += ".";
+ }
+ }
+ if (s2.length > 0) {
+ s1 += JSIO.stringOfLength(32, ((i%16>0)? ((16 - i%16) * 3) : 0) + 4) + s2;
+ }
+ return s1;
+ };
+
+ JSIO.htmlEscape = function(str) {
+ return str
+ .replace(new RegExp( "&", "g" ), "&amp;")
+ .replace(new RegExp( "<", "g" ), "&lt;")
+ .replace(new RegExp( ">", "g" ), "&gt;")
+ .replace(new RegExp( "\x13", "g" ), "<br/>")
+ .replace(new RegExp( "\x10", "g" ), "<br/>");
+ };
+
+ JSIO.massApply = function(func, funcThis, arr, needReturn) {
+ var arrayLimit = 99999; // Chrome has an apply/array limit of 99999; Firefox = 491519
+ if (arr.length < arrayLimit) return func.apply(funcThis, arr);
+ else {
+ var newThis = funcThis;
+ var offset = 0;
+ var end = 99999;
+
+ while (offset < arr.length) {
+ var arrSlice;
+ if (arr.subarray) arrSlice = arr.subarray(offset, end);
+ else if (arr.slice) arrSlice = arr.slice(offset, end);
+
+ if (needReturn) newThis += func.apply(newThis, arrSlice);
+ else func.apply(funcThis, arrSlice);
+
+ offset += arrayLimit;
+ end += arrayLimit;
+ end = Math.min(arr.length, end);
+ }
+ return newThis;
+ }
+ }
+
+})();
+
+/// JSIO.core.js ends
+
+
+// JSIO.BasicByteReaders.js
+// ------------------------------------------------------------------
+//
+// Part of the JSIO library. Adds a couple basic ByteReaders to JSIO.
+// ByteReaders are forward-only byte-wise readers. They read one byte at
+// a time from a source.
+//
+// =======================================================
+//
+// A ByteReader exposes an interface with these functions:
+//
+// readByte()
+// must return null when EOF is reached.
+//
+// readToEnd()
+// returns an array of all bytes read, to EOF
+//
+// beginReadToEnd(callback)
+// async version of the above
+//
+// readBytes(n)
+// returns an array of the next n bytes from the source
+//
+// beginReadBytes(n, callback)
+// async version of the above
+//
+// =======================================================
+//
+// Copyleft (c) 2010, Dino Chiesa via MS-PL
+// Copyleft (c) 2012, Brendan Byrd via GPL
+//
+// This work is licensed under the GPLv3.
+
+
+(function(){
+ var version = "2.0 2012Feb";
+
+ if (typeof JSIO !== "object") { JSIO = {}; }
+ if ((typeof JSIO.version !== "string")) {
+ JSIO.version = version;
+ }
+ else if ((JSIO.version.length < 3) ||
+ (JSIO.version.substring(0,3) !== "2.0")) {
+ JSIO.version += " " + version;
+ }
+
+ // =======================================================
+ // the base object, used as the prototype of all ByteReader objects.
+ var _byteReaderBase = function () {
+ this.position = 0;
+ // position must be incremented in .readByte() for all derived classes
+ };
+
+ _byteReaderBase.prototype._throwError = JSIO.throwError;
+
+ _byteReaderBase.prototype._limitCheck = function(len, startPos) {
+ var LOE = {
+ len: len,
+ pos: startPos,
+ end: startPos+len
+ };
+
+ if (len === 0) return {len:0, pos:0, end:0};
+ if (len < 0) this._throwError("Invalid read length");
+ if (!this.length) return {len:len, pos:this.position, end:len+this.position};
+ if (!startPos >= 0) LOE.pos = this.position;
+ if (this.length <= LOE.pos) this._throwError("EOF reached");
+
+ LOE.end = LOE.pos+len;
+ if (this.length < LOE.end) LOE.end = LOE.pos+(LOE.len = this.length-this.position);
+ return LOE;
+ }
+
+ JSIO.SeekOrigin = {
+ Begin : 0,
+ Current : 1,
+ End : 2,
+ SEEK_SET : 0,
+ SEEK_CUR : 1,
+ SEEK_END : 2
+ };
+
+ _byteReaderBase.prototype.seek = function(offset, origin) {
+ switch (origin) {
+ case JSIO.SeekOrigin.Begin:
+ if (offset == this.position) return this.position;
+ if (!this.length) {
+ if (offset < this.position) this._throwError('Uni-directional stream cannot seek backwards', null, 'seek');
+ else if (offset > this.position) return this.read(offset - this.position); // read will limit check
+ }
+ else {
+ if (this.length < offset) this._throwError('Cannot seek past reader length', null, 'seek');
+ this.position = offset;
+ }
+ break;
+ case JSIO.SeekOrigin.Current:
+ return this.seek(this.position + offset, JSIO.SeekOrigin.Begin);
+ break;
+ case JSIO.SeekOrigin.End:
+ if (!this.length) this._throwError('Uni-directional stream has no known end length for seek', null, 'seek');
+ return this.seek(this.length - 1 + offset, JSIO.SeekOrigin.Begin);
+ break;
+ default:
+ this._throwError('Invalid seek method', null, 'seek');
+ break;
+ }
+
+ return this.position;
+ };
+
+ _byteReaderBase.prototype.read = function(len, startPos) {
+ var LOE = this._limitCheck(len, startPos);
+ if (LOE.len === 0) return [];
+ if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin);
+
+ var bytesRead = [];
+
+ // Faster methods with an array or stream
+ if (this.array && this.array.subarray) bytesRead = this.array.subarray(LOE.pos, LOE.end);
+ else if (this.array && this.array.slice) bytesRead = this.array.slice(LOE.pos, LOE.end);
+ else if (this.stream) bytesRead = this.stream.read(LOE.len, LOE.pos);
+ else if (this.length) { // Random-access stream
+ for(var i=LOE.pos; i<LOE.end; i++) { bytesRead.push(this.readByteAt(i)); }
+ }
+ else { // Uni-directional stream
+ for(var i=LOE.pos; i<LOE.end; i++) {
+ var b = this.readByte();
+ if (b === null || b === undefined) break;
+ bytesRead.push(b);
+ }
+ }
+ this.position = LOE.end;
+ return bytesRead;
+ };
+
+ _byteReaderBase.prototype.beginRead = function(len, startPos, callback) {
+ var LOE = this._limitCheck(len, startPos);
+ if (LOE.len === 0) return setTimeout(function() { callback([]); }, 1);
+ if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin);
+
+ var bytesRead = [];
+ var thisReader = this;
+ var leftToRead = LOE.len;
+
+ var readBatchAsync = function() {
+ var c = 0;
+ var pos = thisReader.position;
+
+ // read a 32k batch
+ var l = (leftToRead >= 32768) ? 32768 : leftToRead;
+ var newBytes = thisReader.read(l);
+ JSIO.massApply(bytesRead.push, bytesRead, newBytes);
+ c += l;
+ leftToRead -= l;
+ if (newBytes.length < l) leftToRead = 0;
+
+ if (leftToRead>0) setTimeout(readBatchAsync, 1);
+ else callback(bytesRead);
+ };
+
+ // kickoff
+ setTimeout(readBatchAsync, 1); // always async, in ALL situations
+ return null;
+ };
+
+ _byteReaderBase.prototype.readToEnd = function() {
+ if (this.array && this.array.subarray) return this.array.subarray(this.position);
+ else if (this.array && this.array.slice) return this.array.slice(this.position);
+ else if (this.length) return this.read(this.length - this.position);
+ else return this.read(9000 * 9000); // over 9000
+ };
+
+ _byteReaderBase.prototype.beginReadToEnd = function(callback) {
+ if (this.array && this.array.subarray) setTimeout(function() { callback( this.array.subarray(this.position) ); }, 1);
+ else if (this.array && this.array.slice) setTimeout(function() { callback( this.array.slice(this.position) ); }, 1);
+ else if (this.length) return this.beginRead(this.length - this.position, this.position, callback);
+ else return this.beginRead(9000 * 9000, this.position, callback);
+ };
+
+ // Generic routines; one of these two MUST be overloaded (preferrably both)
+ _byteReaderBase.prototype.readByte = function(){
+ if (this.length && this.position >= this.length) return null; // EOF
+
+ var byte;
+ if (this.array) byte = this.array[this.position++];
+ else if (this.length) byte = this.readByteAt(this.position++);
+ else if (this.stream) byte = this.stream.read(1)[0];
+ else byte = this.read(1)[0];
+ return (byte === null || byte === undefined) ? null : byte;
+ };
+ _byteReaderBase.prototype.readByteAt = function(i) {
+ var pos = this.position; // no position changes on this one
+ if (i === null || i === undefined) i = this.position;
+
+ var byte;
+ if (this.array) byte = this.array[i];
+ else if (i === pos) byte = this.readByte();
+ else if (this.stream) byte = this.stream.read(1, i)[0];
+ else byte = this.read(1, i)[0];
+
+ this.position = pos;
+ return (byte === null || byte === undefined) ? null : byte;
+ }
+
+ _byteReaderBase.prototype.readBytes = _byteReaderBase.prototype.read;
+ _byteReaderBase.prototype.beginReadBytes = function(len, callback) { return this.beginRead(len, this.position, callback); };
+
+ _byteReaderBase.prototype.readNumber = function(len, startPos){
+ var LOE = this._limitCheck(len, startPos);
+ if (LOE.len === 0) LOE.len = 1;
+ if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin);
+
+ var result = 0;
+ var bytes = this.read(LOE.len, LOE.pos);
+ for (var i=bytes.length-1; i>=0; i--) {
+ // IE only supports 32-bit integer shifting
+ //result = result << 8 | bytes[i];
+ result = result*256 + bytes[i];
+ }
+ return result;
+ };
+
+ _byteReaderBase.prototype.readString = function(len, startPos){
+ var LOE = this._limitCheck(len, startPos);
+ if (LOE.len === 0) return '';
+ if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin);
+
+ var result = '';
+ var bytes = this.read(LOE.len, LOE.pos);
+ for(var i=0; i<bytes.length; i++){
+ result += String.fromCharCode(bytes[i]);
+ }
+ return result;
+ };
+
+ _byteReaderBase.prototype.readNullTerminatedString = function(startPos){
+ var pos = startPos || this.position;
+ if (this.length && this.length < pos) this._throwError('EOF reached', null, 'readNullTerminatedString');
+ if (pos != this.position) this.seek(pos, JSIO.SeekOrigin.Begin);
+
+ var slarge = "";
+ var s = "";
+ var c = 0;
+
+ // Faster method with an array
+ if (this.array && this.array.indexOf) {
+ var len = pos - this.array.indexOf(0, pos);
+ if (len > 0) return this.readString(len, pos);
+ }
+
+ var ch;
+ while(1) {
+ ch = String.fromCharCode(this.readByteAt(pos+c));
+ if (ch === null) break;
+
+ s += ch;
+ c++;
+ if(c >= 32768) {
+ slarge += s;
+ s = "";
+ pos += c;
+ this.position += c;
+ c = 0;
+ }
+ };
+ this.position = pos + c;
+ return slarge + s;
+ };
+
+ _byteReaderBase.prototype.beginReadNullTerminatedString = function(callback, startPos){
+ var pos = startPos || this.position;
+ if (this.length && this.length < pos) this._throwError('EOF reached', null, 'beginReadNullTerminatedString');
+
+ var slarge = "";
+ var s = "";
+ var thisBinStream = this;
+
+ var readBatchAsync = function() {
+ var c = 0;
+
+ var ch;
+ while(1) {
+ ch = String.fromCharCode(this.readByteAt(pos+c));
+ if (ch === null) break;
+
+ s += ch;
+ c++;
+ if(c >= 32768) {
+ slarge += s;
+ s = "";
+ pos += c;
+ this.position += c;
+ c = 0;
+ }
+ };
+
+ thisBinStream.position = pos + c;
+ if (ch!==null) setTimeout(readBatchAsync, 1);
+ else callback(slarge+s);
+ };
+
+ // Faster method with an array
+ if (this.array && this.array.indexOf) {
+ var len = pos - this.array.indexOf(0, pos);
+ if (len > 0) readBatchASync = function() { callback(thisBinStream.readString(len, pos)); };
+ }
+
+ // kickoff
+ setTimeout(readBatchAsync, 1); // always async, in ALL situations
+ return null;
+ };
+
+
+
+ JSIO._ByteReaderBase = _byteReaderBase;
+ // =======================================================
+
+
+
+
+ // =======================================================
+ // reads from an array of bytes.
+ // This basically wraps a readByte() fn onto array access.
+ var _arrayReader = function(array) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.ArrayReader', 'ctor');
+ this.position = 0;
+ this.array = array;
+ this.length = array.length;
+ this._typename = "JSIO.ArrayReader";
+ this._version = version;
+ return this;
+ };
+
+ _arrayReader.prototype = new JSIO._ByteReaderBase();
+
+ _arrayReader.prototype.readByte = function() {
+ if (this.position >= this.array.length) return null; // EOF
+ return this.array[this.position++];
+ };
+ _arrayReader.prototype.readByteAt = function(i) {
+ return this.array[i];
+ };
+
+ // =======================================================
+
+
+ // =======================================================
+ // reads bytes at a time from a defined segment of a stream.
+ var _streamSegmentReader = function(stream, offset, len) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.StreamSegmentReader', 'ctor');
+ if (!stream) this._throwError('You must pass a non-null stream', 'JSIO.StreamSegmentReader', 'ctor');
+
+ if (!(offset >= 1)) offset = 0;
+ if (!(len >= 1)) len = 0;
+
+ this.stream = stream;
+ this.array = null;
+ if (stream.array) {
+ var end = len ? offset + len : null;
+ if (stream.array.subarray) this.array = stream.array.subarray(offset, end);
+ else if (stream.array.slice) this.array = stream.array.slice(offset, end);
+ }
+ this.length = this.array ? this.array.length : (stream.length ? stream.length - offset : null);
+ this.offset = offset;
+ this.limit = len;
+ this.position = 0;
+ this._typeName = 'JSIO.StreamSegmentReader';
+ this._version = version;
+
+ if (this.array) {
+ this.readByte = _arrayReader.prototype.readByte;
+ this.readByteAt = _arrayReader.prototype.readByteAt;
+ }
+ return this;
+ };
+
+ _streamSegmentReader.prototype = new JSIO._ByteReaderBase();
+
+ _streamSegmentReader.prototype.readByte = function() {
+ if (this.limit && this.position >= this.limit) return null; // EOF
+ this.position++;
+ return this.stream.readByteAt(this.offset + this.position - 1);
+ };
+ _streamSegmentReader.prototype.readByteAt = function(i) {
+ if (this.limit && i >= this.limit) return null; // EOF
+ return this.stream.readByteAt(this.offset + i);
+ };
+
+ // =======================================================
+
+ JSIO.ArrayReader = _arrayReader;
+ JSIO.StreamReader = _streamSegmentReader;
+ JSIO.StreamSegmentReader = _streamSegmentReader;
+
+})();
+
+
+/// JSIO.BasicByteReaders.js ends
+
+// JSIO.BinaryUrlStream.js
+// ------------------------------------------------------------------
+//
+// a class that acts as a stream wrapper around binary files obtained from URLs.
+//
+// =======================================================
+//
+// Copyleft (c) 2008, Andy G.P. Na <nagoon97@naver.com> via an MIT-style license
+// Copyleft (c) 2012, Brendan Byrd via GPL
+//
+// This work is licensed under the GPLv3.
+
+(function(){
+ var version = "2.0 2012Feb";
+ var typename = "JSIO.BinaryUrlStream";
+
+ if ((typeof JSIO !== "object") ||
+ (typeof JSIO.version !== "string") ||
+ (JSIO.version.length < 3) ||
+ (JSIO.version.substring(0,3) !== "2.0"))
+ JSIO.throwError('This extension requires JSIO.core.js v2.0', typename);
+
+ if (typeof JSIO._ByteReaderBase !== "function")
+ JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename);
+
+ if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) {
+ var IEBinaryToArray_ByteStr_Script =
+ "<!-- IEBinaryToArray_ByteStr -->\r\n"+
+ "<script type='text/vbscript'>\r\n"+
+ "Function IEBinaryToArray_ByteStr(Binary)\r\n"+
+ " IEBinaryToArray_ByteStr = CStr(Binary)\r\n"+
+ "End Function\r\n"+
+ "Function IEBinaryToArray_ByteAsc_Last(Binary)\r\n"+
+ " Dim lastIndex\r\n"+
+ " lastIndex = LenB(Binary)\r\n"+
+ " if lastIndex mod 2 Then\r\n"+
+ " IEBinaryToArray_ByteAsc_Last = AscB( MidB( Binary, lastIndex, 1 ) )\r\n"+
+ " Else\r\n"+
+ " IEBinaryToArray_ByteAsc_Last = -1\r\n"+
+ " End If\r\n"+
+ "End Function\r\n"+
+ "</script>\r\n";
+
+ // inject VBScript
+ document.write(IEBinaryToArray_ByteStr_Script);
+ }
+
+ JSIO.IEByteMapping = null;
+
+ var bus = function(url, callback) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.BinaryUrlStream', 'ctor');
+
+ this.callback = callback;
+ this.position = 0;
+ this.length = null;
+ this.readByte = JSIO.ArrayReader.prototype.readByte;
+ this.readByteAt = JSIO.ArrayReader.prototype.readByteAt;
+ this.req = null;
+ this._typename = typename;
+ this._version = version;
+ this.status = "-none-";
+
+ var _IeGetBinResource = function(fileURL){
+ var binStream = this;
+ // http://stackoverflow.com/questions/1919972/how-do-i-access-xhr-responsebody-for-binary-data-from-javascript-in-ie
+
+ // my helper to convert from responseBody to a byte array
+ var convertResponseBodyToArray = function (binary) {
+ var byteArray = new Array;
+
+ try {
+ // very fast; very little work involved
+ byteArray = new VBArray(binary).toArray();
+ }
+ catch(err) {
+ // use the BinaryToArray VBScript
+ if (!JSIO.IEByteMapping) {
+ JSIO.IEByteMapping = {};
+ for ( var i = 0; i < 256; i++ ) {
+ for ( var j = 0; j < 256; j++ ) {
+ JSIO.IEByteMapping[ String.fromCharCode( i + j * 256 ) ] = [ i, j ];
+ }
+ }
+ }
+ var rawBytes = IEBinaryToArray_ByteStr(binary);
+ var lastAsc = IEBinaryToArray_ByteAsc_Last(binary);
+
+ for ( var i = 0; i < rawBytes.length; i++ ) {
+ byteArray.push.apply(byteArray, JSIO.IEByteMapping[ rawBytes.substr(i,1) ]);
+ }
+ if (lastAsc >= 0) byteArray.push(lastAsc);
+ }
+
+ return byteArray;
+ };
+
+ this.req = (function() {
+ if (window.XMLHttpRequest) return new window.XMLHttpRequest();
+ else if (window.ActiveXObject) {
+ // the many versions of IE's XML fetchers
+ var AXOs = [
+ 'MSXML2.XMLHTTP.6.0',
+ 'MSXML2.XMLHTTP.5.0',
+ 'MSXML2.XMLHTTP.4.0',
+ 'MSXML2.XMLHTTP.3.0',
+ 'MSXML2.XMLHTTP',
+ 'Microsoft.XMLHTTP',
+ 'MSXML.XMLHTTP'
+ ];
+ for (var i = 0; i < AXOs.length; i++) {
+ try { return new ActiveXObject(AXOs[i]); }
+ catch(e) { continue; }
+ }
+ }
+ return null;
+ })();
+ this.req.open("GET", fileURL, true);
+ this.req.setRequestHeader("Accept-Charset", "x-user-defined");
+ this.req.onreadystatechange = function(event){
+ if (binStream.req.readyState == 4) {
+ binStream.status = "Status: " + binStream.req.status + ' ' + binStream.req.statusText;
+ if (binStream.req.status == 200) {
+ binStream.array = convertResponseBodyToArray(binStream.req.responseBody);
+ binStream.length = binStream.array.length;
+ if (binStream.length < 0) this._throwError('Failed to load "'+ fileURL + '" after converting');
+
+ if (typeof binStream.callback == "function") binStream.callback(binStream);
+ }
+ else {
+ binStream._throwError('Failed to load "'+ fileURL + '": HTTP ' + binStream.status);
+ }
+ }
+ };
+ this.req.send();
+ };
+
+ var _NormalGetBinResource = function(fileURL){
+ var binStream= this;
+ this.req = new XMLHttpRequest();
+ this.req.open('GET', fileURL, true);
+ this.req.onreadystatechange = function(aEvt) {
+ if (binStream.req.readyState == 4) {
+ binStream.status = "Status: " + binStream.req.status + ' ' + binStream.req.statusText;
+ if(binStream.req.status == 200){
+ var fileContents = binStream.req.responseText;
+ binStream.length = fileContents.byteLength;
+ binStream.array = fileContents.split('');
+ for ( var i = 0; i < binStream.array.length; i++ ) {
+ binStream.array[i] = binStream.array[i].charCodeAt(0) & 0xff;
+ }
+
+ if (typeof binStream.callback == "function") binStream.callback(binStream);
+ }
+ else {
+ binStream._throwError('Failed to load "'+ fileURL + '": HTTP ' + binStream.status);
+ }
+ }
+ };
+ //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
+ this.req.overrideMimeType('text/plain; charset=x-user-defined');
+ this.req.send(null);
+ };
+
+ // http://stackoverflow.com/questions/327685/is-there-a-way-to-read-binary-data-into-javascript
+ var _ArrayBufferGetBinResource = function(fileURL){
+ var binStream= this;
+ this.req = new XMLHttpRequest();
+ this.req.open('GET', fileURL, true);
+ this.req.onreadystatechange = function(aEvt) {
+ if (binStream.req.readyState == 4) {
+ binStream.status = "Status: " + binStream.req.status + ' ' + binStream.req.statusText;
+ if(binStream.req.status == 200){
+ var fileContents = binStream.req.response;
+ binStream.length = fileContents.byteLength;
+ binStream.array = new Uint8Array(fileContents);
+
+ if (typeof binStream.callback == "function") binStream.callback(binStream);
+ }
+ else {
+ binStream._throwError('Failed to load "'+ fileURL + '": HTTP ' + binStream.status);
+ }
+ }
+ };
+ this.req.responseType = 'arraybuffer';
+ this.req.overrideMimeType('text/plain; charset=x-user-defined');
+ this.req.send(null);
+ };
+
+
+ if (typeof ArrayBuffer !== 'undefined') _ArrayBufferGetBinResource.apply(this, [url]);
+ else if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) _IeGetBinResource.apply(this, [url]);
+ else _NormalGetBinResource.apply(this, [url]);
+ };
+
+ bus.prototype = new JSIO._ByteReaderBase();
+
+ bus.prototype.readByte = function(){
+ var byte = this.readByteAt(this.position++);
+ return (byte === null || byte === undefined) ? null : byte;
+ };
+
+ JSIO.BinaryUrlStream = bus;
+
+})();
+
+/// JSIO.BinaryUrlStream.js ends
+
+// JSIO.TextDecoder.js
+// ------------------------------------------------------------------
+//
+// Part of the JSIO library. Adds text decoders, for UTF-8 and UTF-16,
+// and plain text.
+//
+// =======================================================
+//
+// Derived in part from work by notmasteryet.
+// http://www.codeproject.com/KB/scripting/Javascript_binaryenc.aspx
+
+// Copyleft (c) 2008, notmasteryet via an MIT-style license
+// Copyleft (c) 2010, Dino Chiesa via MS-PL
+// Copyleft (c) 2012, Brendan Byrd via GPL
+//
+// This work is licensed under the GPLv3.
+
+(function(){
+ var version = "2.0 2012Feb";
+ var typename = "JSIO.TextDecoder";
+
+ if ((typeof JSIO !== "object") ||
+ (typeof JSIO.version !== "string") ||
+ (JSIO.version.length < 3) ||
+ (JSIO.version.substring(0,3) !== "2.0"))
+ JSIO.throwError('This extension requires JSIO.core.js v2.0', typename);
+
+ if (typeof JSIO._ByteReaderBase !== "function")
+ JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename);
+
+ var _ansi = function(reader) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.TextDecoder.ANSI', 'ctor');
+ this.byteReader = reader;
+ this.charWidth = 1;
+ this._version = version;
+ this._typename = typename + ".ANSI";
+ return this;
+ };
+
+ _ansi.prototype.readChar = function() {
+ var code = this.byteReader.readByte();
+ return (code < 0) ? null : String.fromCharCode(code);
+ };
+
+ _ansi.prototype.parseChar = function(code) {
+ return (code < 0) ? null : String.fromCharCode(code);
+ };
+
+ var _utf16 = function(reader) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.TextDecoder.UTF16', 'ctor');
+ this.byteReader = reader;
+ this.charWidth = 2;
+ this.bomState = 0;
+ this._version = version;
+ this._typename = typename + ".UTF16";
+ return this;
+ };
+
+ _utf16.prototype.readChar = function() {
+ var b1 = this.byteReader.readByte();
+ if (b1 < 0) return null;
+ var b2 = this.byteReader.readByte();
+ if (b2 < 0) this._throwError('Incomplete UTF16 character', null, 'readChar');
+
+ if ((this.bomState === 0) && ((b1 + b2) == 509)) {
+ this.bomState = (b2 == 254) ? 1 : 2;
+
+ b1 = this.byteReader.readByte();
+ if (b1 < 0) return null;
+ b2 = this.byteReader.readByte();
+ if (b2 < 0) this._throwError('Incomplete UTF16 character', null, 'readChar');
+ }
+ else {
+ this.bomState = 1;
+ }
+ return this.parseChar(b1, b2);
+ };
+
+ _utf16.prototype.parseChar = function(b1, b2) {
+ return String.fromCharCode( this.bomState == 1 ? (b2 << 8 | b1) : (b1 << 8 | b2) );
+ }
+
+ /* RFC 3629 */
+ var _utf8 = function(reader) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.TextDecoder.UTF8', 'ctor');
+ this.byteReader = reader;
+ this.charWidth = null;
+ this.waitBom = true;
+ this.strict = false;
+ this.pendingChar = null;
+ this._version = version;
+ this._typename = typename + ".UTF8";
+ return this;
+ };
+
+ _utf8.prototype.readChar = function() {
+ var ch = null;
+ do {
+ if (this.pendingChar !== null) {
+ ch = this.pendingChar;
+ this.pendingChar = null;
+ }
+ else {
+ var b1 = this.byteReader.readByte();
+ if (b1 === null) return null;
+
+ if ((b1 & 0x80) === 0) ch = String.fromCharCode(b1);
+ else {
+ var currentPrefix = 0xC0;
+ var ttlBytes = 0;
+ do {
+ var mask = currentPrefix >> 1 | 0x80;
+ if((b1 & mask) == currentPrefix) break;
+ currentPrefix = currentPrefix >> 1 | 0x80;
+ } while(++ttlBytes < 5);
+
+ if (ttlBytes > 0) {
+ var code;
+ if (ttlBytes === 1) code = (b1 & 0x1F) << 6 | (this.byteReader.readByte() & 0x3F);
+ else {
+ code = code << 6*ttlBytes
+ var bytes = this.byteReader.read(ttlBytes);
+ for (var i = 0; i > ttlBytes; i++) {
+ var bi = bytes[i];
+ if ((bi & 0xC0) != 0x80) this._throwError('Invalid sequence character', null, 'readChar');
+ code = (code << 6) | (bi & 0x3F);
+ }
+ }
+
+ if (code <= 0xFFFF) {
+ ch = (code == 0xFEFF && this.waitBom) ? null : String.fromCharCode(code);
+ }
+ else {
+ var v = code - 0x10000;
+ var w1 = 0xD800 | ((v >> 10) & 0x3FF);
+ var w2 = 0xDC00 | (v & 0x3FF);
+ this.pendingChar = String.fromCharCode(w2);
+ ch = String.fromCharCode(w1);
+ }
+ }
+ else {
+ // a byte higher than 0x80.
+ if (this.strict) this._throwError('Invalid character', null, 'readChar');
+ // fall back to "super ascii" (eg IBM-437)
+ else ch = String.fromCharCode(b1);
+ }
+ }
+ }
+ this.waitBom = false;
+ } while(ch === null);
+ return ch;
+ };
+
+ JSIO.TextDecoder = {
+ Default : _ansi,
+ ANSI : _ansi,
+ UTF16 : _utf16,
+ UTF8 : _utf8
+ };
+
+})();
+
+
+/// JSIO.TextDecoder.js ends
+
+// JSIO.TextReader.js
+// ------------------------------------------------------------------
+//
+// A reader class that decodes text as it reads.
+//
+// =======================================================
+//
+// Methods:
+// readChar() = read 1 char
+// read(n) = read n chars
+// readLine() = read one line of data (to \n)
+// unreadChar(ch) = unread one char
+// readToEnd() = read all data in the reader;
+// return a string.
+// beginReadToEnd(cb) = asynchronously read all data.
+//
+// =======================================================
+//
+// Derived in part from work by notmasteryet.
+// http://www.codeproject.com/KB/scripting/Javascript_binaryenc.aspx
+//
+// Copyleft (c) 2008, notmasteryet via an MIT-style license
+// Copyleft (c) 2010, Dino Chiesa via MS-PL
+// Copyleft (c) 2012, Brendan Byrd via GPL
+//
+// This work is licensed under the GPLv3.
+
+
+(function(){
+ var version = "2.0 2012Feb";
+ var typename = "JSIO.TextReader";
+
+ if (typeof JSIO._ByteReaderBase !== "function")
+ JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename);
+
+ var tr = function(textDecoder) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename, 'ctor');
+ this.decoder = textDecoder;
+ this._version = version;
+ this._typename = typename;
+ this.unreads = [];
+ };
+
+ // read one char
+ tr.prototype.readChar = function() {
+ return (this.unreads.length > 0) ? this.unreads.pop() : this.decoder.readChar();
+ };
+
+ // read a length of data
+ tr.prototype.read = function(n) {
+ // ANSI makes this easy
+ if (this.decoder.charWidth === 1) return JSIO.massApply(String.fromCharCode, new String, this.decoder.byteReader.read(n), true);
+
+ var s = "";
+ for (vari=0; i<n; i++) {
+ var ch = this.readChar();
+ if (ch !== null) s+= ch;
+ else break;
+ }
+ return s;
+ };
+
+ tr.prototype.unreadChar = function(ch) {
+ this.unreads.push(ch);
+ };
+
+ tr.prototype.readToEnd = function() {
+ // ANSI makes this easy
+ if (this.decoder.charWidth === 1) return JSIO.massApply(String.fromCharCode, new String, this.decoder.byteReader.readToEnd(n), true);
+
+ var slarge = "";
+ var s = "";
+ var c = 0;
+ var ch = this.readChar();
+ while(ch !== null) {
+ s += ch;
+ c++;
+ if(c >= 32768) {
+ slarge += s;
+ s = "";
+ c = 0;
+ }
+ ch = this.readChar();
+ }
+ return slarge + s;
+ };
+
+ tr.prototype.beginReadToEnd = function(callback) {
+ // ANSI makes this easy
+ if (this.decoder.charWidth === 1) {
+ this.decoder.byteReader.beginReadToEnd(function (bytes) {
+ callback( JSIO.massApply(String.fromCharCode, new String, bytes, true) );
+ });
+ return null;
+ }
+
+ var slarge = "";
+ var s = "";
+ var txtrdr = this;
+
+ var readBatchAsync = function() {
+ var c = 0;
+ var ch = txtrdr.readChar();
+ while(ch !== null) {
+ s += ch;c++;
+ if(c >= 32768) {
+ slarge += s;
+ s = "";
+ break;
+ }
+ ch = txtrdr.readChar();
+ }
+ if (ch!==null){
+ setTimeout(readBatchAsync, 1);
+ }
+ else {
+ callback(slarge+s);
+ }
+ };
+
+ // kickoff
+ setTimeout(readBatchAsync, 1); // always async, in ALL situations
+ return null;
+ };
+
+ tr.prototype.readLine = function() {
+ var s = "";
+ var ch = this.readChar();
+ if (ch === null) return null;
+
+ while(ch != "\r" && ch != "\n") {
+ s += ch;
+ ch = this.readChar();
+ if (ch === null) return s;
+ }
+ if(ch == "\r") {
+ ch = this.readChar();
+ if(ch !== null && ch != "\n"){
+ this.unreadChar(ch);
+ }
+ }
+ return s;
+ };
+
+ JSIO.TextReader = tr;
+
+})();
+
+
+/// JSIO.TextReader.js ends
+
+// JSIO.Crc32.js
+//
+// Part of the JSIO library. This adds an CRC32-calculating
+// ByteReader to JSIO.
+//
+// =======================================================
+//
+// A ByteReader exposes an interface with these functions:
+//
+// readByte()
+// must return null when EOF is reached.
+//
+// readToEnd()
+// returns an array of all bytes read, to EOF
+//
+// beginReadToEnd(callback)
+// async version of the above
+//
+// readBytes(n)
+// returns an array of all n bytes read from the source
+//
+// beginReadBytes(n, callback)
+// async version of the above
+//
+// =======================================================
+//
+// Copyleft (c) 2010, Dino Chiesa via MS-PL
+// Copyleft (c) 2012, Brendan Byrd via GPL
+//
+// This work is licensed under the GPLv3.
+
+(function(){
+ var version = "2.0 2012Feb";
+ var typename = "JSIO.Crc32";
+
+ if (typeof JSIO._ByteReaderBase !== "function")
+ JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename);
+
+ JSIO.crc32Table = null;
+ JSIO.crc32Polynomial = 0xEDB88320;
+
+ var crc32TableCalc = function () {
+ // do this once only, for all instances
+ if (JSIO.crc32Table) return;
+ JSIO.crc32Table = new Array(256);
+ for (var i = 0; i < 256; i++) {
+ var c=i;
+ for (var k = 0; k < 8; k++) {
+ if ((c & 1) == 1) c = JSIO.crc32Polynomial ^ (c>>>1);
+ else c >>>= 1;
+ }
+ JSIO.crc32Table[i] = c;
+ }
+ };
+
+ JSIO.computeCrc32 = function(str) {
+ crc32TableCalc(); // once
+ var c = 0xFFFFFFFF;
+ var sL = str.length;
+ if (typeof str == "object") {
+ for (var n1=0; n1<sL; n1++) {
+ c = JSIO.crc32Table[(c&0xff) ^ str[n1]] ^ (c>>>8);
+ }
+ } else {
+ for (var n2=0; n2<sL; n2++) {
+ c = JSIO.crc32Table[(c&0xff) ^ str.charCodeAt(n2)] ^ (c>>>8);
+ }
+ }
+ c ^= 0xFFFFFFFF;
+ if (c < 0) c += 0xFFFFFFFF+1;
+ return c;
+ };
+
+ // =======================================================
+ var _crc32 = function() {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename, 'ctor');
+ crc32TableCalc(); // once
+ this._typename = typename;
+ this._version = version;
+ this._runningCrc32 = 0xFFFFFFFF;
+ };
+
+ _crc32.prototype.slurpByte = function(b) {
+ var r = this._runningCrc32;
+ this._runningCrc32 = r>>>8 ^ JSIO.crc32Table[b ^ (r & 0x000000FF)];
+ };
+
+ _crc32.prototype.result = function() {
+ var c = this._runningCrc32 ^ 0xFFFFFFFF;
+ if (c < 0) c += 0xFFFFFFFF+1;
+ return c;
+ };
+ // =======================================================
+
+
+
+ var _crc32CalculatingReader = function(reader) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.Crc32Reader', 'ctor');
+ this._byteReader = reader;
+ this._typename = "JSIO.Crc32Reader";
+ this._version = version;
+ this._crc32 = new JSIO.Crc32();
+ };
+
+ _crc32CalculatingReader.prototype = new JSIO._ByteReaderBase();
+
+ _crc32CalculatingReader.prototype.readByte = function() {
+ var b = this._byteReader.readByte();
+ if (b !== null) this._crc32.slurpByte(b);
+ this.position++;
+ return b;
+ };
+
+ _crc32CalculatingReader.prototype.read = function(len) {
+ if (len === 0) return [];
+ var bytes = this._byteReader.read(len);
+ len = bytes.length;
+
+ var tbl = JSIO.crc32Table;
+ var r = this._crc32._runningCrc32;
+ var t;
+ for (var i = 0; i < len; i++) {
+ t = tbl[ bytes[i] ^ (r & 0x000000FF) ];
+ r = (r >>> 8) ^ t;
+ }
+ this._crc32._runningCrc32 = r;
+
+ this.position += len;
+ return bytes;
+ }
+
+ _crc32CalculatingReader.prototype.crc32 = function() {
+ return this._crc32.result();
+ };
+
+ JSIO.Crc32 = _crc32;
+ JSIO.Crc32Reader = _crc32CalculatingReader;
+
+})();
+
+/// JSIO.CRC32.js ends
+// JSIO.InflatingReader.js
+// ------------------------------------------------------------------
+//
+// Part of the JSIO library. This adds an Inflating ByteReader to
+// JSIO.
+//
+// =======================================================
+//
+// A ByteReader exposes an interface with these functions:
+//
+// readByte()
+// must return null when EOF is reached.
+//
+// readToEnd()
+// returns an array of all bytes read, to EOF
+//
+// beginReadToEnd(callback)
+// async version of the above
+//
+// readBytes(n)
+// returns an array of all n bytes read from the source
+//
+// beginReadBytes(n, callback)
+// async version of the above
+//
+// =======================================================
+//
+// Derived in part from work by notmasteryet.
+// http://www.codeproject.com/KB/scripting/Javascript_binaryenc.aspx
+//
+// Copyleft (c) 2008, notmasteryet via an MIT-style license
+// Copyleft (c) 2010, Dino Chiesa via MS-PL
+// Copyleft (c) 2012, Brendan Byrd via GPL
+//
+// This work is licensed under the GPLv3.
+
+
+(function(){
+ var version = "2.0 2012Feb";
+ var typename = "JSIO.InflatingReader";
+
+ if (typeof JSIO._ByteReaderBase !== "function")
+ JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename);
+
+ // =======================================================
+ // _InternalBitReader is used internally in the InflatingReader class.
+ //
+
+ JSIO.bitShiftTable = null;
+
+ var bitShiftTableCalc = function () {
+ // do this once only, for all instances
+ if (JSIO.bitShiftTable) return;
+
+ var bits = 8;
+ JSIO.bitShiftTable = {
+ LSB: new Array(bits),
+ MSB: new Array(bits)
+ }
+ for (var bp = 0; bp < bits; bp++) {
+ var lim = bits - bp;
+ JSIO.bitShiftTable.LSB[bp] = new Array(lim);
+ JSIO.bitShiftTable.MSB[bp] = new Array(lim);
+
+ var maskLSB = 1 << bp;
+ var maskMSB = 1 << lim-1;
+ for (var len = 1; len <= lim; len++) {
+ JSIO.bitShiftTable.LSB[bp][len-1] = maskLSB;
+ JSIO.bitShiftTable.MSB[bp][len-1] = maskMSB;
+ maskLSB |= 1 << bp+len;
+ maskMSB |= 1 << lim-len-1;
+ }
+ }
+ };
+
+ var _InternalBitReader = function(reader) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename + '._InternalBitReader.ctor');
+ this.bitsLength = 0;
+ this.bits = 0;
+ this.byteReader = reader;
+ this._typeName = typename + "._InternalBitReader";
+ this._version = version;
+ bitShiftTableCalc();
+ };
+
+ _InternalBitReader.prototype._throwError = JSIO.throwError;
+
+ _InternalBitReader.prototype.readBit = function() {
+ if (this.bitsLength === 0) {
+ var nextByte = this.byteReader.readByte();
+ if (nextByte === null) this._throwError('Unexpected end of stream', null, 'readBit');
+ this.bits = nextByte;
+ this.bitsLength = 8;
+ }
+
+ var bit = (this.bits & 1 << 8-this.bitsLength) !== 0;
+ this.bitsLength--;
+ return bit;
+ };
+
+ _InternalBitReader.prototype.align = function() { this.bitsLength = 0; };
+
+ _InternalBitReader.prototype.readBits = function(len, type) {
+ var data = 0;
+ type = type || 'LSB';
+ var tbl = JSIO.bitShiftTable[type];
+ var dl = 0;
+ while (len > 0) {
+ if (this.bitsLength === 0) {
+ var nextByte = this.byteReader.readByte();
+ if (nextByte === null) this._throwError('Unexpected end of stream', null, 'read'+type);
+ this.bits = nextByte;
+ this.bitsLength = 8;
+ }
+
+ var maskLen = (this.bitsLength >= len) ? len : this.bitsLength;
+ var bitsPos = 8-this.bitsLength;
+ data |= (this.bits & tbl[bitsPos][maskLen-1]) >>> bitsPos << dl;
+
+ dl += maskLen;
+ len -= maskLen;
+ this.bitsLength -= maskLen;
+ };
+ return data;
+ };
+
+ _InternalBitReader.prototype.readLSB = function(len) { return this.readBits(len, 'LSB'); }
+ _InternalBitReader.prototype.readMSB = function(len) { return this.readBits(len, 'MSB'); }
+
+ //
+ // =======================================================
+
+
+ /* inflating ByteReader - RFC 1951 */
+ var _inflatingReader = function(reader) {
+ if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename, 'ctor');
+ this._byteReader = reader;
+ this._bitReader = new _InternalBitReader(reader);
+ this._buffer = [];
+ this._bufferPosition = 0;
+ this._state = 0;
+ this._blockFinal = false;
+ this._typeName = typename;
+ this._version = version;
+ return this;
+ };
+
+
+ // shared fns and variables
+
+ var staticCodes = null;
+ var staticDistances = null;
+
+ var clenMap = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
+
+ var buildCodes = function(lengths){
+ var i=0;
+ var codes = new Array(lengths.length);
+ var maxBits = lengths[0];
+ for (i=1; i<lengths.length; i++) {
+ if (maxBits < lengths[i]) maxBits = lengths[i];
+ }
+
+ var bitLengthsCount = new Array(maxBits + 1);
+ for (i=0; i<=maxBits; i++) bitLengthsCount[i]=0;
+
+ for (i=0; i<lengths.length; i++) {
+ ++bitLengthsCount[lengths[i]];
+ }
+
+ var nextCode = new Array(maxBits + 1);
+ var code = 0;
+ bitLengthsCount[0] = 0;
+ for (var bits=1; bits<=maxBits; bits++) {
+ code = (code + bitLengthsCount[bits - 1]) << 1;
+ nextCode[bits] = code;
+ }
+
+ for (i=0; i<codes.length; i++) {
+ var len = lengths[i];
+ if (len !== 0) {
+ codes[i] = nextCode[len];
+ nextCode[len]++;
+ }
+ }
+ return codes;
+ };
+
+ var buildTree = function(codes, lengths){
+ var nonEmptyCodes = [];
+ for(var i=0; i<codes.length; ++i) {
+ if(lengths[i] > 0) {
+ var code = {};
+ code.bits = codes[i];
+ code.length = lengths[i];
+ code.index = i;
+ nonEmptyCodes.push(code);
+ }
+ }
+ return buildTreeBranch(nonEmptyCodes, 0, 0);
+ };
+
+
+ var buildTreeBranch = function(codes, prefix, prefixLength){
+ if (codes.length === 0) return null;
+
+ var zeros = [];
+ var ones = [];
+ var branch = {};
+ branch.isLeaf = false;
+ for(var i=0; i<codes.length; ++i) {
+ if (codes[i].length == prefixLength && codes[i].bits == prefix) {
+ branch.isLeaf = true;
+ branch.index = codes[i].index;
+ break;
+ }
+ else {
+ var nextBit = ((codes[i].bits >> (codes[i].length - prefixLength - 1)) & 1) > 0;
+ if (nextBit) ones.push(codes[i]);
+ else zeros.push(codes[i]);
+ }
+ }
+ if(!branch.isLeaf) {
+ branch.zero = buildTreeBranch(zeros, (prefix << 1), prefixLength + 1);
+ branch.one = buildTreeBranch(ones, (prefix << 1) | 1, prefixLength + 1);
+ }
+ return branch;
+ };
+
+
+ var encodedLengthStart = [3,4,5,6,7,8,9,10,
+ 11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,
+ 115,131,163,195,227,258];
+
+ var encodedLengthAdditionalBits = [0,0,0,0,0,0,0,0,
+ 1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0];
+
+ var encodedDistanceStart = [1,2,3,4, 5,7,9, 13,17,25, 33,49,65,
+ 97,129,193,257,385,513,769,1025,1537,2049,
+ 3073,4097,6145,8193,12289,16385,24577];
+
+ var encodedDistanceAdditionalBits = [0,0,0,0,1,1,2,2,3,3,4,4,
+ 5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13];
+
+
+ var readDynamicTrees = function(bitReader){
+ var hlit = bitReader.readLSB(5) + 257;
+ var hdist = bitReader.readLSB(5) + 1;
+ var hclen = bitReader.readLSB(4) + 4;
+ var clen = new Array(19);
+ var i;
+ for (i=0; i<clen.length; i++) { clen[i] = 0; }
+ for (i=0; i<hclen; i++) { clen[clenMap[i]] = bitReader.readLSB(3); }
+
+ var clenCodes = buildCodes(clen);
+ var clenTree = buildTree(clenCodes, clen);
+
+ var lengthsSequence = [];
+ while(lengthsSequence.length < hlit + hdist) {
+ var p = clenTree;
+ while(!p.isLeaf) { p = bitReader.readBit() ? p.one : p.zero; }
+
+ var code = p.index;
+ if (code <= 15) lengthsSequence.push(code);
+ else if (code == 16) {
+ // TODO: replace with faster repeat algorythm
+ var repeat = bitReader.readLSB(2) + 3;
+ for(var q=0; q<repeat; ++q){
+ lengthsSequence.push(lengthsSequence[lengthsSequence.length - 1]);
+ }
+ }
+ else if (code == 17) {
+ var repeat1 = bitReader.readLSB(3) + 3;
+ for(var q1=0; q1<repeat1; ++q1) {
+ lengthsSequence.push(0);
+ }
+ }
+ else if (code == 18) {
+ var repeat2 = bitReader.readLSB(7) + 11;
+ for(var q2=0; q2<repeat2; ++q2){
+ lengthsSequence.push(0);
+ }
+ }
+ }
+
+ var codesLengths = lengthsSequence.slice(0, hlit);
+ var codes = buildCodes(codesLengths);
+ var distancesLengths = lengthsSequence.slice(hlit, hlit + hdist);
+ var distances = buildCodes(distancesLengths);
+
+ return {
+ codesTree : buildTree(codes, codesLengths),
+ distancesTree : buildTree(distances, distancesLengths)
+ };
+ };
+
+
+ _inflatingReader.prototype = new JSIO._ByteReaderBase();
+
+
+ // internal instance fns
+ _inflatingReader.prototype._decodeItem = function() {
+ if (this._state == 2) return null; // end-of-blocks
+
+ var item;
+ if(this._state === 0) {
+ this._blockFinal = this._bitReader.readBit();
+ var blockType = this._bitReader.readLSB(2);
+ switch(blockType) {
+ case 0:
+ this._bitReader.align();
+ var len = this._bitReader.readLSB(16); // low-byte first, as opposed to readNumber's HBF
+ var nlen = this._bitReader.readLSB(16);
+ if ((len & ~nlen) != len) this._throwError('Invalid block type 0 length', null, '_decodeItem');
+
+ item = {};
+ item.itemType = 0;
+ item.array = this._bitReader.byteReader.read(len);
+ if (item.array.length < len) this._throwError('Incomplete block', null, '_decodeItem');
+ if (this._blockFinal) this._state = 2;
+ return item;
+ case 1:
+ this._codesTree = staticCodes;
+ this._distancesTree = staticDistances;
+ this._state = 1;
+ break;
+ case 2:
+ var dTrees = readDynamicTrees(this._bitReader);
+ this._codesTree = dTrees.codesTree;
+ this._distancesTree = dTrees.distancesTree;
+ this._state = 1;
+ break;
+ default:
+ this._throwError('Invalid block type ('+ blockType +')', null, '_decodeItem');
+ }
+ }
+
+ item = {};
+
+ var p = this._codesTree;
+ while (!p.isLeaf) { p = this._bitReader.readBit() ? p.one : p.zero; }
+ if(p.index < 256) {
+ item.itemType = 2;
+ item.symbol = p.index;
+ } else if(p.index > 256) {
+ var lengthCode = p.index;
+ if(lengthCode > 285) this._throwError('Invalid length code', null, '_decodeItem');
+
+ var length = encodedLengthStart[lengthCode - 257];
+ if(encodedLengthAdditionalBits[lengthCode - 257] > 0) {
+ length += this._bitReader.readLSB(encodedLengthAdditionalBits[lengthCode - 257]);
+ }
+
+ p = this._distancesTree;
+ while (!p.isLeaf) { p = this._bitReader.readBit() ? p.one : p.zero; }
+
+ var distanceCode = p.index;
+ var distance = encodedDistanceStart[distanceCode];
+ if (encodedDistanceAdditionalBits[distanceCode] > 0)
+ distance += this._bitReader.readLSB(encodedDistanceAdditionalBits[distanceCode]);
+
+ item.itemType = 3;
+ item.distance = distance;
+ item.length = length;
+ } else {
+ item.itemType = 1;
+ this._state = this._blockFinal ? 2 : 0; // EOB
+ }
+ return item;
+ };
+
+
+
+ // public instance functions
+
+ _inflatingReader.prototype.readByte = function() {
+ var byte = this.read(1)[0];
+ return (byte === null || byte === undefined) ? null : byte;
+ };
+
+ _inflatingReader.prototype.read = function(len) {
+ var b = this._buffer; // (since we use this so much...)
+
+ // Keep reading until we get to the right length
+ while (this._bufferPosition+len > b.length) {
+ var item = this._decodeItem();
+ if (item === null) { // EOF
+ len = b.length - this._bufferPosition;
+ break;
+ }
+ switch(item.itemType) {
+ case 0:
+ JSIO.massApply(b.push, b, item.array);
+ break;
+ case 2:
+ b.push(item.symbol);
+ break;
+ case 3:
+ var j = b.length - item.distance;
+ if (item.distance >= item.length)
+ JSIO.massApply(b.push, b, b.slice(j, j+item.length));
+ // sometimes DEFLATE tries some trickery with "look-ahead" compression
+ else {
+ // this is basically just a repetition of the same string, plus some possible cutoff
+ var count = parseInt(item.length / item.distance);
+ var repArr = b.slice(j);
+ // http://stackoverflow.com/questions/202605/repeat-string-javascript/5450113#5450113
+ while (count > 0) {
+ if (count & 1) JSIO.massApply( b.push, b, repArr);
+ if (count >>= 1) JSIO.massApply(repArr.push, repArr, repArr);
+ }
+ // add any remaining cutoff
+ var r;
+ if (r = item.length % item.distance)
+ JSIO.massApply(b.push, b, b.slice(j, j+r));
+ }
+ break;
+ }
+ }
+ var bytes = b.slice(this._bufferPosition, this._bufferPosition+len);
+ this._bufferPosition += len;
+ this.position += len;
+
+ if (this._bufferPosition > 0xC000) {
+ var shift = b.length - 0x8000;
+ if (shift > this._bufferPosition) shift = this._bufferPosition;
+ b.splice(0, shift);
+ this._bufferPosition -= shift;
+ }
+
+ return bytes;
+ };
+
+ // initialization routine - once per type
+ (function(){
+
+ var codes = new Array(288);
+ var codesLengths = new Array(288);
+ var i=0;
+ for ( i = 0; i <= 143; i++) {
+ codes[i] = 0x0030 + i;
+ codesLengths[i] = 8;
+ }
+ for ( i = 144; i <= 255; i++) {
+ codes[i] = 0x0190 + i - 144;
+ codesLengths[i] = 9;
+ }
+ for ( i = 256; i <= 279; i++) {
+ codes[i] = 0x0000 + i - 256;
+ codesLengths[i] = 7;
+ }
+ for ( i = 280; i <= 287; i++) {
+ codes[i] = 0x00C0 + i - 280;
+ codesLengths[i] = 8;
+ }
+ staticCodes = buildTree(codes, codesLengths);
+
+ var distances = new Array(32);
+ var distancesLengths = new Array(32);
+ for ( i = 0; i <= 31; i++) {
+ distances[i] = i;
+ distancesLengths[i] = 5;
+ }
+ staticDistances = buildTree(distances, distancesLengths);
+ })();
+
+
+ JSIO.InflatingReader = _inflatingReader;
+
+})();
+
+
+/// JSIO.InflatingReader.js ends
+
+// Zipfile.js
+// ------------------------------------------------------------------
+//
+// A class that reads Zip files.
+// Depends on the JSIO library functions.
+//
+// =======================================================
+//
+// Copyleft (c) 2010, Dino Chiesa via MS-PL
+// Copyleft (c) 2012, Brendan Byrd via GPL
+//
+// This work is licensed under the GPLv3.
+
+(function(){
+ var version = "2.0 2012Feb";
+ var typename = "Zipfile";
+
+ if (typeof JSIO.BinaryUrlStream !== "function") JSIO.throwError('This extension requires JSIO.BinaryUrlStream.js v2.0', typename);
+ if (typeof JSIO.TextDecoder !== "object") JSIO.throwError('This extension requires JSIO.TextDecoder.js v2.0', typename);
+ if (typeof JSIO.TextReader !== "function") JSIO.throwError('This extension requires JSIO.TextReader.js v2.0', typename);
+ if (typeof JSIO.Crc32 !== "function") JSIO.throwError('This extension requires JSIO.Crc32.js v2.0', typename);
+ if (typeof JSIO.InflatingReader !== "function") JSIO.throwError('This extension requires JSIO.InflatingReader.js v2.0', typename);
+
+ // =======================================================
+ function ZipEntry(zip) {
+ this.zipfile = zip;
+ this._typename = "ZipEntry";
+ this._version = version;
+ this._crcCalculator = null;
+ }
+
+ ZipEntry.prototype._throwError = JSIO.throwError;
+
+ // return byte array or string
+ ZipEntry.prototype.extract = function(callback, asString) {
+ this.contentType = JSIO.guessFileType(this.name);
+ asString = asString || ( this.contentType == JSIO.FileType.Text ||
+ this.contentType == JSIO.FileType.XML);
+ var thisEntry = this;
+
+ if (this.compressionMethod !== 0 && this.compressionMethod != 8)
+ this._throwError('Unsupported compression method: ' + this.compressionMethod, null, 'extract');
+
+ var reader = (asString) ? this.openTextReader(thisEntry.utf8 ? JSIO.TextDecoder.UTF8 : JSIO.TextDecoder.ANSI) : this.openBinaryReader();
+
+ // diagnostic purpose only; tag the reader with the entry name
+ reader.zipEntryName = thisEntry.name;
+
+ if (typeof callback != "function") {
+ // synchronous
+ var result = reader.readToEnd();
+ this.verifyCrc32();
+ return result;
+ }
+
+ // asynchronous
+ reader.beginReadToEnd(function(result){
+ try {
+ thisEntry.verifyCrc32();
+ callback(thisEntry, result);
+ }
+ catch (exc1) {
+ callback(thisEntry, exc1);
+ }
+ });
+ return null;
+ };
+
+
+ // open a ByteReader on the entry, which will read binary
+ // content from the compressed stream.
+ ZipEntry.prototype.openBinaryReader = function() {
+ var reader =
+ new JSIO.StreamSegmentReader(this.zipfile.binaryStream,
+ this.offset + this.lengthOfHeader,
+ this.compressedSize);
+ if (this.compressionMethod === 0) {
+ this._crcCalculator = new JSIO.Crc32Reader(reader);
+ }
+ else {
+ var inflator = new JSIO.InflatingReader(reader);
+ this._crcCalculator = new JSIO.Crc32Reader(inflator);
+ }
+ // Whether compressed or not, the source ByteReader in each case
+ // is wrapped in a second ByteReader object that calculates CRC
+ // as it reads. That way, after all reading is complete, the
+ // caller can check the calcuated CRC against the expected CRC.
+ return this._crcCalculator;
+ };
+
+ // open a TextReader on the entry, to read text from the
+ // compressed stream.
+ ZipEntry.prototype.openTextReader = function(decoderKind) {
+ var reader = this.openBinaryReader();
+ decoderKind = decoderKind || JSIO.TextDecoder.UTF8;
+ var d = new decoderKind(reader);
+ var textReader = new JSIO.TextReader(d);
+ d._parent = textReader; // store a reference, for diagnostic purposes only
+ return textReader;
+ };
+
+ // verify the CRC on the entry.
+ // call this after all bytes have been read.
+ ZipEntry.prototype.verifyCrc32 = function() {
+ var computedCrc = this._crcCalculator.crc32();
+ var rc = false; // CRC FAIL
+ if (this.crc32 != computedCrc) {
+ var msg = "WARNING: CRC check failed: " +
+ "entry(" + this.name + ") " +
+ "computed(" + JSIO.decimalToHexString(computedCrc,8) + ") " +
+ "expected(" + JSIO.decimalToHexString(this.crc32,8) + ") ";
+ this.zipfile.status.push(msg);
+ } else {
+ rc = true; // OK
+ if (this.zipfile.verbose>2) {
+ this.zipfile.status.push("INFO: CRC check ok: 0x" +
+ JSIO.decimalToHexString(this.crc32,8));
+ }
+ }
+ return rc;
+ };
+
+
+ // ctor
+ ZipFile = function(fileUrl, callback, verbosity) {
+ if (! (this instanceof arguments.callee) ) JSIO.throwError('You must use new to instantiate this class', typename, 'ctor');
+
+ this.verbose = verbosity || 0;
+ this.entries = [];
+ this.entryNames = [];
+ this.status = [];
+ this._version = version;
+ this._typename = "ZipFile";
+ this._throwError = JSIO.throwError;
+
+ var thisZipFile = this;
+
+ // Could use a back-tracking reader for the central directory, but
+ // there's no point, since all the zip data is held in memory anyway.
+
+ /* function ReadCentralDirectory(){
+ var posn = thisZipFile.binaryStream.length - 64;
+ var maxSeekback = Math.Max(s.Length - 0x4000, 10);
+ var success = false;
+ var nTries = 0;
+ do
+ {
+ thisZipFile.binaryStream.Seek(posn, SeekOrigin.Begin);
+ var bytesRead = thisZipFile.binaryStream.findSignature(thisZipFile.Signatures.EndOfCentralDirectory);
+ if (bytesRead != -1)
+ success = true;
+ else
+ {
+ nTries++;
+ // increasingly larger
+ posn -= (32 * (nTries + 1) * nTries);
+ if (posn < 0) posn = 0; // BOF
+ }
+ }
+ while (!success && posn > maxSeekback);
+ if (!success) {
+ thisZipFile.status.push("cannot find End of Central Directory");
+ return;
+ }
+ } */
+
+
+ function DateFromPackedFormat(packed) {
+ if (packed == 0xFFFF || packed === 0) return new Date(1995, 0, 1, 0,0,0,0);
+
+ var packedTime = packed & 0x0000ffff;
+ var packedDate = ((packed & 0xffff0000) >> 16);
+
+ var year = 1980 + ((packedDate & 0xFE00) >> 9);
+ var month = ((packedDate & 0x01E0) >> 5) -1;
+ var day = packedDate & 0x001F;
+
+ var hour = (packedTime & 0xF800) >> 11;
+ var minute = (packedTime & 0x07E0) >> 5;
+ var second = (packedTime & 0x001F) * 2;
+
+ // Validation and error checking.
+ // This is not foolproof but will catch most errors.
+
+ // I can't believe how many different ways applications
+ // can mess up a simple date format.
+
+ if (second >= 60) { minute++; second = 0; }
+ if (minute >= 60) { hour++; minute = 0; }
+ if (hour >= 24) { day++; hour = 0; }
+ var success = false;
+ var d;
+ try {
+ d = new Date(year, month, day, hour, minute, second, 0);
+ success= true;
+ }
+ catch (exc1) {
+ if (year == 1980 && (month === 0 || day === 0)) {
+ try {
+ d = new Date(1980, 0, 1, hour, minute, second, 0);
+ success= true;
+ }
+ catch (exc2) {
+ try {
+ d = new Date(1980, 0, 1, 0, 0, 0, 0);
+ success= true;
+ }
+ catch (exc3) { } // how could this fail??
+ }
+ }
+ else {
+ try {
+ if (year < 1980) year = 1980;
+ if (year > 2030) year = 2030;
+ if (month < 1) month = 1;
+ if (month > 12) month = 12;
+ if (day < 1) day = 1;
+ if (day > 31) day = 31;
+ if (minute < 0) minute = 0;
+ if (minute > 59) minute = 59;
+ if (second < 0) second = 0;
+ if (second > 59) second = 59;
+ d = new Date(year, month-1, day, hour, minute, second, 0);
+ success= true;
+ }
+ catch (exc4){}
+ }
+ }
+ if (!success) this._throwError('Bad date/time value in this ZIP file', null, 'DateFromPackedFormat');
+ return d;
+ }
+
+
+ function ReadZipEntries () {
+ // read only once
+ if (thisZipFile.entryNames.length === 0){
+ var e;
+ while ((e = ReadZipEntry()) !== null) {
+ thisZipFile.entries.push(e);
+ thisZipFile.entryNames.push(e.name);
+ }
+ }
+ }
+
+
+ function ReadZipEntry () {
+ var offset = thisZipFile.binaryStream.position;
+ var sig = thisZipFile.binaryStream.readNumber(4);
+ if (sig == ZipFile.Signatures.DirEntry) {
+ // after all entries, comes the central directory
+ if (thisZipFile.verbose > 0) {
+ thisZipFile.status.push("INFO: at offset 0x" +
+ JSIO.decimalToHexString(offset) +
+ ", found start of Zip Directory.");
+ }
+ // all done reading
+ return null;
+ }
+ if (sig != ZipFile.Signatures.Entry) {
+ thisZipFile.status.push("WARNING: at offset 0x" +
+ JSIO.decimalToHexString(offset) +
+ ", found unexpected signature: 0x" +
+ JSIO.decimalToHexString(sig));
+ return null;
+ }
+
+ var entry = new ZipEntry(thisZipFile);
+ entry.offset = offset;
+ entry.versionNeeded = thisZipFile.binaryStream.readNumber(2);
+ entry.bitField = thisZipFile.binaryStream.readNumber(2);
+ entry.compressionMethod = thisZipFile.binaryStream.readNumber(2);
+ var timeBlob = thisZipFile.binaryStream.readNumber(4);
+ entry.lastModified = DateFromPackedFormat(timeBlob);
+ entry.crc32 = thisZipFile.binaryStream.readNumber(4);
+ entry.compressedSize = thisZipFile.binaryStream.readNumber(4);
+ entry.uncompressedSize = thisZipFile.binaryStream.readNumber(4);
+
+ if ((entry.bitField & 0x01) == 0x01){
+ thisZipFile.status.push("This zipfile uses Encryption, which is not supported by ZipFile.js.");
+ return null;
+ }
+
+ entry.utf8 = ((entry.bitField & 0x0800) == 0x0800);
+
+ if ((entry.bitField & 0x0008) == 0x0008){
+ thisZipFile.status.push("This zipfile uses a bit 3 trailing data descriptor, which is not supported by ZipFile.js.");
+ return null;
+ }
+
+ if (entry.compressedSize == 0xFFFFFFFF ||
+ entry.uncompressedSize == 0xFFFFFFFF) {
+ thisZipFile.status.push("This zipfile uses ZIP64, which is not supported by ZipFile.js");
+ return null;
+ }
+
+ var filenameLength = thisZipFile.binaryStream.readNumber(2);
+ var extraFieldLength = thisZipFile.binaryStream.readNumber(2);
+
+ thisZipFile.status.push("INFO: filename length= " + filenameLength);
+
+ // we've read 30 bytes of metadata so far
+ var bytesRead = 30 + filenameLength + extraFieldLength;
+
+ if (entry.utf8) {
+ thisZipFile.status.push("INFO: before filename, position= 0x" +
+ JSIO.decimalToHexString( thisZipFile.binaryStream.position ));
+ var binReader =
+ new JSIO.StreamSegmentReader(thisZipFile.binaryStream,
+ thisZipFile.binaryStream.position,
+ filenameLength);
+ var utf8Decoder = new JSIO.TextDecoder.UTF8(binReader);
+ var textReader = new JSIO.TextReader(utf8Decoder);
+ entry.name = textReader.readToEnd();
+
+ // advance the filepointer:
+ thisZipFile.binaryStream.seek(filenameLength,
+ JSIO.SeekOrigin.Current,
+ thisZipFile);
+
+ thisZipFile.status.push("INFO: after filename, position= 0x" +
+ JSIO.decimalToHexString( thisZipFile.binaryStream.position ));
+ }
+ else {
+ entry.name = thisZipFile.binaryStream.readString(filenameLength);
+ }
+
+ // There are a bunch of things in the "extra" header, thisZipFile we
+ // could parse, like timestamps and other things. This class
+ // only identifies and separates them.
+
+ // More info here: http://www.pkware.com/documents/casestudies/APPNOTE.TXT
+
+ var extraPos = 0;
+ entry.extra = [];
+ while (extraPos < extraFieldLength) {
+ var extraBlock = {
+ type: thisZipFile.binaryStream.readNumber(2),
+ size: thisZipFile.binaryStream.readNumber(2)
+ };
+ extraBlock.typeDescription = ZipFile.ExtraFieldTypes[extraBlock.type];
+ extraBlock.data = thisZipFile.binaryStream.read(extraBlock.size);
+ entry.extra.push(extraBlock);
+ extraPos += 4 + extraBlock.size;
+ }
+
+ if (thisZipFile.verbose > 1) {
+ thisZipFile.status.push("INFO: at offset 0x" +
+ JSIO.decimalToHexString(entry.offset) +
+ ", found entry '" + entry.name + "' fnl(" +
+ filenameLength + ") efl(" +
+ extraFieldLength +")");
+ }
+
+ if (extraFieldLength > 0) {
+ if (thisZipFile.verbose > 0) {
+ thisZipFile.status.push("INFO: entry " + entry.name + " has " +
+ extraFieldLength + " bytes of " +
+ "extra metadata (ID'd but ignored)");
+ }
+ }
+
+ entry.lengthOfHeader = bytesRead;
+ entry.totalEntrySize = entry.lengthOfHeader + entry.compressedSize;
+
+ // seek past the data without reading it. We will read on Extract()
+ if (thisZipFile.verbose > 1) {
+ thisZipFile.status.push("INFO: seek 0x" +
+ JSIO.decimalToHexString(entry.compressedSize) +
+ " (" + entry.compressedSize + ") bytes");
+ }
+
+ thisZipFile.binaryStream.seek(entry.compressedSize,
+ JSIO.SeekOrigin.Current,
+ thisZipFile);
+
+ return entry;
+ }
+
+
+ var parseZipFile = function(bfr){
+ try {
+ if (bfr.req.status == 200) {
+ var sig = thisZipFile.binaryStream.readNumber(4);
+ if (sig != ZipFile.Signatures.Entry){
+ thisZipFile.status.push("WARNING: this file does not appear to be a zip file");
+ } else {
+ thisZipFile.binaryStream.seek(0, JSIO.SeekOrigin.Begin);
+ ReadZipEntries();
+ if (thisZipFile.verbose > 0) {
+ thisZipFile.status.push("INFO: read " + thisZipFile.entries.length + " entries");
+ }
+ }
+ }
+ else {
+ thisZipFile.status.push("ERROR: the URL could not be read (" +
+ bfr.req.status + " " + bfr.req.statusText + ")");
+ }
+ callback(thisZipFile);
+ }
+ catch (exc1)
+ {
+ thisZipFile.status.push("Exception: " + exc1.message);
+ callback(thisZipFile);
+ }
+ };
+
+ this.binaryStream = new JSIO.BinaryUrlStream(fileUrl, parseZipFile);
+
+ return this;
+ };
+
+
+ ZipFile.Signatures = {
+ Entry : 0x04034b50,
+ EndOfCentralDirectory : 0x06054b50,
+ DirEntry : 0x02014b50
+ };
+
+ ZipFile.Version = version;
+
+ ZipFile.EncryptionAlgorithm = {
+ None : 0,
+ PkzipWeak : 1,
+ WinZipAes : 2
+ };
+
+ ZipFile.ExtraFieldTypes = {};
+ ZipFile.ExtraFieldTypes[0x0001] = 'Zip64 Extended Info';
+ ZipFile.ExtraFieldTypes[0x0007] = 'AV Info';
+ ZipFile.ExtraFieldTypes[0x0008] = 'Extended Language Encoding Data (PFS)';
+ ZipFile.ExtraFieldTypes[0x0009] = 'OS/2';
+ ZipFile.ExtraFieldTypes[0x000a] = 'NTFS ';
+ ZipFile.ExtraFieldTypes[0x000c] = 'OpenVMS';
+ ZipFile.ExtraFieldTypes[0x000d] = 'UNIX';
+ ZipFile.ExtraFieldTypes[0x000e] = 'File Stream and Fork Descriptors';
+ ZipFile.ExtraFieldTypes[0x000f] = 'Patch Descriptor';
+ ZipFile.ExtraFieldTypes[0x0014] = 'PKCS#7 Store for X.509 Certificates';
+ ZipFile.ExtraFieldTypes[0x0015] = 'X.509 Certificate ID and Signature (Individual File)';
+ ZipFile.ExtraFieldTypes[0x0016] = 'X.509 Certificate ID (Central Directory)';
+ ZipFile.ExtraFieldTypes[0x0017] = 'Strong Encryption Header';
+ ZipFile.ExtraFieldTypes[0x0018] = 'Record Management Controls';
+ ZipFile.ExtraFieldTypes[0x0019] = 'PKCS#7 Encryption Recipient Certificate List';
+ ZipFile.ExtraFieldTypes[0x0065] = 'IBM S/390 (Z390), AS/400 (I400) attributes (uncompressed)';
+ ZipFile.ExtraFieldTypes[0x0066] = 'IBM S/390 (Z390), AS/400 (I400) attributes (compressed)';
+ ZipFile.ExtraFieldTypes[0x4690] = 'POSZIP 4690 (reserved) ';
+ ZipFile.ExtraFieldTypes[0x07c8] = 'Macintosh';
+ ZipFile.ExtraFieldTypes[0x2605] = 'ZipIt Macintosh';
+ ZipFile.ExtraFieldTypes[0x2705] = 'ZipIt Macintosh 1.3.5+';
+ ZipFile.ExtraFieldTypes[0x2805] = 'ZipIt Macintosh 1.3.5+';
+ ZipFile.ExtraFieldTypes[0x334d] = 'Info-ZIP Macintosh';
+ ZipFile.ExtraFieldTypes[0x4341] = 'Acorn/SparkFS ';
+ ZipFile.ExtraFieldTypes[0x4453] = 'Windows NT security descriptor (binary ACL)';
+ ZipFile.ExtraFieldTypes[0x4704] = 'VM/CMS';
+ ZipFile.ExtraFieldTypes[0x470f] = 'MVS';
+ ZipFile.ExtraFieldTypes[0x4b46] = 'FWKCS MD5';
+ ZipFile.ExtraFieldTypes[0x4c41] = 'OS/2 access control list (text ACL)';
+ ZipFile.ExtraFieldTypes[0x4d49] = 'Info-ZIP OpenVMS';
+ ZipFile.ExtraFieldTypes[0x4f4c] = 'Xceed original location extra field';
+ ZipFile.ExtraFieldTypes[0x5356] = 'AOS/VS (ACL)';
+ ZipFile.ExtraFieldTypes[0x5455] = 'extended timestamp';
+ ZipFile.ExtraFieldTypes[0x554e] = 'Xceed unicode extra field';
+ ZipFile.ExtraFieldTypes[0x5855] = 'Info-ZIP UNIX (original, also OS/2, NT, etc)';
+ ZipFile.ExtraFieldTypes[0x6375] = 'Info-ZIP Unicode Comment Extra Field';
+ ZipFile.ExtraFieldTypes[0x6542] = 'BeOS/BeBox';
+ ZipFile.ExtraFieldTypes[0x7075] = 'Info-ZIP Unicode Path Extra Field';
+ ZipFile.ExtraFieldTypes[0x756e] = 'ASi UNIX';
+ ZipFile.ExtraFieldTypes[0x7855] = 'Info-ZIP UNIX (new)';
+ ZipFile.ExtraFieldTypes[0xa220] = 'Microsoft Open Packaging Growth Hint';
+ ZipFile.ExtraFieldTypes[0xfd4a] = 'SMS/QDOS';
+
+})();
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/geoxml3.js b/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/geoxml3.js
new file mode 100644
index 00000000..6cc72d79
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/geoxml3/geoxml3.js
@@ -0,0 +1,1855 @@
+/**
+ * @fileOverview Renders KML on the Google Maps JavaScript API Version 3
+ * @name GeoXML3
+ * @author Sterling Udell, Larry Ross, Brendan Byrd
+ * @see http://code.google.com/p/geoxml3/
+ *
+ * geoxml3.js
+ *
+ * Renders KML on the Google Maps JavaScript API Version 3
+ * http://code.google.com/p/geoxml3/
+ *
+ * Copyright 2010 Sterling Udell, Larry Ross
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+if (!String.prototype.trim) {
+/**
+ * Remove leading and trailing whitespace.
+ *
+ * @augments String
+ * @return {String}
+ */
+ String.prototype.trim = function () {
+ return this.replace(/^\s+|\s+$/g, '');
+ };
+}
+
+/**
+ * @namespace The GeoXML3 namespace.
+ */
+geoXML3 = window.geoXML3 || {instances: []};
+
+/**
+ * Constructor for the root KML parser object.
+ *
+ * <p>All top-level objects and functions are declared under a namespace of geoXML3.
+ * The core object is geoXML3.parser; typically, you'll instantiate a one parser
+ * per map.</p>
+ *
+ * @class Main XML parser.
+ * @param {geoXML3.parserOptions} options
+ */
+geoXML3.parser = function (options) {
+ // Private variables
+ var parserOptions = new geoXML3.parserOptions(options);
+ var docs = []; // Individual KML documents
+ var docsByUrl = {}; // Same docs as an hash by cleanURL
+ var kmzMetaData = {}; // Extra files from KMZ data
+ var styles = {}; // Global list of styles
+ var lastPlacemark;
+ var parserName;
+ if (!parserOptions.infoWindow && parserOptions.singleInfoWindow)
+ parserOptions.infoWindow = new google.maps.InfoWindow();
+
+ var parseKmlString = function (kmlString, docSet) {
+ // Internal values for the set of documents as a whole
+ var internals = {
+ parser: this,
+ docSet: docSet || [],
+ remaining: 1,
+ parseOnly: !(parserOptions.afterParse || parserOptions.processStyles)
+ };
+ thisDoc = new Object();
+ thisDoc.internals = internals;
+ internals.docSet.push(thisDoc);
+ render(geoXML3.xmlParse(kmlString),thisDoc);
+ }
+
+ var parse = function (urls, docSet) {
+ // Process one or more KML documents
+ if (!parserName) {
+ parserName = 'geoXML3.instances[' + (geoXML3.instances.push(this) - 1) + ']';
+ }
+
+ if (typeof urls === 'string') {
+ // Single KML document
+ urls = [urls];
+ }
+
+ // Internal values for the set of documents as a whole
+ var internals = {
+ parser: this,
+ docSet: docSet || [],
+ remaining: urls.length,
+ parseOnly: !(parserOptions.afterParse || parserOptions.processStyles)
+ };
+ var thisDoc, j;
+ for (var i = 0; i < urls.length; i++) {
+ var baseUrl = cleanURL(defileURL(location.pathname), urls[i]);
+ if (docsByUrl[baseUrl]) {
+ // Reloading an existing document
+ thisDoc = docsByUrl[baseUrl];
+ thisDoc.reload = true;
+ }
+ else {
+ thisDoc = new Object();
+ thisDoc.baseUrl = baseUrl;
+ internals.docSet.push(thisDoc);
+ }
+ thisDoc.url = urls[i];
+ thisDoc.internals = internals;
+ fetchDoc(thisDoc.url, thisDoc);
+ }
+ };
+
+ function fetchDoc(url, doc, resFunc) {
+ resFunc = resFunc || function (responseXML) { render(responseXML, doc); };
+
+ if (typeof ZipFile === 'function' && typeof JSIO === 'object' && typeof JSIO.guessFileType === 'function') { // KMZ support requires these modules loaded
+ contentType = JSIO.guessFileType(doc.baseUrl);
+ if (contentType == JSIO.FileType.Binary || contentType == JSIO.FileType.Unknown) {
+ doc.isCompressed = true;
+ doc.baseDir = doc.baseUrl + '/';
+ geoXML3.fetchZIP(url, resFunc, doc.internals.parser);
+ return;
+ }
+ }
+ doc.isCompressed = false;
+ doc.baseDir = defileURL(doc.baseUrl);
+ geoXML3.fetchXML(url, resFunc);
+ }
+
+ var hideDocument = function (doc) {
+ if (!doc) doc = docs[0];
+ // Hide the map objects associated with a document
+ var i;
+ if (!!doc.markers) {
+ for (i = 0; i < doc.markers.length; i++) {
+ if(!!doc.markers[i].infoWindow) doc.markers[i].infoWindow.close();
+ doc.markers[i].setVisible(false);
+ }
+ }
+ if (!!doc.ggroundoverlays) {
+ for (i = 0; i < doc.ggroundoverlays.length; i++) {
+ doc.ggroundoverlays[i].setOpacity(0);
+ }
+ }
+ if (!!doc.gpolylines) {
+ for (i=0;i<doc.gpolylines.length;i++) {
+ if(!!doc.gpolylines[i].infoWindow) doc.gpolylines[i].infoWindow.close();
+ doc.gpolylines[i].setMap(null);
+ }
+ }
+ if (!!doc.gpolygons) {
+ for (i=0;i<doc.gpolygons.length;i++) {
+ if(!!doc.gpolygons[i].infoWindow) doc.gpolygons[i].infoWindow.close();
+ doc.gpolygons[i].setMap(null);
+ }
+ }
+ };
+
+ var showDocument = function (doc) {
+ if (!doc) doc = docs[0];
+ // Show the map objects associated with a document
+ var i;
+ if (!!doc.markers) {
+ for (i = 0; i < doc.markers.length; i++) {
+ doc.markers[i].setVisible(true);
+ }
+ }
+ if (!!doc.ggroundoverlays) {
+ for (i = 0; i < doc.ggroundoverlays.length; i++) {
+ doc.ggroundoverlays[i].setOpacity(doc.ggroundoverlays[i].percentOpacity_);
+ }
+ }
+ if (!!doc.gpolylines) {
+ for (i=0;i<doc.gpolylines.length;i++) {
+ doc.gpolylines[i].setMap(parserOptions.map);
+ }
+ }
+ if (!!doc.gpolygons) {
+ for (i=0;i<doc.gpolygons.length;i++) {
+ doc.gpolygons[i].setMap(parserOptions.map);
+ }
+ }
+ };
+
+ var defaultStyle = {
+ balloon: {
+ bgColor: 'ffffffff',
+ textColor: 'ff000000',
+ text: "<h3>$[name]</h3>\n<div>$[description]</div>\n<div>$[geDirections]</div>",
+ displayMode: 'default'
+ },
+ icon: {
+ scale: 1.0,
+ dim: {
+ x: 0,
+ y: 0,
+ w: -1,
+ h: -1
+ },
+ hotSpot: {
+ x: 0.5,
+ y: 0.5,
+ xunits: 'fraction',
+ yunits: 'fraction'
+ }
+ },
+ line: {
+ color: 'ffffffff', // white (KML default)
+ colorMode: 'normal',
+ width: 1.0
+ },
+ poly: {
+ color: 'ffffffff', // white (KML default)
+ colorMode: 'normal',
+ fill: true,
+ outline: true
+ }
+ };
+
+ var kmlNS = 'http://www.opengis.net/kml/2.2';
+ var gxNS = 'http://www.google.com/kml/ext/2.2';
+ var nodeValue = geoXML3.nodeValue;
+ var getBooleanValue = geoXML3.getBooleanValue;
+ var getElementsByTagNameNS = geoXML3.getElementsByTagNameNS;
+ var getElementsByTagName = geoXML3.getElementsByTagName;
+
+function processStyleUrl(node) {
+ var styleUrlStr = nodeValue(getElementsByTagName(node, 'styleUrl')[0]);
+ if (!!styleUrlStr && styleUrlStr.indexOf('#') != -1)
+ var styleUrl = styleUrlStr.split('#');
+ else var styleUrl = ["",""];
+ return styleUrl;
+}
+
+ function processStyle(thisNode, baseUrl, styleID, baseDir) {
+ var style = (baseUrl === '{inline}') ? clone(defaultStyle) : (styles[baseUrl][styleID] = styles[baseUrl][styleID] || clone(defaultStyle));
+
+ var styleNodes = getElementsByTagName(thisNode, 'BalloonStyle');
+ if (!!styleNodes && styleNodes.length > 0) {
+ style.balloon.bgColor = nodeValue(getElementsByTagName(styleNodes[0], 'bgColor')[0], style.balloon.bgColor);
+ style.balloon.textColor = nodeValue(getElementsByTagName(styleNodes[0], 'textColor')[0], style.balloon.textColor);
+ style.balloon.text = nodeValue(getElementsByTagName(styleNodes[0], 'text')[0], style.balloon.text);
+ style.balloon.displayMode = nodeValue(getElementsByTagName(styleNodes[0], 'displayMode')[0], style.balloon.displayMode);
+ }
+
+ // style.list = (unsupported; doesn't make sense in Google Maps)
+
+ var styleNodes = getElementsByTagName(thisNode, 'IconStyle');
+ if (!!styleNodes && styleNodes.length > 0) {
+ var icon = style.icon;
+
+ icon.scale = parseFloat(nodeValue(getElementsByTagName(styleNodes[0], 'scale')[0], icon.scale));
+ // style.icon.heading = (unsupported; not supported in API)
+ // style.icon.color = (unsupported; not supported in API)
+ // style.icon.colorMode = (unsupported; not supported in API)
+
+ styleNodes = getElementsByTagName(thisNode, 'Icon');
+ if (!!styleNodes && styleNodes.length > 0) {
+ icon.href = nodeValue(getElementsByTagName(styleNodes[0], 'href')[0]);
+ icon.url = cleanURL(baseDir, icon.href);
+ // Detect images buried in KMZ files (and use a base64 encoded URL)
+ if (kmzMetaData[icon.url]) icon.url = kmzMetaData[icon.url].dataUrl;
+
+ // Support for icon palettes and exact size dimensions
+ icon.dim = {
+ x: parseInt(nodeValue(getElementsByTagNameNS(styleNodes[0], gxNS, 'x')[0], icon.dim.x)),
+ y: parseInt(nodeValue(getElementsByTagNameNS(styleNodes[0], gxNS, 'y')[0], icon.dim.y)),
+ w: parseInt(nodeValue(getElementsByTagNameNS(styleNodes[0], gxNS, 'w')[0], icon.dim.w)),
+ h: parseInt(nodeValue(getElementsByTagNameNS(styleNodes[0], gxNS, 'h')[0], icon.dim.h))
+ };
+
+ styleNodes = getElementsByTagName(styleNodes[0], 'hotSpot')[0];
+ if (!!styleNodes && styleNodes.length > 0) {
+ icon.hotSpot = {
+ x: styleNodes[0].getAttribute('x'),
+ y: styleNodes[0].getAttribute('y'),
+ xunits: styleNodes[0].getAttribute('xunits'),
+ yunits: styleNodes[0].getAttribute('yunits')
+ };
+ }
+
+ // certain occasions where we need the pixel size of the image (like the default settings...)
+ // (NOTE: Scale is applied to entire image, not just the section of the icon palette. So,
+ // if we need scaling, we'll need the img dimensions no matter what.)
+ if ( (icon.dim.w < 0 || icon.dim.h < 0) && (icon.xunits != 'pixels' || icon.yunits == 'fraction') || icon.scale != 1.0) {
+ // (hopefully, this will load by the time we need it...)
+ icon.img = new Image();
+ icon.img.onload = function() {
+ if (icon.dim.w < 0 || icon.dim.h < 0) {
+ icon.dim.w = this.width;
+ icon.dim.h = this.height;
+ }
+ };
+ icon.img.src = icon.url;
+
+ // sometimes the file is already cached and it never calls onLoad
+ if (icon.img.width > 0) {
+ icon.dim.w = icon.img.width;
+ icon.dim.h = icon.img.height;
+ }
+ }
+ }
+ }
+
+ // style.label = (unsupported; may be possible but not with API)
+
+ styleNodes = getElementsByTagName(thisNode, 'LineStyle');
+ if (!!styleNodes && styleNodes.length > 0) {
+ style.line.color = nodeValue(getElementsByTagName(styleNodes[0], 'color')[0], style.line.color);
+ style.line.colorMode = nodeValue(getElementsByTagName(styleNodes[0], 'colorMode')[0], style.line.colorMode);
+ style.line.width = nodeValue(getElementsByTagName(styleNodes[0], 'width')[0], style.line.width);
+ // style.line.outerColor = (unsupported; not supported in API)
+ // style.line.outerWidth = (unsupported; not supported in API)
+ // style.line.physicalWidth = (unsupported; unnecessary in Google Maps)
+ // style.line.labelVisibility = (unsupported; possible to implement)
+ }
+
+ styleNodes = getElementsByTagName(thisNode, 'PolyStyle');
+ if (!!styleNodes && styleNodes.length > 0) {
+ style.poly.color = nodeValue( getElementsByTagName(styleNodes[0], 'color')[0], style.poly.color);
+ style.poly.colorMode = nodeValue( getElementsByTagName(styleNodes[0], 'colorMode')[0], style.poly.colorMode);
+ style.poly.outline = getBooleanValue(getElementsByTagName(styleNodes[0], 'outline')[0], style.poly.outline);
+ style.poly.fill = getBooleanValue(getElementsByTagName(styleNodes[0], 'fill')[0], style.poly.fill);
+ }
+ return style;
+ }
+
+ // from http://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-a-javascript-object
+ // http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
+ function clone(obj){
+ if(obj == null || typeof(obj) != 'object') return obj;
+ if (obj.cloneNode) return obj.cloneNode(true);
+ var temp = new obj.constructor();
+ for(var key in obj) temp[key] = clone(obj[key]);
+ return temp;
+ }
+
+ function processStyleMap(thisNode, baseUrl, styleID, baseDir) {
+ var pairs = getElementsByTagName(thisNode, 'Pair');
+ var map = new Object();
+
+ // add each key to the map
+ for (var pr=0;pr<pairs.length;pr++) {
+ var pairKey = nodeValue(getElementsByTagName(pairs[pr], 'key')[0]);
+ var pairStyle = nodeValue(getElementsByTagName(pairs[pr], 'Style')[0]);
+ var pairStyleUrl = processStyleUrl(pairs[pr]);
+ var pairStyleBaseUrl = pairStyleUrl[0] ? cleanURL(baseDir, pairStyleUrl[0]) : baseUrl;
+ var pairStyleID = pairStyleUrl[1];
+
+ if (!!pairStyle) {
+ map[pairKey] = processStyle(pairStyle, pairStyleBaseUrl, pairStyleID);
+ } else if (!!pairStyleID && !!styles[pairStyleBaseUrl][pairStyleID]) {
+ map[pairKey] = clone(styles[pairStyleBaseUrl][pairStyleID]);
+ }
+ }
+ if (!!map["normal"]) {
+ styles[baseUrl][styleID] = clone(map["normal"]);
+ } else {
+ styles[baseUrl][styleID] = clone(defaultStyle);
+ }
+ if (!!map["highlight"]) {
+ processStyleID(map["highlight"]);
+ }
+ styles[baseUrl][styleID].map = clone(map);
+ }
+
+ function processPlacemarkCoords(node, tag) {
+ var parent = getElementsByTagName(node, tag);
+ var coordListA = [];
+ for (var i=0; i<parent.length; i++) {
+ var coordNodes = getElementsByTagName(parent[i], 'coordinates');
+ if (!coordNodes) {
+ if (coordListA.length > 0) {
+ break;
+ } else {
+ return [{coordinates: []}];
+ }
+ }
+
+ for (var j=0; j<coordNodes.length;j++) {
+ var coords = nodeValue(coordNodes[j]).trim();
+ coords = coords.replace(/,\s+/g, ',');
+ var path = coords.split(/\s+/g);
+ var pathLength = path.length;
+ var coordList = [];
+ for (var k = 0; k < pathLength; k++) {
+ coords = path[k].split(',');
+ if (!isNaN(coords[0]) && !isNaN(coords[1])) {
+ coordList.push({
+ lat: parseFloat(coords[1]),
+ lng: parseFloat(coords[0]),
+ alt: parseFloat(coords[2])
+ });
+ }
+ }
+ coordListA.push({coordinates: coordList});
+ }
+ }
+ return coordListA;
+ }
+
+ var render = function (responseXML, doc) {
+ // Callback for retrieving a KML document: parse the KML and display it on the map
+ if (!responseXML) {
+ // Error retrieving the data
+ geoXML3.log('Unable to retrieve ' + doc.url);
+ if (parserOptions.failedParse) parserOptions.failedParse(doc);
+ doc.failed = true;
+ return;
+ } else if (responseXML.parseError && responseXML.parseError.errorCode != 0) {
+ // IE parse error
+ var err = responseXML.parseError;
+ var msg = 'Parse error in line ' + err.line + ', col ' + err.linePos + ' (error code: ' + err.errorCode + ")\n" +
+ "\nError Reason: " + err.reason +
+ 'Error Line: ' + err.srcText;
+
+ geoXML3.log('Unable to retrieve ' + doc.url + ': ' + msg);
+ if (parserOptions.failedParse) parserOptions.failedParse(doc);
+ doc.failed = true;
+ return;
+ } else if (responseXML.documentElement && responseXML.documentElement.nodeName == 'parsererror') {
+ // Firefox parse error
+ geoXML3.log('Unable to retrieve ' + doc.url + ': ' + responseXML.documentElement.childNodes[0].nodeValue);
+ if (parserOptions.failedParse) parserOptions.failedParse(doc);
+ doc.failed = true;
+ return;
+ } else if (!doc) {
+ throw 'geoXML3 internal error: render called with null document';
+ } else { //no errors
+ var i;
+ doc.placemarks = [];
+ doc.groundoverlays = [];
+ doc.ggroundoverlays = [];
+ doc.networkLinks = [];
+ doc.gpolygons = [];
+ doc.gpolylines = [];
+
+ // Check for dependent KML files
+ var nodes = getElementsByTagName(responseXML, 'styleUrl');
+ var docSet = doc.internals.docSet;
+
+ for (var i = 0; i < nodes.length; i++) {
+ var url = nodeValue(nodes[i]).split('#')[0];
+ if (!url) continue; // #id (inside doc)
+ var rUrl = cleanURL( doc.baseDir, url );
+ if (rUrl === doc.baseUrl) continue; // self
+ if (docsByUrl[rUrl]) continue; // already loaded
+
+ var thisDoc;
+ var j = docSet.indexOfObjWithItem('baseUrl', rUrl);
+ if (j != -1) {
+ // Already listed to be loaded, but probably in the wrong order.
+ // Load it right away to immediately resolve dependency.
+ thisDoc = docSet[j];
+ if (thisDoc.failed) continue; // failed to load last time; don't retry it again
+ }
+ else {
+ // Not listed at all; add it in
+ thisDoc = new Object();
+ thisDoc.url = rUrl; // url can't be trusted inside KMZ files, since it may .. outside of the archive
+ thisDoc.baseUrl = rUrl;
+ thisDoc.internals = doc.internals;
+
+ doc.internals.docSet.push(thisDoc);
+ doc.internals.remaining++;
+ }
+
+ // render dependent KML first then re-run renderer
+ fetchDoc(rUrl, thisDoc, function (thisResXML) {
+ render(thisResXML, thisDoc);
+ render(responseXML, doc);
+ });
+
+ // to prevent cross-dependency issues, just load the one
+ // file first and re-check the rest later
+ return;
+ }
+
+ // Parse styles
+ doc.styles = styles[doc.baseUrl] = styles[doc.baseUrl] || {};
+ var styleID, styleNodes;
+ nodes = getElementsByTagName(responseXML, 'Style');
+ nodeCount = nodes.length;
+ for (i = 0; i < nodeCount; i++) {
+ thisNode = nodes[i];
+ var styleID = thisNode.getAttribute('id');
+ if (!!styleID) processStyle(thisNode, doc.baseUrl, styleID, doc.baseDir);
+ }
+ // Parse StyleMap nodes
+ nodes = getElementsByTagName(responseXML, 'StyleMap');
+ for (i = 0; i < nodes.length; i++) {
+ thisNode = nodes[i];
+ var styleID = thisNode.getAttribute('id');
+ if (!!styleID) processStyleMap(thisNode, doc.baseUrl, styleID, doc.baseDir);
+ }
+
+ if (!!parserOptions.processStyles || !parserOptions.createMarker) {
+ // Convert parsed styles into GMaps equivalents
+ processStyles(doc);
+ }
+
+ // Parse placemarks
+ if (!!doc.reload && !!doc.markers) {
+ for (i = 0; i < doc.markers.length; i++) {
+ doc.markers[i].active = false;
+ }
+ }
+ var placemark, node, coords, path, marker, poly;
+ var placemark, coords, path, pathLength, marker, polygonNodes, coordList;
+ var placemarkNodes = getElementsByTagName(responseXML, 'Placemark');
+ for (pm = 0; pm < placemarkNodes.length; pm++) {
+ // Init the placemark object
+ node = placemarkNodes[pm];
+ var styleUrl = processStyleUrl(node);
+ placemark = {
+ name: nodeValue(getElementsByTagName(node, 'name')[0]),
+ description: nodeValue(getElementsByTagName(node, 'description')[0]),
+ styleUrl: styleUrl.join('#'),
+ styleBaseUrl: styleUrl[0] ? cleanURL(doc.baseDir, styleUrl[0]) : doc.baseUrl,
+ styleID: styleUrl[1],
+ visibility: getBooleanValue(getElementsByTagName(node, 'visibility')[0], true),
+ balloonVisibility: getBooleanValue(getElementsByTagNameNS(node, gxNS, 'balloonVisibility')[0], !parserOptions.suppressInfoWindows)
+ };
+ placemark.style = (styles[placemark.styleBaseUrl] && styles[placemark.styleBaseUrl][placemark.styleID]) || clone(defaultStyle);
+ // inline style overrides shared style
+ var inlineStyles = getElementsByTagName(node, 'Style');
+ if (inlineStyles && (inlineStyles.length > 0)) {
+ var style = processStyle(node, '{inline}', '{inline}');
+ processStyleID(style);
+ if (style) placemark.style = style;
+ }
+
+ if (/^https?:\/\//.test(placemark.description)) {
+ placemark.description = ['<a href="', placemark.description, '">', placemark.description, '</a>'].join('');
+ }
+
+ // record list of variables for substitution
+ placemark.vars = {
+ display: {
+ name: 'Name',
+ description: 'Description',
+ address: 'Street Address',
+ id: 'ID',
+ Snippet: 'Snippet',
+ geDirections: 'Directions'
+ },
+ val: {
+ name: placemark.name || '',
+ description: placemark.description || '',
+ address: nodeValue(getElementsByTagName(node, 'address')[0], ''),
+ id: node.getAttribute('id') || '',
+ Snippet: nodeValue(getElementsByTagName(node, 'Snippet')[0], '')
+ },
+ directions: [
+ 'f=d',
+ 'source=GeoXML3'
+ ]
+ };
+
+ // add extended data to variables
+ var extDataNodes = getElementsByTagName(node, 'ExtendedData');
+ if (!!extDataNodes && extDataNodes.length > 0) {
+ var dataNodes = getElementsByTagName(extDataNodes[0], 'Data');
+ for (var d = 0; d < dataNodes.length; d++) {
+ var dn = dataNodes[d];
+ var name = dn.getAttribute('name');
+ if (!name) continue;
+ var dName = nodeValue(getElementsByTagName(dn, 'displayName')[0], name);
+ var val = nodeValue(getElementsByTagName(dn, 'value')[0]);
+
+ placemark.vars.val[name] = val;
+ placemark.vars.display[name] = dName;
+ }
+ }
+
+ // process MultiGeometry
+ var GeometryNodes = getElementsByTagName(node, 'coordinates');
+ var Geometry = null;
+ if (!!GeometryNodes && (GeometryNodes.length > 0)) {
+ for (var gn=0;gn<GeometryNodes.length;gn++) {
+ if (GeometryNodes[gn].parentNode &&
+ GeometryNodes[gn].parentNode.nodeName) {
+ var GeometryPN = GeometryNodes[gn].parentNode;
+ Geometry = GeometryPN.nodeName;
+
+ // Extract the coordinates
+ // What sort of placemark?
+ switch(Geometry) {
+ case "Point":
+ placemark.Point = processPlacemarkCoords(node, "Point")[0];
+ placemark.latlng = new google.maps.LatLng(placemark.Point.coordinates[0].lat, placemark.Point.coordinates[0].lng);
+ pathLength = 1;
+ break;
+ case "LinearRing":
+ // Polygon/line
+ polygonNodes = getElementsByTagName(node, 'Polygon');
+ // Polygon
+ if (!placemark.Polygon)
+ placemark.Polygon = [{
+ outerBoundaryIs: {coordinates: []},
+ innerBoundaryIs: [{coordinates: []}]
+ }];
+ for (var pg=0;pg<polygonNodes.length;pg++) {
+ placemark.Polygon[pg] = {
+ outerBoundaryIs: {coordinates: []},
+ innerBoundaryIs: [{coordinates: []}]
+ }
+ placemark.Polygon[pg].outerBoundaryIs = processPlacemarkCoords(polygonNodes[pg], "outerBoundaryIs");
+ placemark.Polygon[pg].innerBoundaryIs = processPlacemarkCoords(polygonNodes[pg], "innerBoundaryIs");
+ }
+ coordList = placemark.Polygon[0].outerBoundaryIs;
+ break;
+
+ case "LineString":
+ pathLength = 0;
+ placemark.LineString = processPlacemarkCoords(node,"LineString");
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ // call the custom placemark parse function if it is defined
+ if (!!parserOptions.pmParseFn) parserOptions.pmParseFn(node, placemark);
+ doc.placemarks.push(placemark);
+
+ // single marker
+ if (placemark.Point) {
+ if (!!google.maps) {
+ doc.bounds = doc.bounds || new google.maps.LatLngBounds();
+ doc.bounds.extend(placemark.latlng);
+ }
+
+ // Potential user-defined marker handler
+ var pointCreateFunc = parserOptions.createMarker || createMarker;
+ var found = false;
+ if (!parserOptions.createMarker) {
+ // Check to see if this marker was created on a previous load of this document
+ if (!!doc) {
+ doc.markers = doc.markers || [];
+ if (doc.reload) {
+ for (var j = 0; j < doc.markers.length; j++) {
+ if (doc.markers[j].getPosition().equals(placemark.latlng)) {
+ found = doc.markers[j].active = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!found) {
+ // Call the marker creator
+ var marker = pointCreateFunc(placemark, doc);
+ if (marker) marker.active = placemark.visibility;
+ }
+ }
+ // polygon/line
+ var poly, line;
+ if (!!doc) {
+ if (placemark.Polygon) doc.gpolygons = doc.gpolygons || [];
+ if (placemark.LineString) doc.gpolylines = doc.gpolylines || [];
+ }
+
+ var polyCreateFunc = parserOptions.createPolygon || createPolygon;
+ var lineCreateFunc = parserOptions.createLineString || createPolyline;
+ if (placemark.Polygon) {
+ poly = polyCreateFunc(placemark,doc);
+ if (poly) poly.active = placemark.visibility;
+ }
+ if (placemark.LineString) {
+ line = lineCreateFunc(placemark,doc);
+ if (line) line.active = placemark.visibility;
+ }
+ if (!!google.maps) {
+ doc.bounds = doc.bounds || new google.maps.LatLngBounds();
+ if (poly) doc.bounds.union(poly.bounds);
+ if (line) doc.bounds.union(line.bounds);
+ }
+
+ } // placemark loop
+
+ if (!!doc.reload && !!doc.markers) {
+ for (i = doc.markers.length - 1; i >= 0 ; i--) {
+ if (!doc.markers[i].active) {
+ if (!!doc.markers[i].infoWindow) {
+ doc.markers[i].infoWindow.close();
+ }
+ doc.markers[i].setMap(null);
+ doc.markers.splice(i, 1);
+ }
+ }
+ }
+
+ // Parse ground overlays
+ if (!!doc.reload && !!doc.groundoverlays) {
+ for (i = 0; i < doc.groundoverlays.length; i++) {
+ doc.groundoverlays[i].active = false;
+ }
+ }
+
+ if (!!doc) {
+ doc.groundoverlays = doc.groundoverlays || [];
+ }
+ // doc.groundoverlays =[];
+ var groundOverlay, color, transparency, overlay;
+ var groundNodes = getElementsByTagName(responseXML, 'GroundOverlay');
+ for (i = 0; i < groundNodes.length; i++) {
+ node = groundNodes[i];
+
+ // Detect images buried in KMZ files (and use a base64 encoded URL)
+ var gnUrl = cleanURL( doc.baseDir, nodeValue(getElementsByTagName(node, 'href')[0]) );
+ if (kmzMetaData[gnUrl]) gnUrl = kmzMetaData[gnUrl].dataUrl;
+
+ // Init the ground overlay object
+ groundOverlay = {
+ name: nodeValue(getElementsByTagName(node, 'name')[0]),
+ description: nodeValue(getElementsByTagName(node, 'description')[0]),
+ icon: { href: gnUrl },
+ latLonBox: {
+ north: parseFloat(nodeValue(getElementsByTagName(node, 'north')[0])),
+ east: parseFloat(nodeValue(getElementsByTagName(node, 'east')[0])),
+ south: parseFloat(nodeValue(getElementsByTagName(node, 'south')[0])),
+ west: parseFloat(nodeValue(getElementsByTagName(node, 'west')[0]))
+ }
+ };
+ if (!!google.maps) {
+ doc.bounds = doc.bounds || new google.maps.LatLngBounds();
+ doc.bounds.union(new google.maps.LatLngBounds(
+ new google.maps.LatLng(groundOverlay.latLonBox.south, groundOverlay.latLonBox.west),
+ new google.maps.LatLng(groundOverlay.latLonBox.north, groundOverlay.latLonBox.east)
+ ));
+ }
+
+ // Opacity is encoded in the color node
+ var colorNode = getElementsByTagName(node, 'color');
+ if (colorNode && colorNode.length > 0) {
+ groundOverlay.opacity = geoXML3.getOpacity(nodeValue(colorNode[0]));
+ } else {
+ groundOverlay.opacity = 1.0; // KML default
+ }
+
+ doc.groundoverlays.push(groundOverlay);
+ if (!!parserOptions.createOverlay) {
+ // User-defined overlay handler
+ parserOptions.createOverlay(groundOverlay, doc);
+ } else {
+ // Check to see if this overlay was created on a previous load of this document
+ var found = false;
+ if (!!doc) {
+ doc.groundoverlays = doc.groundoverlays || [];
+ if (doc.reload) {
+ overlayBounds = new google.maps.LatLngBounds(
+ new google.maps.LatLng(groundOverlay.latLonBox.south, groundOverlay.latLonBox.west),
+ new google.maps.LatLng(groundOverlay.latLonBox.north, groundOverlay.latLonBox.east)
+ );
+ var overlays = doc.groundoverlays;
+ for (i = overlays.length; i--;) {
+ if ((overlays[i].bounds().equals(overlayBounds)) &&
+ (overlays.url_ === groundOverlay.icon.href)) {
+ found = overlays[i].active = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!found) {
+ // Call the built-in overlay creator
+ overlay = createOverlay(groundOverlay, doc);
+ overlay.active = true;
+ }
+ }
+ if (!!doc.reload && !!doc.groundoverlays && !!doc.groundoverlays.length) {
+ var overlays = doc.groundoverlays;
+ for (i = overlays.length; i--;) {
+ if (!overlays[i].active) {
+ overlays[i].remove();
+ overlays.splice(i, 1);
+ }
+ }
+ doc.groundoverlays = overlays;
+ }
+ }
+
+ // Parse network links
+ var networkLink;
+ var docPath = document.location.pathname.split('/');
+ docPath = docPath.splice(0, docPath.length - 1).join('/');
+ var linkNodes = getElementsByTagName(responseXML, 'NetworkLink');
+ for (i = 0; i < linkNodes.length; i++) {
+ node = linkNodes[i];
+
+ // Init the network link object
+ networkLink = {
+ name: nodeValue(getElementsByTagName(node, 'name')[0]),
+ link: {
+ href: nodeValue(getElementsByTagName(node, 'href')[0]),
+ refreshMode: nodeValue(getElementsByTagName(node, 'refreshMode')[0])
+ }
+ };
+
+ // Establish the specific refresh mode
+ if (!networkLink.link.refreshMode) {
+ networkLink.link.refreshMode = 'onChange';
+ }
+ if (networkLink.link.refreshMode === 'onInterval') {
+ networkLink.link.refreshInterval = parseFloat(nodeValue(getElementsByTagName(node, 'refreshInterval')[0]));
+ if (isNaN(networkLink.link.refreshInterval)) {
+ networkLink.link.refreshInterval = 0;
+ }
+ } else if (networkLink.link.refreshMode === 'onChange') {
+ networkLink.link.viewRefreshMode = nodeValue(getElementsByTagName(node, 'viewRefreshMode')[0]);
+ if (!networkLink.link.viewRefreshMode) {
+ networkLink.link.viewRefreshMode = 'never';
+ }
+ if (networkLink.link.viewRefreshMode === 'onStop') {
+ networkLink.link.viewRefreshTime = nodeValue(getElementsByTagName(node, 'refreshMode')[0]);
+ networkLink.link.viewFormat = nodeValue(getElementsByTagName(node, 'refreshMode')[0]);
+ if (!networkLink.link.viewFormat) {
+ networkLink.link.viewFormat = 'BBOX=[bboxWest],[bboxSouth],[bboxEast],[bboxNorth]';
+ }
+ }
+ }
+
+ if (!/^[\/|http]/.test(networkLink.link.href)) {
+ // Fully-qualify the HREF
+ networkLink.link.href = docPath + '/' + networkLink.link.href;
+ }
+
+ // Apply the link
+ if ((networkLink.link.refreshMode === 'onInterval') &&
+ (networkLink.link.refreshInterval > 0)) {
+ // Reload at regular intervals
+ setInterval(parserName + '.parse("' + networkLink.link.href + '")',
+ 1000 * networkLink.link.refreshInterval);
+ } else if (networkLink.link.refreshMode === 'onChange') {
+ if (networkLink.link.viewRefreshMode === 'never') {
+ // Load the link just once
+ doc.internals.parser.parse(networkLink.link.href, doc.internals.docSet);
+ } else if (networkLink.link.viewRefreshMode === 'onStop') {
+ // Reload when the map view changes
+
+ }
+ }
+ }
+ }
+
+ if (!!doc.bounds) {
+ doc.internals.bounds = doc.internals.bounds || new google.maps.LatLngBounds();
+ doc.internals.bounds.union(doc.bounds);
+ }
+ if (!!doc.markers || !!doc.groundoverlays || !!doc.gpolylines || !!doc.gpolygons) {
+ doc.internals.parseOnly = false;
+ }
+
+ if (!doc.internals.parseOnly) {
+ // geoXML3 is not being used only as a real-time parser, so keep the processed documents around
+ if (!docsByUrl[doc.baseUrl]) {
+ docs.push(doc);
+ docsByUrl[doc.baseUrl] = doc;
+ }
+ else {
+ // internal replacement, which keeps the same memory ref loc in docs and docsByUrl
+ for (var i in docsByUrl[doc.baseUrl]) {
+ docsByUrl[doc.baseUrl][i] = doc[i];
+ }
+ }
+ }
+
+ doc.internals.remaining--;
+ if (doc.internals.remaining === 0) {
+ // We're done processing this set of KML documents
+ // Options that get invoked after parsing completes
+ if (parserOptions.zoom && !!doc.internals.bounds) {
+ parserOptions.map.fitBounds(doc.internals.bounds);
+ }
+ if (parserOptions.afterParse) {
+ parserOptions.afterParse(doc.internals.docSet);
+ }
+ }
+ };
+
+ var kmlColor = function (kmlIn, colorMode) {
+ var kmlColor = {};
+ kmlIn = kmlIn || 'ffffffff'; // white (KML 2.2 default)
+
+ var aa = kmlIn.substr(0,2);
+ var bb = kmlIn.substr(2,2);
+ var gg = kmlIn.substr(4,2);
+ var rr = kmlIn.substr(6,2);
+
+ kmlColor.opacity = parseInt(aa, 16) / 256;
+ kmlColor.color = (colorMode === 'random') ? randomColor(rr, gg, bb) : '#' + rr + gg + bb;
+ return kmlColor;
+ };
+
+ // Implemented per KML 2.2 <ColorStyle> specs
+ var randomColor = function(rr, gg, bb) {
+ var col = { rr: rr, gg: gg, bb: bb };
+ for (var k in col) {
+ var v = col[k];
+ if (v == null) v = 'ff';
+
+ // RGB values are limiters for random numbers (ie: 7f would be a random value between 0 and 7f)
+ v = Math.round(Math.random() * parseInt(rr, 16)).toString(16);
+ if (v.length === 1) v = '0' + v;
+ col[k] = v;
+ }
+
+ return '#' + col.rr + col.gg + col.bb;
+ };
+
+ var processStyleID = function (style) {
+ var icon = style.icon;
+ if (!icon.href) return;
+
+ if (icon.img && !icon.img.complete) {
+ // we're still waiting on the image loading (probably because we've been blocking since the declaration)
+ // so, let's queue this function on the onload stack
+ icon.markerBacklog = [];
+ icon.img.onload = function() {
+ if (icon.dim.w < 0 || icon.dim.h < 0) {
+ icon.dim.w = this.width;
+ icon.dim.h = this.height;
+ }
+ processStyleID(style);
+
+ // we will undoubtedly get some createMarker queuing, so set this up in advance
+ for (var i = 0; i < icon.markerBacklog.length; i++) {
+ var p = icon.markerBacklog[i][0];
+ var d = icon.markerBacklog[i][1];
+ createMarker(p, d);
+ if (p.marker) p.marker.active = true;
+ }
+ delete icon.markerBacklog;
+ };
+ return;
+ }
+ else if (icon.dim.w < 0 || icon.dim.h < 0) {
+ if (icon.img && icon.img.complete) {
+ // sometimes the file is already cached and it never calls onLoad
+ icon.dim.w = icon.img.width;
+ icon.dim.h = icon.img.height;
+ }
+ else {
+ // settle for a default of 32x32
+ icon.dim.whGuess = true;
+ icon.dim.w = 32;
+ icon.dim.h = 32;
+ }
+ }
+
+ // pre-scaled variables
+ var rnd = Math.round;
+ var scaled = {
+ x: icon.dim.x * icon.scale,
+ y: icon.dim.y * icon.scale,
+ w: icon.dim.w * icon.scale,
+ h: icon.dim.h * icon.scale,
+ aX: icon.hotSpot.x * icon.scale,
+ aY: icon.hotSpot.y * icon.scale,
+ iW: (icon.img ? icon.img.width : icon.dim.w) * icon.scale,
+ iH: (icon.img ? icon.img.height : icon.dim.h) * icon.scale
+ };
+
+ // Figure out the anchor spot
+ var aX, aY;
+ switch (icon.hotSpot.xunits) {
+ case 'fraction': aX = rnd(scaled.aX * icon.dim.w); break;
+ case 'insetPixels': aX = rnd(icon.dim.w * icon.scale - scaled.aX); break;
+ default: aX = rnd(scaled.aX); break; // already pixels
+ }
+ aY = rnd( ((icon.hotSpot.yunits === 'fraction') ? icon.dim.h : 1) * scaled.aY ); // insetPixels Y = pixels Y
+ var iconAnchor = new google.maps.Point(aX, aY);
+
+ // Sizes
+ // (NOTE: Scale is applied to entire image, not just the section of the icon palette.)
+ var iconSize = icon.dim.whGuess ? null : new google.maps.Size(rnd(scaled.w), rnd(scaled.h));
+ var iconScale = icon.scale == 1.0 ? null :
+ icon.dim.whGuess ? new google.maps.Size(rnd(scaled.w), rnd(scaled.h))
+ : new google.maps.Size(rnd(scaled.iW), rnd(scaled.iH));
+ var iconOrigin = new google.maps.Point(rnd(scaled.x), rnd(scaled.y));
+
+ // Detect images buried in KMZ files (and use a base64 encoded URL)
+ if (kmzMetaData[icon.url]) icon.url = kmzMetaData[icon.url].dataUrl;
+
+ // Init the style object with the KML icon
+ icon.marker = new google.maps.MarkerImage(
+ icon.url, // url
+ iconSize, // size
+ iconOrigin, // origin
+ iconAnchor, // anchor
+ iconScale // scaledSize
+ );
+
+ // Look for a predictable shadow
+ var stdRegEx = /\/(red|blue|green|yellow|lightblue|purple|pink|orange)(-dot)?\.png/;
+ var shadowSize = new google.maps.Size(59, 32);
+ var shadowPoint = new google.maps.Point(16, 32);
+ if (stdRegEx.test(icon.href)) {
+ // A standard GMap-style marker icon
+ icon.shadow = new google.maps.MarkerImage(
+ 'http://maps.google.com/mapfiles/ms/micons/msmarker.shadow.png', // url
+ shadowSize, // size
+ null, // origin
+ shadowPoint, // anchor
+ shadowSize // scaledSize
+ );
+ } else if (icon.href.indexOf('-pushpin.png') > -1) {
+ // Pushpin marker icon
+ icon.shadow = new google.maps.MarkerImage(
+ 'http://maps.google.com/mapfiles/ms/micons/pushpin_shadow.png', // url
+ shadowSize, // size
+ null, // origin
+ shadowPoint, // anchor
+ shadowSize // scaledSize
+ );
+ } /* else {
+ // Other MyMaps KML standard icon
+ icon.shadow = new google.maps.MarkerImage(
+ icon.href.replace('.png', '.shadow.png'), // url
+ shadowSize, // size
+ null, // origin
+ anchorPoint, // anchor
+ shadowSize // scaledSize
+ );
+ } */
+ }
+
+ var processStyles = function (doc) {
+ for (var styleID in doc.styles) {
+ processStyleID(doc.styles[styleID]);
+ }
+ };
+
+ var createMarker = function (placemark, doc) {
+ // create a Marker to the map from a placemark KML object
+ var icon = placemark.style.icon;
+
+ if ( !icon.marker && icon.img ) {
+ // yay, single point of failure is holding up multiple markers...
+ icon.markerBacklog = icon.markerBacklog || [];
+ icon.markerBacklog.push([placemark, doc]);
+ return;
+ }
+
+ // Load basic marker properties
+ var markerOptions = geoXML3.combineOptions(parserOptions.markerOptions, {
+ map: parserOptions.map,
+ position: new google.maps.LatLng(placemark.Point.coordinates[0].lat, placemark.Point.coordinates[0].lng),
+ title: placemark.name,
+ zIndex: Math.round(placemark.Point.coordinates[0].lat * -100000)<<5,
+ icon: icon.marker,
+ shadow: icon.shadow,
+ flat: !icon.shadow,
+ visible: placemark.visibility
+ });
+
+ // Create the marker on the map
+ var marker = new google.maps.Marker(markerOptions);
+ if (!!doc) doc.markers.push(marker);
+
+ // Set up and create the infowindow if it is not suppressed
+ createInfoWindow(placemark, doc, marker);
+ placemark.marker = marker;
+ return marker;
+ };
+
+ var createOverlay = function (groundOverlay, doc) {
+ // Add a ProjectedOverlay to the map from a groundOverlay KML object
+
+ if (!window.ProjectedOverlay) {
+ throw 'geoXML3 error: ProjectedOverlay not found while rendering GroundOverlay from KML';
+ }
+
+ var bounds = new google.maps.LatLngBounds(
+ new google.maps.LatLng(groundOverlay.latLonBox.south, groundOverlay.latLonBox.west),
+ new google.maps.LatLng(groundOverlay.latLonBox.north, groundOverlay.latLonBox.east)
+ );
+ var overlayOptions = geoXML3.combineOptions(parserOptions.overlayOptions, {percentOpacity: groundOverlay.opacity*100});
+ var overlay = new ProjectedOverlay(parserOptions.map, groundOverlay.icon.href, bounds, overlayOptions);
+
+ if (!!doc) {
+ doc.ggroundoverlays = doc.ggroundoverlays || [];
+ doc.ggroundoverlays.push(overlay);
+ }
+
+ return overlay;
+ };
+
+ // Create Polyline
+ var createPolyline = function(placemark, doc) {
+ var path = [];
+ for (var j=0; j<placemark.LineString.length; j++) {
+ var coords = placemark.LineString[j].coordinates;
+ var bounds = new google.maps.LatLngBounds();
+ for (var i=0;i<coords.length;i++) {
+ var pt = new google.maps.LatLng(coords[i].lat, coords[i].lng);
+ path.push(pt);
+ bounds.extend(pt);
+ }
+ }
+ // point to open the infowindow if triggered
+ var point = path[Math.floor(path.length/2)];
+ // Load basic polyline properties
+ var kmlStrokeColor = kmlColor(placemark.style.line.color, placemark.style.line.colorMode);
+ var polyOptions = geoXML3.combineOptions(parserOptions.polylineOptions, {
+ map: parserOptions.map,
+ path: path,
+ strokeColor: kmlStrokeColor.color,
+ strokeWeight: placemark.style.line.width,
+ strokeOpacity: kmlStrokeColor.opacity,
+ title: placemark.name,
+ visible: placemark.visibility
+ });
+ var p = new google.maps.Polyline(polyOptions);
+ p.bounds = bounds;
+
+ // setup and create the infoWindow if it is not suppressed
+ createInfoWindow(placemark, doc, p);
+ if (!!doc) doc.gpolylines.push(p);
+ placemark.polyline = p;
+ return p;
+ }
+
+ // Create Polygon
+ var createPolygon = function(placemark, doc) {
+ var bounds = new google.maps.LatLngBounds();
+ var pathsLength = 0;
+ var paths = [];
+ for (var polygonPart=0;polygonPart<placemark.Polygon.length;polygonPart++) {
+ for (var j=0; j<placemark.Polygon[polygonPart].outerBoundaryIs.length; j++) {
+ var coords = placemark.Polygon[polygonPart].outerBoundaryIs[j].coordinates;
+ var path = [];
+ for (var i=0;i<coords.length;i++) {
+ var pt = new google.maps.LatLng(coords[i].lat, coords[i].lng);
+ path.push(pt);
+ bounds.extend(pt);
+ }
+ paths.push(path);
+ pathsLength += path.length;
+ }
+ for (var j=0; j<placemark.Polygon[polygonPart].innerBoundaryIs.length; j++) {
+ var coords = placemark.Polygon[polygonPart].innerBoundaryIs[j].coordinates;
+ var path = [];
+ for (var i=0;i<coords.length;i++) {
+ var pt = new google.maps.LatLng(coords[i].lat, coords[i].lng);
+ path.push(pt);
+ bounds.extend(pt);
+ }
+ paths.push(path);
+ pathsLength += path.length;
+ }
+ }
+
+ // Load basic polygon properties
+ var kmlStrokeColor = kmlColor(placemark.style.line.color, placemark.style.line.colorMode);
+ var kmlFillColor = kmlColor(placemark.style.poly.color, placemark.style.poly.colorMode);
+ if (!placemark.style.poly.fill) kmlFillColor.opacity = 0.0;
+ var strokeWeight = placemark.style.line.width;
+ if (!placemark.style.poly.outline) {
+ strokeWeight = 0;
+ kmlStrokeColor.opacity = 0.0;
+ }
+ var polyOptions = geoXML3.combineOptions(parserOptions.polygonOptions, {
+ map: parserOptions.map,
+ paths: paths,
+ title: placemark.name,
+ strokeColor: kmlStrokeColor.color,
+ strokeWeight: strokeWeight,
+ strokeOpacity: kmlStrokeColor.opacity,
+ fillColor: kmlFillColor.color,
+ fillOpacity: kmlFillColor.opacity,
+ visible: placemark.visibility
+ });
+ var p = new google.maps.Polygon(polyOptions);
+ p.bounds = bounds;
+
+ createInfoWindow(placemark, doc, p);
+ if (!!doc) doc.gpolygons.push(p);
+ placemark.polygon = p;
+ return p;
+ }
+
+ var createInfoWindow = function(placemark, doc, gObj) {
+ var bStyle = placemark.style.balloon;
+ var vars = placemark.vars;
+
+ if (!placemark.balloonVisibility || bStyle.displayMode === 'hide') return;
+
+ // define geDirections
+ if (placemark.latlng) {
+ vars.directions.push('sll=' + placemark.latlng.toUrlValue());
+
+ var url = 'http://maps.google.com/maps?' + vars.directions.join('&');
+ var address = encodeURIComponent( vars.val.address || placemark.latlng.toUrlValue() ).replace(/\%20/g, '+');
+
+ vars.val.geDirections = '<a href="' + url + '&daddr=' + address + '" target=_blank>To Here</a> - <a href="' + url + '&saddr=' + address + '" target=_blank>From Here</a>';
+ }
+ else vars.val.geDirections = '';
+
+ // add in the variables
+ var iwText = bStyle.text.replace(/\$\[(\w+(\/displayName)?)\]/g, function(txt, n, dn) { return dn ? vars.display[n] : vars.val[n]; });
+ var classTxt = 'geoxml3_infowindow geoxml3_style_' + placemark.styleID;
+
+ // color styles
+ var styleArr = [];
+ if (bStyle.bgColor != 'ffffffff') styleArr.push('background: ' + kmlColor(bStyle.bgColor ).color + ';');
+ if (bStyle.textColor != 'ff000000') styleArr.push('color: ' + kmlColor(bStyle.textColor).color + ';');
+ var styleProp = styleArr.length ? ' style="' + styleArr.join(' ') + '"' : '';
+
+ var infoWindowOptions = geoXML3.combineOptions(parserOptions.infoWindowOptions, {
+ content: '<div class="' + classTxt + '"' + styleProp + '>' + iwText + '</div>',
+ pixelOffset: new google.maps.Size(0, 2)
+ });
+
+ gObj.infoWindow = parserOptions.infoWindow || new google.maps.InfoWindow(infoWindowOptions);
+ gObj.infoWindowOptions = infoWindowOptions;
+
+ // Info Window-opening event handler
+ google.maps.event.addListener(gObj, 'click', function(e) {
+ var iW = this.infoWindow;
+ iW.close();
+ iW.setOptions(this.infoWindowOptions);
+
+ if (e && e.latLng) iW.setPosition(e.latLng);
+ else if (this.bounds) iW.setPosition(this.bounds.getCenter());
+
+ iW.open(this.map, this.bounds ? null : this);
+ });
+
+ }
+
+ return {
+ // Expose some properties and methods
+
+ options: parserOptions,
+ docs: docs,
+ docsByUrl: docsByUrl,
+ kmzMetaData: kmzMetaData,
+
+ parse: parse,
+ parseKmlString: parseKmlString,
+ hideDocument: hideDocument,
+ showDocument: showDocument,
+ processStyles: processStyles,
+ createMarker: createMarker,
+ createOverlay: createOverlay,
+ createPolyline: createPolyline,
+ createPolygon: createPolygon
+ };
+};
+// End of KML Parser
+
+// Helper objects and functions
+geoXML3.getOpacity = function (kmlColor) {
+ // Extract opacity encoded in a KML color value. Returns a number between 0 and 1.
+ if (!!kmlColor &&
+ (kmlColor !== '') &&
+ (kmlColor.length == 8)) {
+ var transparency = parseInt(kmlColor.substr(0, 2), 16);
+ return transparency / 255;
+ } else {
+ return 1;
+ }
+};
+
+// Log a message to the debugging console, if one exists
+geoXML3.log = function(msg) {
+ if (!!window.console) {
+ console.log(msg);
+ } else { alert("log:"+msg); }
+};
+
+/**
+ * Creates a new parserOptions object.
+ * @class GeoXML3 parser options.
+ * @param {Object} overrides Any options you want to declare outside of the defaults should be included here.
+ * @property {google.maps.Map} map The API map on which geo objects should be rendered.
+ * @property {google.maps.MarkerOptions} markerOptions If the parser is adding Markers to the map itself, any options specified here will be applied to them.
+ * @property {google.maps.InfoWindowOptions} infoWindowOptions If the parser is adding Markers to the map itself, any options specified here will be applied to their attached InfoWindows.
+ * @property {ProjectedOverlay.options} overlayOptions If the parser is adding ProjectedOverlays to the map itself, any options specified here will be applied to them.
+ */
+geoXML3.parserOptions = function (overrides) {
+ this.map = null,
+ /** If true, the parser will automatically move the map to a best-fit of the geodata after parsing of a KML document completes.
+ * @type Boolean
+ * @default true
+ */
+ this.zoom = true,
+ /**#@+ @type Boolean
+ * @default false */
+ /** If true, only a single Marker created by the parser will be able to have its InfoWindow open at once (simulating the behavior of GMaps API v2). */
+ this.singleInfoWindow = false,
+ /** If true, suppresses the rendering of info windows. */
+ this.suppressInfoWindows = false,
+ /**
+ * Control whether to process styles now or later.
+ *
+ * <p>By default, the parser only processes KML &lt;Style&gt; elements into their GMaps equivalents
+ * if it will be creating its own Markers (the createMarker option is null). Setting this option
+ * to true will force such processing to happen anyway, useful if you're going to be calling parser.createMarker
+ * yourself later. OTOH, leaving this option false removes runtime dependency on the GMaps API, enabling
+ * the use of geoXML3 as a standalone KML parser.</p>
+ */
+ this.processStyles = false,
+ /**#@-*/
+
+ this.markerOptions = {},
+ this.infoWindowOptions = {},
+ this.overlayOptions = {},
+
+ /**#@+ @event */
+ /** This function will be called when parsing of a KML document is complete.
+ * @param {geoXML3.parser#docs} doc Parsed KML data. */
+ this.afterParse = null,
+ /** This function will be called when parsing of a KML document is complete.
+ * @param {geoXML3.parser#docs} doc Parsed KML data. */
+ this.failedParse = null,
+ /**
+ * If supplied, this function will be called once for each marker <Placemark> in the KML document, instead of the parser adding its own Marker to the map.
+ * @param {geoXML3.parser.render#placemark} placemark Placemark object.
+ * @param {geoXML3.parser#docs} doc Parsed KML data.
+ */
+ this.createMarker = null,
+ /**
+ * If supplied, this function will be called once for each <GroundOverlay> in the KML document, instead of the parser adding its own ProjectedOverlay to the map.
+ * @param {geoXML3.parser.render#groundOverlay} groundOverlay GroundOverlay object.
+ * @param {geoXML3.parser#docs} doc Parsed KML data.
+ */
+ this.createOverlay = null
+ /**#@-*/
+
+ if (overrides) {
+ for (var prop in overrides) {
+ if (overrides.hasOwnProperty(prop)) this[prop] = overrides[prop];
+ }
+ }
+ return this;
+};
+
+/**
+ * Combine two options objects: a set of default values and a set of override values.
+ *
+ * @deprecated This has been replaced with {@link geoXML3.parserOptions#combineOptions}.
+ * @param {geoXML3.parserOptions|Object} overrides Override values.
+ * @param {geoXML3.parserOptions|Object} defaults Default values.
+ * @return {geoXML3.parserOptions} Combined result.
+ */
+geoXML3.combineOptions = function (overrides, defaults) {
+ var result = {};
+ if (!!overrides) {
+ for (var prop in overrides) {
+ if (overrides.hasOwnProperty(prop)) result[prop] = overrides[prop];
+ }
+ }
+ if (!!defaults) {
+ for (prop in defaults) {
+ if (defaults.hasOwnProperty(prop) && result[prop] === undefined) result[prop] = defaults[prop];
+ }
+ }
+ return result;
+};
+
+/**
+ * Combine two options objects: a set of default values and a set of override values.
+ *
+ * @function
+ * @param {geoXML3.parserOptions|Object} overrides Override values.
+ * @param {geoXML3.parserOptions|Object} defaults Default values.
+ * @return {geoXML3.parserOptions} Combined result.
+ */
+geoXML3.parserOptions.prototype.combineOptions = geoXML3.combineOptions;
+
+// Retrieve an XML document from url and pass it to callback as a DOM document
+geoXML3.fetchers = [];
+
+/**
+ * Parses a XML string.
+ *
+ * <p>Parses the given XML string and returns the parsed document in a
+ * DOM data structure. This function will return an empty DOM node if
+ * XML parsing is not supported in this browser.</p>
+ *
+ * @param {String} str XML string.
+ * @return {Element|Document} DOM.
+ */
+geoXML3.xmlParse = function (str) {
+ if (!!window.DOMParser) return (new DOMParser()).parseFromString(str, 'text/xml');
+ else if (!!window.ActiveXObject) {
+ var doc;
+
+ // the many versions of IE's DOM parsers
+ var AXOs = [
+ 'MSXML2.DOMDocument.6.0',
+ 'MSXML2.DOMDocument.5.0',
+ 'MSXML2.DOMDocument.4.0',
+ 'MSXML2.DOMDocument.3.0',
+ 'MSXML2.DOMDocument',
+ 'Microsoft.XMLDOM',
+ 'MSXML.DOMDocument'
+ ];
+ for (var i = 0; i < AXOs.length; i++) {
+ try { doc = new ActiveXObject(AXOs[i]); break; }
+ catch(e) { continue; }
+ }
+ if (!doc) return createElement('div', null);
+
+ if (doc.async) doc.async = false;
+ doc.loadXML(str);
+ return doc;
+ }
+
+ return createElement('div', null);
+}
+
+/**
+ * Fetches a XML document.
+ *
+ * <p>Fetches/parses the given XML URL and passes the parsed document (in a
+ * DOM data structure) to the given callback. Documents are downloaded
+ * and parsed asynchronously.</p>
+ *
+ * @param {String} url URL of XML document. Must be uncompressed XML only.
+ * @param {Function(Document)} callback Function to call when the document is processed.
+ */
+geoXML3.fetchXML = function (url, callback) {
+ function timeoutHandler() { callback(); };
+
+ var xhrFetcher = new Object();
+ if (!!geoXML3.fetchers.length) xhrFetcher = geoXML3.fetchers.pop();
+ else if (!!window.XMLHttpRequest) xhrFetcher.fetcher = new window.XMLHttpRequest(); // Most browsers
+ else if (!!window.ActiveXObject) { // Some IE
+ // the many versions of IE's XML fetchers
+ var AXOs = [
+ 'MSXML2.XMLHTTP.6.0',
+ 'MSXML2.XMLHTTP.5.0',
+ 'MSXML2.XMLHTTP.4.0',
+ 'MSXML2.XMLHTTP.3.0',
+ 'MSXML2.XMLHTTP',
+ 'Microsoft.XMLHTTP',
+ 'MSXML.XMLHTTP'
+ ];
+ for (var i = 0; i < AXOs.length; i++) {
+ try { xhrFetcher.fetcher = new ActiveXObject(AXOs[i]); break; }
+ catch(e) { continue; }
+ }
+ if (!xhrFetcher.fetcher) {
+ geoXML3.log('Unable to create XHR object');
+ callback(null);
+ return null;
+ }
+ }
+
+ if (!!xhrFetcher.fetcher.overrideMimeType) xhrFetcher.fetcher.overrideMimeType('text/xml');
+ xhrFetcher.fetcher.open('GET', url, true);
+ xhrFetcher.fetcher.onreadystatechange = function () {
+ if (xhrFetcher.fetcher.readyState === 4) {
+ // Retrieval complete
+ if (!!xhrFetcher.xhrtimeout) clearTimeout(xhrFetcher.xhrtimeout);
+ if (xhrFetcher.fetcher.status >= 400) {
+ geoXML3.log('HTTP error ' + xhrFetcher.fetcher.status + ' retrieving ' + url);
+ callback();
+ }
+ // Returned successfully
+ else {
+ if (xhrFetcher.fetcher.responseXML) {
+ // Sometimes IE will get the data, but won't bother loading it as an XML doc
+ var xmlDoc = xhrFetcher.fetcher.responseXML;
+ if (xmlDoc && !xmlDoc.documentElement && !xmlDoc.ownerElement) xmlDoc.loadXML(xhrFetcher.fetcher.responseText);
+ callback(xmlDoc);
+ } else // handle valid xml sent with wrong MIME type
+ callback(geoXML3.xmlParse(xhrFetcher.fetcher.responseText));
+ }
+
+ // We're done with this fetcher object
+ geoXML3.fetchers.push(xhrFetcher);
+ }
+ };
+
+ xhrFetcher.xhrtimeout = setTimeout(timeoutHandler, 60000);
+ xhrFetcher.fetcher.send(null);
+ return null;
+};
+
+/**
+ * Fetches a KMZ document.
+ *
+ * <p>Fetches/parses the given ZIP URL, parses each image file, and passes
+ * the parsed KML document to the given callback. Documents are downloaded
+ * and parsed asynchronously, though the KML file is always passed after the
+ * images have been processed, in case the callback requires the image data.</p>
+ *
+ * @requires ZipFile.complete.js
+ * @param {String} url URL of KMZ document. Must be a valid KMZ/ZIP archive.
+ * @param {Function(Document)} callback Function to call when the document is processed.
+ * @param {geoXML3.parser} parser A geoXML3.parser object. This is used to populate the KMZ image data.
+ * @author Brendan Byrd
+ * @see http://code.google.com/apis/kml/documentation/kmzarchives.html
+ */
+geoXML3.fetchZIP = function (url, callback, parser) {
+ // Just need a single 'new' declaration with a really long function...
+ var zipFile = new ZipFile(url, function (zip) {
+ // Retrieval complete
+
+ // Check for ERRORs in zip.status
+ for (var i = 0; i < zip.status.length; i++) {
+ var msg = zip.status[i];
+ if (msg.indexOf("ERROR") == 0) {
+ geoXML3.log('HTTP/ZIP error retrieving ' + url + ': ' + msg);
+ callback();
+ return;
+ }
+ else if (msg.indexOf("WARNING") == 0) { // non-fatal, but still might be useful
+ geoXML3.log('HTTP/ZIP warning retrieving ' + url + ': ' + msg);
+ }
+ }
+
+ // Make sure KMZ structure is according to spec (with a single KML file in the root dir)
+ var KMLCount = 0;
+ var KML;
+ for (var i = 0; i < zip.entries.length; i++) {
+ var name = zip.entries[i].name;
+ if (!/\.kml$/.test(name)) continue;
+
+ KMLCount++;
+ if (KMLCount == 1) KML = i;
+ else {
+ geoXML3.log('KMZ warning retrieving ' + url + ': found extra KML "' + name + '" in KMZ; discarding...');
+ }
+ }
+
+ // Returned successfully, but still needs extracting
+ var baseUrl = cleanURL(defileURL(url), url) + '/';
+ var kmlProcessing = { // this is an object just so it gets passed properly
+ timer: null,
+ extractLeft: 0,
+ timerCalls: 0
+ };
+ var extractCb = function(entry, entryContent) {
+ var mdUrl = cleanURL(baseUrl, entry.name);
+ var ext = entry.name.substring(entry.name.lastIndexOf(".") + 1).toLowerCase();
+ kmlProcessing.extractLeft--;
+
+ if ((typeof entryContent.description == "string") && (entryContent.name == "Error")) {
+ geoXML3.log('KMZ error extracting ' + mdUrl + ': ' + entryContent.description);
+ callback();
+ return;
+ }
+
+ // MIME types that can be used in KML
+ var mime;
+ if (ext === 'jpg') ext = 'jpeg';
+ if (/^(gif|jpeg|png)$/.test(ext)) mime = 'image/' + ext;
+ else if (ext === 'mp3') mime = 'audio/mpeg';
+ else if (ext === 'm4a') mime = 'audio/mp4';
+ else if (ext === 'm4a') mime = 'audio/MP4-LATM';
+ else mime = 'application/octet-stream';
+
+ parser.kmzMetaData[mdUrl] = {};
+ parser.kmzMetaData[mdUrl].entry = entry;
+ // data:image/gif;base64,R0lGODlhEAAOALMA...
+ parser.kmzMetaData[mdUrl].dataUrl = 'data:' + mime + ';base64,' + base64Encode(entryContent);
+
+ // IE cannot handle GET requests beyond 2071 characters, even if it's an inline image
+ if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent) && parser.kmzMetaData[mdUrl].dataUrl.length > 2071)
+ parser.kmzMetaData[mdUrl].dataUrl =
+ // this is a simple IE icon; to hint at the problem...
+ 'data:image/gif;base64,R0lGODlhDwAQAOMPADBPvSpQ1Dpoyz1p6FhwvU2A6ECP63CM04CWxYCk+V6x+UK++Jao3rvC3fj7+v///yH5BAEKAA8ALAAAAAAPABAAAASC8Mk5mwCAUMlWwcLRHEelLA' +
+ 'oGDMgzSsiyGCAhCETDPMh5XQCBwYBrNBIKWmg0MCQHj8MJU5IoroYCY6AAAgrDIbbQDGIK6DR5UPhlNo0JAlSUNAiDgH7eNAxEDWAKCQM2AAFheVxYAA0AIkFOJ1gBcQQaUQKKA5w7LpcEBwkJaKMUEQA7';
+ };
+ var kmlExtractCb = function(entry, entryContent) {
+ if ((typeof entryContent.description == "string") && (entryContent.name == "Error")) {
+ geoXML3.log('KMZ error extracting ' + mdUrl + ': ' + entryContent.description);
+ callback();
+ return;
+ }
+
+ // check to see if the KML is the last file extracted
+ clearTimeout(kmlProcessing.timer);
+ if (kmlProcessing.extractLeft <= 1) {
+ kmlProcessing.extractLeft--;
+ callback(geoXML3.xmlParse(entryContent));
+ return;
+ }
+ else {
+ // KML file isn't last yet; it may need to use those files, so wait a bit (100ms)
+ kmlProcessing.timerCalls++;
+ if (kmlProcessing.timerCalls < 100) {
+ kmlProcessing.timer = setTimeout(function() { kmlExtractCb(entry, entryContent); }, 100);
+ }
+ else {
+ geoXML3.log('KMZ warning extracting ' + url + ': entire ZIP has not been extracted after 10 seconds; running through KML, anyway...');
+ kmlProcessing.extractLeft--;
+ callback(geoXML3.xmlParse(entryContent));
+ }
+ }
+ return;
+ };
+ for (var i = 0; i < zip.entries.length; i++) {
+ var entry = zip.entries[i];
+ var ext = entry.name.substring(entry.name.lastIndexOf(".") + 1).toLowerCase();
+ if (!/^(gif|jpe?g|png|kml)$/.test(ext)) continue; // not going to bother to extract files we don't support
+ if (ext === "kml" && i != KML) continue; // extra KMLs get discarded
+ if (!parser && ext != "kml") continue; // cannot store images without a parser object
+
+ // extract asynchronously
+ kmlProcessing.extractLeft++;
+ if (ext === "kml") entry.extract(kmlExtractCb);
+ else entry.extract(extractCb);
+ }
+ });
+
+};
+
+/**
+ * Extract the text value of a DOM node, with leading and trailing whitespace trimmed.
+ *
+ * @param {Element} node XML node/element.
+ * @param {Any} delVal Default value if the node doesn't exist.
+ * @return {String|Null}
+ */
+geoXML3.nodeValue = function(node, defVal) {
+ var retStr="";
+ if (!node) {
+ return (typeof defVal === 'undefined' || defVal === null) ? null : defVal;
+ }
+ if(node.nodeType==3||node.nodeType==4||node.nodeType==2){
+ retStr+=node.nodeValue;
+ }else if(node.nodeType==1||node.nodeType==9||node.nodeType==11){
+ for(var i=0;i<node.childNodes.length;++i){
+ retStr+=arguments.callee(node.childNodes[i]);
+ }
+ }
+ return retStr;
+};
+
+/**
+ * Loosely translate various values of a DOM node to a boolean.
+ *
+ * @param {Element} node XML node/element.
+ * @param {Boolean} delVal Default value if the node doesn't exist.
+ * @return {Boolean|Null}
+ */
+geoXML3.getBooleanValue = function(node, defVal) {
+ var nodeContents = geoXML3.nodeValue(node);
+ if (nodeContents === null) return defVal || false;
+ nodeContents = parseInt(nodeContents);
+ if (isNaN(nodeContents)) return true;
+ if (nodeContents == 0) return false;
+ else return true;
+}
+
+/**
+ * Browser-normalized version of getElementsByTagNameNS.
+ *
+ * <p>Required because IE8 doesn't define it.</p>
+ *
+ * @param {Element|Document} node DOM object.
+ * @param {String} namespace Full namespace URL to search against.
+ * @param {String} tagname XML local tag name.
+ * @return {Array of Elements}
+ * @author Brendan Byrd
+ */
+geoXML3.getElementsByTagNameNS = function(node, namespace, tagname) {
+ if (node && typeof node.getElementsByTagNameNS != 'undefined') return node.getElementsByTagNameNS(namespace, tagname);
+ if (!node) return [];
+
+ var root = node.documentElement || node.ownerDocument && node.ownerDocument.documentElement;
+ if (!root || !root.attributes) return [];
+
+ // search for namespace prefix
+ for (var i = 0; i < root.attributes.length; i++) {
+ var attr = root.attributes[i];
+ if (attr.prefix === 'xmlns' && attr.nodeValue === namespace) return node.getElementsByTagName(attr.baseName + ':' + tagname);
+ else if (attr.nodeName === 'xmlns' && attr.nodeValue === namespace) {
+ // default namespace
+ if (typeof node.selectNodes != 'undefined') {
+ // Newer IEs have the SelectionNamespace property that can be used with selectNodes
+ if (!root.ownerDocument.getProperty('SelectionNamespaces'))
+ root.ownerDocument.setProperty('SelectionNamespaces', "xmlns:defaultNS='" + namespace + "'");
+ return node.selectNodes('.//defaultNS:' + tagname);
+ }
+ else {
+ // Otherwise, you can still try to tack on the 'xmlns' attribute to root
+ root.setAttribute('xmlns:defaultNS', namespace);
+ return node.getElementsByTagName('defaultNS:' + tagname);
+ }
+ }
+ }
+ return geoXML3.getElementsByTagName(node, tagname); // try the unqualified version
+};
+
+/**
+ * Browser-normalized version of getElementsByTagName.
+ *
+ * <p>Required because MSXML 6.0 will treat this function as a NS-qualified function,
+ * despite the missing NS parameter.</p>
+ *
+ * @param {Element|Document} node DOM object.
+ * @param {String} tagname XML local tag name.
+ * @return {Array of Elements}
+ * @author Brendan Byrd
+ */
+geoXML3.getElementsByTagName = function(node, tagname) {
+ if (node && typeof node.getElementsByTagNameNS != 'undefined') return node.getElementsByTagName(tagname); // if it has both functions, it should be accurate
+// if (node && typeof node.selectNodes != 'undefined') return node.selectNodes(".//*[local-name()='" + tagname + "']");
+ return node.getElementsByTagName(tagname); // hope for the best...
+}
+
+/**
+ * Turn a directory + relative URL into an absolute one.
+ *
+ * @private
+ * @param {String} d Base directory.
+ * @param {String} s Relative URL.
+ * @return {String} Absolute URL.
+ * @author Brendan Byrd
+ */
+var toAbsURL = function (d, s) {
+ var p, f, i;
+ var h = location.protocol + "://" + location.host;
+
+ if (!s.length) return '';
+ if (/^\w+:/.test(s)) return s;
+ if (s.indexOf('/') == 0) return h + s;
+
+ p = d.replace(/\/[^\/]*$/, '');
+ f = s.match(/\.\.\//g);
+ if (f) {
+ s = s.substring(f.length * 3);
+ for (i = f.length; i--;) { p = p.substring(0, p.lastIndexOf('/')); }
+ }
+
+ return h + p + '/' + s;
+}
+
+/**
+ * Remove current host from URL
+ *
+ * @private
+ * @param {String} s Absolute or relative URL.
+ * @return {String} Root-based relative URL.
+ * @author Brendan Byrd
+ */
+var dehostURL = function (s) {
+ var h = location.protocol + "://" + location.host;
+ h = h.replace(/([\.\\\+\*\?\[\^\]\$\(\)])/g, '\\$1'); // quotemeta
+ return s.replace(new RegExp('^' + h, 'i'), '');
+}
+
+/**
+ * Removes all query strings, #IDs, '../' references, and
+ * hosts from a URL.
+ *
+ * @private
+ * @param {String} d Base directory.
+ * @param {String} s Absolute or relative URL.
+ * @return {String} Root-based relative URL.
+ * @author Brendan Byrd
+ */
+var cleanURL = function (d, s) { return dehostURL(toAbsURL(d ? d.split('#')[0].split('?')[0] : defileURL(location.pathname), s ? s.split('#')[0].split('?')[0] : '')); }
+/**
+ * Remove filename from URL
+ *
+ * @private
+ * @param {String} s Relative URL.
+ * @return {String} Base directory.
+ * @author Brendan Byrd
+ */
+var defileURL = function (s) { return s ? s.substr(0, s.lastIndexOf('/') + 1) : '/'; }
+
+
+// Some extra Array subs for ease of use
+// http://stackoverflow.com/questions/143847/best-way-to-find-an-item-in-a-javascript-array
+Array.prototype.hasObject = (
+ !Array.indexOf ? function (obj) {
+ var l = this.length + 1;
+ while (l--) {
+ if (this[l - 1] === obj) return true;
+ }
+ return false;
+ } : function (obj) {
+ return (this.indexOf(obj) !== -1);
+ }
+);
+Array.prototype.hasItemInObj = function (name, item) {
+ var l = this.length + 1;
+ while (l--) {
+ if (this[l - 1][name] === item) return true;
+ }
+ return false;
+};
+if (!Array.prototype.indexOf) {
+ Array.prototype.indexOf = function (obj, fromIndex) {
+ if (fromIndex == null) {
+ fromIndex = 0;
+ } else if (fromIndex < 0) {
+ fromIndex = Math.max(0, this.length + fromIndex);
+ }
+ for (var i = fromIndex, j = this.length; i < j; i++) {
+ if (this[i] === obj) return i;
+ }
+ return -1;
+ };
+}
+Array.prototype.indexOfObjWithItem = function (name, item, fromIndex) {
+ if (fromIndex == null) {
+ fromIndex = 0;
+ } else if (fromIndex < 0) {
+ fromIndex = Math.max(0, this.length + fromIndex);
+ }
+ for (var i = fromIndex, j = this.length; i < j; i++) {
+ if (this[i][name] === item) return i;
+ }
+ return -1;
+};
+
+/**
+ * Borrowed from jquery.base64.js, with some "Array as input" corrections
+ *
+ * @private
+ * @param {Array of charCodes} input An array of byte ASCII codes (0-255).
+ * @return {String} A base64-encoded string.
+ * @author Brendan Byrd
+ */
+var base64Encode = function(input) {
+ var keyString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+ var output = "";
+ var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+ var i = 0;
+ while (i < input.length) {
+ chr1 = input[i++];
+ chr2 = input[i++];
+ chr3 = input[i++];
+ enc1 = chr1 >> 2;
+ enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+ enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+ enc4 = chr3 & 63;
+
+ if (chr2 == undefined) enc3 = enc4 = 64;
+ else if (chr3 == undefined) enc4 = 64;
+
+ output = output + keyString.charAt(enc1) + keyString.charAt(enc2) + keyString.charAt(enc3) + keyString.charAt(enc4);
+ }
+ return output;
+};
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/README b/www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/README
new file mode 100644
index 00000000..64a2208c
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/README
@@ -0,0 +1,6 @@
+==markerclusterer.js and markerwithlabel.js==
+Is fetched from trunk @ https://github.com/googlemaps/v3-utility-library
+
+On update don't forget to add the following line at the end of the file:
+
+ window.MarkerClusterer = MarkerClusterer;
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/googleearth-compiled.js b/www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/googleearth-compiled.js
new file mode 100644
index 00000000..e1870183
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/googleearth-compiled.js
@@ -0,0 +1,20 @@
+(function(){var j=window,k=document,l=Math;function n(a,b){return a.width=b}function o(a,b){return a.position=b}function p(a,b){return a.height=b}function q(a,b){return a.zIndex=b}var r="appendChild",s="createElement",t="getCoordinates",u="getView",w="pushLatLngAlt",x="setTimeout",y="style",z="addListener",A="getFeatures",B="InfoWindow",C="maps",D="getPosition",E="setStyleSelector",F="earth",H="prototype",I="setGeometry",J="substring",K="parentNode",L="event";
+function M(a){if(!google||!google[F])throw"google.earth not loaded";if(!google[F].isSupported())throw"Google Earth API is not supported on this system";if(!google[F].isInstalled())throw"Google Earth API is not installed on this system";this.l="//maps.google.com/mapfiles/kml/paddle/red-circle.png";this.b=a;this.k=a.getDiv();this.d=!1;this.f="Earth";this.i=[];this.e={};this.j=null;this.g=0;aa(this);ba(this)}j.GoogleEarth=M;M.MAP_TYPE_ID="GoogleEarthAPI";M[H].o=function(){return this.a};
+M[H].getInstance=M[H].o;
+function aa(a){var b=a.b,c={tileSize:new google[C].Size(256,256),maxZoom:19,name:a.f,alt:a.f,getTile:function(a,b,c){return c[s]("DIV")}};b.mapTypes.set("GoogleEarthAPI",c);b.setOptions({mapTypeControlOptions:{mapTypeIds:[google[C].MapTypeId.ROADMAP,google[C].MapTypeId.SATELLITE,"GoogleEarthAPI"]}});google[C][L][z](b,"maptypeid_changed",function(){if(a.b.getMapTypeId()=="GoogleEarthAPI"){var b;a:{b=RegExp("title=['\"]?"+a.f+"[\"']?");for(var c=a.c[K].childNodes,e=0,g;g=c[e];e++)if(b.test(g.innerHTML)){b=g;
+ break a}b=void 0}c=b;e=c[y].zIndex;g=a.c[K].childNodes;for(var h=0,i;i=g[h];h++)i.__gme_ozi=i[y].zIndex,q(i[y],-1);c.__gme_ozi=e;q(a.c[y],q(c[y],0));c=a.h=k[s]("IFRAME");c.src="javascript:false;";c.scrolling="no";c.frameBorder="0";e=c[y];q(e,-1E5);n(e,p(e,"100%"));o(e,"absolute");e.left=e.top=0;b[r](c);a.c[y].display="";a.d=!0;a.a?N(a):ca(a)}else da(a)})}
+function N(a){a.e={};O(a,!0);for(var b=a.a[A]();b.getFirstChild();)b.removeChild(b.getFirstChild());a.g++;for(var b=0,c;c=a.i[b];b++)google[C][L].removeListener(c);b={};c=P;for(var d=0,f;f=c[d];d++)b[f]=Q(a,f);for(c=0;d=b.Marker[c];c++)ea(a,d);for(c=0;d=b.Polygon[c];c++){var e=a,g=d,d=e.a;f=S(e,g);var h=d.createPolygon("");f[I](h);e=T(e,g);f[E](e);e=d.createLinearRing("");h.setOuterBoundary(e);for(var h=e[t](),g=g.getPath().getArray(),e=0,i=void 0;i=g[e];e++)h[w](i.lat(),i.lng(),0);d[A]()[r](f)}for(c=
+ 0;d=b.Polyline[c];c++){h=a;g=d;d=h.a;f=S(h,g);e=d.createLineString("");e.setTessellate(!0);f[I](e);h=T(h,g);f[E](h);h=e[t]();g=g.getPath().getArray();e=0;for(i=void 0;i=g[e];e++)h[w](i.lat(),i.lng(),0);d[A]()[r](f)}for(c=0;d=b.Rectangle[c];c++){var g=a,m=d,d=g.a,h=m.getBounds();f=h.getNorthEast();h=h.getSouthWest();e=S(g,m);e[I](d.createPolygon(""));i=d.createLinearRing("");g=T(g,m);e[E](g);g=i[t]();g[w](f.lat(),f.lng(),0);g[w](f.lat(),h.lng(),0);g[w](h.lat(),h.lng(),0);g[w](h.lat(),f.lng(),0);g[w](f.lat(),
+ f.lng(),0);e.getGeometry().setOuterBoundary(i);e.setName("placemark");d[A]()[r](e)}for(c=0;d=b.Circle[c];c++){i=a;m=d;d=i.a;f=m.getCenter();g=m.getRadius();h=S(i,m);h[I](d.createPolygon(""));e=d.createLinearRing("");i=T(i,m);h[E](i);for(i=0;i<25;i++){var R=f,v=g,m=14.4*i;v/=6378137;m*=l.PI/180;var G=R.lat()*(l.PI/180),R=R.lng()*(l.PI/180),X=l.cos(v),v=l.sin(v),Y=l.sin(G),G=l.cos(G),Z=X*Y+v*G*l.cos(m),m=new google[C].LatLng(l.asin(Z)/(l.PI/180),(R+l.atan2(v*G*l.sin(m),X-Y*Z))/(l.PI/180));e[t]()[w](m.lat(),
+ m.lng(),0)}h.getGeometry().setOuterBoundary(e);h.setName("placemark");d[A]()[r](h)}for(c=0;d=b.KmlLayer[c];c++)fa(a,d.getUrl());for(c=0;d=b.GroundOverlay[c];c++)g=d.getBounds(),f=g.getNorthEast(),g=g.getSouthWest(),h=a.a,e=h.createGroundOverlay(""),e.setLatLonBox(h.createLatLonBox("")),e.getLatLonBox().setBox(f.lat(),g.lat(),f.lng(),g.lng(),0),e.setIcon(h.createIcon("")),e.getIcon().setHref(d.getUrl()),h[A]()[r](e)}
+function O(a,b){var c=a.b.getCenter(),d=a.a.createLookAt("");d.setRange(l.pow(2,27-a.b.getZoom()));d.setLatitude(c.lat());d.setLongitude(c.lng());d.setHeading(0);d.setAltitude(0);var f=a.a;b?(f.getOptions().setFlyToSpeed(5),f[u]().setAbstractView(d),d.setTilt(15),f.getOptions().setFlyToSpeed(0.75),j[x](function(){f[u]().setAbstractView(d)},200),j[x](function(){f.getOptions().setFlyToSpeed(1)},250)):f[u]().setAbstractView(d)}
+function U(a,b){a[0]=="#"&&(a=a[J](1,9));typeof b=="undefined"?b="FF":(b=parseInt(parseFloat(b)*255,10).toString(16),b.length==1&&(b="0"+b));return[b,a[J](4,6),a[J](2,4),a[J](0,2)].join("")}function S(a,b){var c=a.g+"GEV3_"+b.__gme_id;a.e[c]=b;return a.a.createPlacemark(c)}function fa(a,b){var c=a.a;google[F].fetchKml(c,b,function(a){a?c[A]()[r](a):j[x](function(){alert("Bad or null KML.")},0)})}
+function ea(a,b){if(b[D]()){var c=a.a,d=S(a,b);b.getTitle()&&d.setName(b.getTitle());var f=c.createIcon("");b.getIcon()?f.setHref(b.getIcon()):f.setHref(a.l);var e=c.createStyle("");e.getIconStyle().setIcon(f);d[E](e);f=c.createPoint("");f.setLatitude(b[D]().lat());f.setLongitude(b[D]().lng());d[I](f);c[A]()[r](d);a.i.push(google[C][L][z](b,"position_changed",function(){var c=a.g+"GEV3_"+b.__gme_id,d=a.e[c],c=a.a.getElementById(c).getGeometry(),d=d[D]();c.setLatitude(d.lat());c.setLongitude(d.lng())}))}}
+function T(a,b){var c=a.a.createStyle(""),d=c.getPolyStyle(),f=c.getLineStyle();f.setWidth(V(b,"strokeWeight",3));var e=V(b,"strokeOpacity",1),g=V(b,"fillOpacity",0.3),h=V(b,"strokeColor","#000000"),i=V(b,"fillColor","#000000");f.getColor().set(U(h,e));d.getColor().set(U(i,g));return c}function V(a,b,c){a=a.get(b);return typeof a=="undefined"?c:a}
+function ca(a){google[F].createInstance(a.m,function(b){a.a=b;ga(a);N(a)},function(b){ha(a);a.b.setMapTypeId(google[C].MapTypeId.ROADMAP);throw"Google Earth API failed to initialize: "+b;})}
+function ga(a){var b=a.a;b.getWindow().setVisibility(!0);var c=b.getNavigationControl();c.setVisibility(b.VISIBILITY_AUTO);c=c.getScreenXY();c.setYUnits(b.UNITS_INSET_PIXELS);c.setXUnits(b.UNITS_PIXELS);c=b.getLayerRoot();c.enableLayerById(b.LAYER_BORDERS,!0);c.enableLayerById(b.LAYER_ROADS,!0);google[C][L][z](a.b,"GEInfoWindowOpened",function(b){if(a.d){var c=a.a.createHtmlStringBalloon("");c.setFeature(a.j);c.setContentString(b.getContent());a.a.setBalloon(c)}});google[F].addEventListener(b.getGlobe(),
+ "click",function(b){var c=b.getTarget(),e=a.e[c.getId()];if(e){b.preventDefault();for(var b=Q(a,"InfoWindow"),g=0,h;h=b[g];g++)h.close();a.j=c;google[C][L].trigger(e,"click")}})}function ia(a){var b=a.a[u]().copyAsLookAt(a.a.ALTITUDE_RELATIVE_TO_GROUND),c=b.getRange(),c=l.round(27-l.log(c)/l.log(2));!a.b.getZoom()==c?a.b.setZoom(c-1):a.b.setZoom(c);a.b.panTo(new google[C].LatLng(b.getLatitude(),b.getLongitude()))}function da(a){a.d&&(ia(a),j[x](function(){O(a)},50),j[x](function(){ha(a)},2200))}
+function ha(a){for(var b=a.c[K].childNodes,c=0,d;d=b[c];c++)q(d[y],d.__gme_ozi);a.h[K].removeChild(a.h);a.h=null;a.c[y].display="none";a.d=!1}
+function ba(a){var b=a.k,c=a.c=k[s]("DIV");o(c[y],"absolute");n(c[y],0);p(c[y],0);c.index=0;c[y].display="none";var d=a.n=k[s]("DIV");n(d[y],b.clientWidth+"px");p(d[y],b.clientHeight+"px");o(d[y],"absolute");c[r](d);b=a.m=k[s]("DIV");o(b[y],"absolute");n(b[y],"100%");p(b[y],"100%");d[r](b);a.b.controls[google[C].ControlPosition.TOP_LEFT].push(c);google[C][L][z](a.b,"resize",function(){var b=a.n[y],c=a.k;n(b,c.clientWidth+"px");p(b,c.clientHeight+"px")})}
+function Q(a,b){var c=[],d=W[b],f;for(f in d)if(d.hasOwnProperty(f)){var e=d[f];e.get("map")==a.b&&c.push(e)}return c}var W={};function ja(){var a=$,b=google[C][a][H];b.__gme_setMapOriginal=b.setMap;W[a]={};google[C][a][H].setMap=function(b){if(b){if(!this.__gme_id)this.__gme_id=ka++,W[a][this.__gme_id]=this}else delete W[a][this.__gme_id],this.__gme_id=void 0;this.__gme_setMapOriginal(b)}}
+for(var P="Marker,Polyline,Polygon,Rectangle,Circle,KmlLayer,GroundOverlay,InfoWindow".split(","),ka=0,la=P,ma=0,$;$=la[ma];ma++)if(ja(),$=="InfoWindow")google[C][B][H].p=google[C][B][H].open,W.InfoWindow={},google[C][B][H].open=function(a,b){if(a){if(!this.__gme_id)this.__gme_id=ka++,W[B][this.__gme_id]=this}else delete W[B][this.__gme_id],this.__gme_id=void 0;google[C][L].trigger(a,"GEInfoWindowOpened",this);this.p(a,b)};})();
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/markerclusterer.js b/www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/markerclusterer.js
new file mode 100644
index 00000000..e5379c62
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/markerclusterer.js
@@ -0,0 +1,1637 @@
+/*jslint browser: true, confusion: true, sloppy: true, vars: true, nomen: false, plusplus: false, indent: 2 */
+/*global window,google */
+
+/**
+ * @name MarkerClustererPlus for Google Maps V3
+ * @version 2.1.2 [May 28, 2014]
+ * @author Gary Little
+ * @fileoverview
+ * The library creates and manages per-zoom-level clusters for large amounts of markers.
+ * <p>
+ * This is an enhanced V3 implementation of the
+ * <a href="http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/"
+ * >V2 MarkerClusterer</a> by Xiaoxi Wu. It is based on the
+ * <a href="http://google-maps-utility-library-v3.googlecode.com/svn/tags/markerclusterer/"
+ * >V3 MarkerClusterer</a> port by Luke Mahe. MarkerClustererPlus was created by Gary Little.
+ * <p>
+ * v2.0 release: MarkerClustererPlus v2.0 is backward compatible with MarkerClusterer v1.0. It
+ * adds support for the <code>ignoreHidden</code>, <code>title</code>, <code>batchSizeIE</code>,
+ * and <code>calculator</code> properties as well as support for four more events. It also allows
+ * greater control over the styling of the text that appears on the cluster marker. The
+ * documentation has been significantly improved and the overall code has been simplified and
+ * polished. Very large numbers of markers can now be managed without causing Javascript timeout
+ * errors on Internet Explorer. Note that the name of the <code>clusterclick</code> event has been
+ * deprecated. The new name is <code>click</code>, so please change your application code now.
+ */
+
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * @name ClusterIconStyle
+ * @class This class represents the object for values in the <code>styles</code> array passed
+ * to the {@link MarkerClusterer} constructor. The element in this array that is used to
+ * style the cluster icon is determined by calling the <code>calculator</code> function.
+ *
+ * @property {string} url The URL of the cluster icon image file. Required.
+ * @property {number} height The display height (in pixels) of the cluster icon. Required.
+ * @property {number} width The display width (in pixels) of the cluster icon. Required.
+ * @property {Array} [anchorText] The position (in pixels) from the center of the cluster icon to
+ * where the text label is to be centered and drawn. The format is <code>[yoffset, xoffset]</code>
+ * where <code>yoffset</code> increases as you go down from center and <code>xoffset</code>
+ * increases to the right of center. The default is <code>[0, 0]</code>.
+ * @property {Array} [anchorIcon] The anchor position (in pixels) of the cluster icon. This is the
+ * spot on the cluster icon that is to be aligned with the cluster position. The format is
+ * <code>[yoffset, xoffset]</code> where <code>yoffset</code> increases as you go down and
+ * <code>xoffset</code> increases to the right of the top-left corner of the icon. The default
+ * anchor position is the center of the cluster icon.
+ * @property {string} [textColor="black"] The color of the label text shown on the
+ * cluster icon.
+ * @property {number} [textSize=11] The size (in pixels) of the label text shown on the
+ * cluster icon.
+ * @property {string} [textDecoration="none"] The value of the CSS <code>text-decoration</code>
+ * property for the label text shown on the cluster icon.
+ * @property {string} [fontWeight="bold"] The value of the CSS <code>font-weight</code>
+ * property for the label text shown on the cluster icon.
+ * @property {string} [fontStyle="normal"] The value of the CSS <code>font-style</code>
+ * property for the label text shown on the cluster icon.
+ * @property {string} [fontFamily="Arial,sans-serif"] The value of the CSS <code>font-family</code>
+ * property for the label text shown on the cluster icon.
+ * @property {string} [backgroundPosition="0 0"] The position of the cluster icon image
+ * within the image defined by <code>url</code>. The format is <code>"xpos ypos"</code>
+ * (the same format as for the CSS <code>background-position</code> property). You must set
+ * this property appropriately when the image defined by <code>url</code> represents a sprite
+ * containing multiple images. Note that the position <i>must</i> be specified in px units.
+ */
+/**
+ * @name ClusterIconInfo
+ * @class This class is an object containing general information about a cluster icon. This is
+ * the object that a <code>calculator</code> function returns.
+ *
+ * @property {string} text The text of the label to be shown on the cluster icon.
+ * @property {number} index The index plus 1 of the element in the <code>styles</code>
+ * array to be used to style the cluster icon.
+ * @property {string} title The tooltip to display when the mouse moves over the cluster icon.
+ * If this value is <code>undefined</code> or <code>""</code>, <code>title</code> is set to the
+ * value of the <code>title</code> property passed to the MarkerClusterer.
+ */
+/**
+ * A cluster icon.
+ *
+ * @constructor
+ * @extends google.maps.OverlayView
+ * @param {Cluster} cluster The cluster with which the icon is to be associated.
+ * @param {Array} [styles] An array of {@link ClusterIconStyle} defining the cluster icons
+ * to use for various cluster sizes.
+ * @private
+ */
+function ClusterIcon(cluster, styles) {
+ cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView);
+
+ this.cluster_ = cluster;
+ this.className_ = cluster.getMarkerClusterer().getClusterClass();
+ this.styles_ = styles;
+ this.center_ = null;
+ this.div_ = null;
+ this.sums_ = null;
+ this.visible_ = false;
+
+ this.setMap(cluster.getMap()); // Note: this causes onAdd to be called
+}
+
+
+/**
+ * Adds the icon to the DOM.
+ */
+ClusterIcon.prototype.onAdd = function () {
+ var cClusterIcon = this;
+ var cMouseDownInCluster;
+ var cDraggingMapByCluster;
+
+ this.div_ = document.createElement("div");
+ this.div_.className = this.className_;
+ if (this.visible_) {
+ this.show();
+ }
+
+ this.getPanes().overlayMouseTarget.appendChild(this.div_);
+
+ // Fix for Issue 157
+ this.boundsChangedListener_ = google.maps.event.addListener(this.getMap(), "bounds_changed", function () {
+ cDraggingMapByCluster = cMouseDownInCluster;
+ });
+
+ google.maps.event.addDomListener(this.div_, "mousedown", function () {
+ cMouseDownInCluster = true;
+ cDraggingMapByCluster = false;
+ });
+
+ google.maps.event.addDomListener(this.div_, "click", function (e) {
+ cMouseDownInCluster = false;
+ if (!cDraggingMapByCluster) {
+ var theBounds;
+ var mz;
+ var mc = cClusterIcon.cluster_.getMarkerClusterer();
+ /**
+ * This event is fired when a cluster marker is clicked.
+ * @name MarkerClusterer#click
+ * @param {Cluster} c The cluster that was clicked.
+ * @event
+ */
+ google.maps.event.trigger(mc, "click", cClusterIcon.cluster_);
+ google.maps.event.trigger(mc, "clusterclick", cClusterIcon.cluster_); // deprecated name
+
+ // The default click handler follows. Disable it by setting
+ // the zoomOnClick property to false.
+ if (mc.getZoomOnClick()) {
+ // Zoom into the cluster.
+ mz = mc.getMaxZoom();
+ theBounds = cClusterIcon.cluster_.getBounds();
+ mc.getMap().fitBounds(theBounds);
+ // There is a fix for Issue 170 here:
+ setTimeout(function () {
+ mc.getMap().fitBounds(theBounds);
+ // Don't zoom beyond the max zoom level
+ if (mz !== null && (mc.getMap().getZoom() > mz)) {
+ mc.getMap().setZoom(mz + 1);
+ }
+ }, 100);
+ }
+
+ // Prevent event propagation to the map:
+ e.cancelBubble = true;
+ if (e.stopPropagation) {
+ e.stopPropagation();
+ }
+ }
+ });
+
+ google.maps.event.addDomListener(this.div_, "mouseover", function () {
+ var mc = cClusterIcon.cluster_.getMarkerClusterer();
+ /**
+ * This event is fired when the mouse moves over a cluster marker.
+ * @name MarkerClusterer#mouseover
+ * @param {Cluster} c The cluster that the mouse moved over.
+ * @event
+ */
+ google.maps.event.trigger(mc, "mouseover", cClusterIcon.cluster_);
+ });
+
+ google.maps.event.addDomListener(this.div_, "mouseout", function () {
+ var mc = cClusterIcon.cluster_.getMarkerClusterer();
+ /**
+ * This event is fired when the mouse moves out of a cluster marker.
+ * @name MarkerClusterer#mouseout
+ * @param {Cluster} c The cluster that the mouse moved out of.
+ * @event
+ */
+ google.maps.event.trigger(mc, "mouseout", cClusterIcon.cluster_);
+ });
+};
+
+
+/**
+ * Removes the icon from the DOM.
+ */
+ClusterIcon.prototype.onRemove = function () {
+ if (this.div_ && this.div_.parentNode) {
+ this.hide();
+ google.maps.event.removeListener(this.boundsChangedListener_);
+ google.maps.event.clearInstanceListeners(this.div_);
+ this.div_.parentNode.removeChild(this.div_);
+ this.div_ = null;
+ }
+};
+
+
+/**
+ * Draws the icon.
+ */
+ClusterIcon.prototype.draw = function () {
+ if (this.visible_) {
+ var pos = this.getPosFromLatLng_(this.center_);
+ this.div_.style.top = pos.y + "px";
+ this.div_.style.left = pos.x + "px";
+ }
+};
+
+
+/**
+ * Hides the icon.
+ */
+ClusterIcon.prototype.hide = function () {
+ if (this.div_) {
+ this.div_.style.display = "none";
+ }
+ this.visible_ = false;
+};
+
+
+/**
+ * Positions and shows the icon.
+ */
+ClusterIcon.prototype.show = function () {
+ if (this.div_) {
+ var img = "";
+ // NOTE: values must be specified in px units
+ var bp = this.backgroundPosition_.split(" ");
+ var spriteH = parseInt(bp[0].replace(/^\s+|\s+$/g, ""), 10);
+ var spriteV = parseInt(bp[1].replace(/^\s+|\s+$/g, ""), 10);
+ var pos = this.getPosFromLatLng_(this.center_);
+ this.div_.style.cssText = this.createCss(pos);
+ img = "<img src='" + this.url_ + "' style='position: absolute; top: " + spriteV + "px; left: " + spriteH + "px; ";
+ if (!this.cluster_.getMarkerClusterer().enableRetinaIcons_) {
+ img += "clip: rect(" + (-1 * spriteV) + "px, " + ((-1 * spriteH) + this.width_) + "px, " +
+ ((-1 * spriteV) + this.height_) + "px, " + (-1 * spriteH) + "px);";
+ }
+ img += "'>";
+ this.div_.innerHTML = img + "<div style='" +
+ "position: absolute;" +
+ "top: " + this.anchorText_[0] + "px;" +
+ "left: " + this.anchorText_[1] + "px;" +
+ "color: " + this.textColor_ + ";" +
+ "font-size: " + this.textSize_ + "px;" +
+ "font-family: " + this.fontFamily_ + ";" +
+ "font-weight: " + this.fontWeight_ + ";" +
+ "font-style: " + this.fontStyle_ + ";" +
+ "text-decoration: " + this.textDecoration_ + ";" +
+ "text-align: center;" +
+ "width: " + this.width_ + "px;" +
+ "line-height:" + this.height_ + "px;" +
+ "'>" + this.sums_.text + "</div>";
+ if (typeof this.sums_.title === "undefined" || this.sums_.title === "") {
+ this.div_.title = this.cluster_.getMarkerClusterer().getTitle();
+ } else {
+ this.div_.title = this.sums_.title;
+ }
+ this.div_.style.display = "";
+ }
+ this.visible_ = true;
+};
+
+
+/**
+ * Sets the icon styles to the appropriate element in the styles array.
+ *
+ * @param {ClusterIconInfo} sums The icon label text and styles index.
+ */
+ClusterIcon.prototype.useStyle = function (sums) {
+ this.sums_ = sums;
+ var index = Math.max(0, sums.index - 1);
+ index = Math.min(this.styles_.length - 1, index);
+ var style = this.styles_[index];
+ this.url_ = style.url;
+ this.height_ = style.height;
+ this.width_ = style.width;
+ this.anchorText_ = style.anchorText || [0, 0];
+ this.anchorIcon_ = style.anchorIcon || [parseInt(this.height_ / 2, 10), parseInt(this.width_ / 2, 10)];
+ this.textColor_ = style.textColor || "black";
+ this.textSize_ = style.textSize || 11;
+ this.textDecoration_ = style.textDecoration || "none";
+ this.fontWeight_ = style.fontWeight || "bold";
+ this.fontStyle_ = style.fontStyle || "normal";
+ this.fontFamily_ = style.fontFamily || "Arial,sans-serif";
+ this.backgroundPosition_ = style.backgroundPosition || "0 0";
+};
+
+
+/**
+ * Sets the position at which to center the icon.
+ *
+ * @param {google.maps.LatLng} center The latlng to set as the center.
+ */
+ClusterIcon.prototype.setCenter = function (center) {
+ this.center_ = center;
+};
+
+
+/**
+ * Creates the cssText style parameter based on the position of the icon.
+ *
+ * @param {google.maps.Point} pos The position of the icon.
+ * @return {string} The CSS style text.
+ */
+ClusterIcon.prototype.createCss = function (pos) {
+ var style = [];
+ style.push("cursor: pointer;");
+ style.push("position: absolute; top: " + pos.y + "px; left: " + pos.x + "px;");
+ style.push("width: " + this.width_ + "px; height: " + this.height_ + "px;");
+ return style.join("");
+};
+
+
+/**
+ * Returns the position at which to place the DIV depending on the latlng.
+ *
+ * @param {google.maps.LatLng} latlng The position in latlng.
+ * @return {google.maps.Point} The position in pixels.
+ */
+ClusterIcon.prototype.getPosFromLatLng_ = function (latlng) {
+ var pos = this.getProjection().fromLatLngToDivPixel(latlng);
+ pos.x -= this.anchorIcon_[1];
+ pos.y -= this.anchorIcon_[0];
+ pos.x = parseInt(pos.x, 10);
+ pos.y = parseInt(pos.y, 10);
+ return pos;
+};
+
+
+/**
+ * Creates a single cluster that manages a group of proximate markers.
+ * Used internally, do not call this constructor directly.
+ * @constructor
+ * @param {MarkerClusterer} mc The <code>MarkerClusterer</code> object with which this
+ * cluster is associated.
+ */
+function Cluster(mc) {
+ this.markerClusterer_ = mc;
+ this.map_ = mc.getMap();
+ this.gridSize_ = mc.getGridSize();
+ this.minClusterSize_ = mc.getMinimumClusterSize();
+ this.averageCenter_ = mc.getAverageCenter();
+ this.markers_ = [];
+ this.center_ = null;
+ this.bounds_ = null;
+ this.clusterIcon_ = new ClusterIcon(this, mc.getStyles());
+}
+
+
+/**
+ * Returns the number of markers managed by the cluster. You can call this from
+ * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler
+ * for the <code>MarkerClusterer</code> object.
+ *
+ * @return {number} The number of markers in the cluster.
+ */
+Cluster.prototype.getSize = function () {
+ return this.markers_.length;
+};
+
+
+/**
+ * Returns the array of markers managed by the cluster. You can call this from
+ * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler
+ * for the <code>MarkerClusterer</code> object.
+ *
+ * @return {Array} The array of markers in the cluster.
+ */
+Cluster.prototype.getMarkers = function () {
+ return this.markers_;
+};
+
+
+/**
+ * Returns the center of the cluster. You can call this from
+ * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler
+ * for the <code>MarkerClusterer</code> object.
+ *
+ * @return {google.maps.LatLng} The center of the cluster.
+ */
+Cluster.prototype.getCenter = function () {
+ return this.center_;
+};
+
+
+/**
+ * Returns the map with which the cluster is associated.
+ *
+ * @return {google.maps.Map} The map.
+ * @ignore
+ */
+Cluster.prototype.getMap = function () {
+ return this.map_;
+};
+
+
+/**
+ * Returns the <code>MarkerClusterer</code> object with which the cluster is associated.
+ *
+ * @return {MarkerClusterer} The associated marker clusterer.
+ * @ignore
+ */
+Cluster.prototype.getMarkerClusterer = function () {
+ return this.markerClusterer_;
+};
+
+
+/**
+ * Returns the bounds of the cluster.
+ *
+ * @return {google.maps.LatLngBounds} the cluster bounds.
+ * @ignore
+ */
+Cluster.prototype.getBounds = function () {
+ var i;
+ var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
+ var markers = this.getMarkers();
+ for (i = 0; i < markers.length; i++) {
+ bounds.extend(markers[i].getPosition());
+ }
+ return bounds;
+};
+
+
+/**
+ * Removes the cluster from the map.
+ *
+ * @ignore
+ */
+Cluster.prototype.remove = function () {
+ this.clusterIcon_.setMap(null);
+ this.markers_ = [];
+ delete this.markers_;
+};
+
+
+/**
+ * Adds a marker to the cluster.
+ *
+ * @param {google.maps.Marker} marker The marker to be added.
+ * @return {boolean} True if the marker was added.
+ * @ignore
+ */
+Cluster.prototype.addMarker = function (marker) {
+ var i;
+ var mCount;
+ var mz;
+
+ if (this.isMarkerAlreadyAdded_(marker)) {
+ return false;
+ }
+
+ if (!this.center_) {
+ this.center_ = marker.getPosition();
+ this.calculateBounds_();
+ } else {
+ if (this.averageCenter_) {
+ var l = this.markers_.length + 1;
+ var lat = (this.center_.lat() * (l - 1) + marker.getPosition().lat()) / l;
+ var lng = (this.center_.lng() * (l - 1) + marker.getPosition().lng()) / l;
+ this.center_ = new google.maps.LatLng(lat, lng);
+ this.calculateBounds_();
+ }
+ }
+
+ marker.isAdded = true;
+ this.markers_.push(marker);
+
+ mCount = this.markers_.length;
+ mz = this.markerClusterer_.getMaxZoom();
+ if (mz !== null && this.map_.getZoom() > mz) {
+ // Zoomed in past max zoom, so show the marker.
+ if (marker.getMap() !== this.map_) {
+ marker.setMap(this.map_);
+ }
+ } else if (mCount < this.minClusterSize_) {
+ // Min cluster size not reached so show the marker.
+ if (marker.getMap() !== this.map_) {
+ marker.setMap(this.map_);
+ }
+ } else if (mCount === this.minClusterSize_) {
+ // Hide the markers that were showing.
+ for (i = 0; i < mCount; i++) {
+ this.markers_[i].setMap(null);
+ }
+ } else {
+ marker.setMap(null);
+ }
+
+ this.updateIcon_();
+ return true;
+};
+
+
+/**
+ * Determines if a marker lies within the cluster's bounds.
+ *
+ * @param {google.maps.Marker} marker The marker to check.
+ * @return {boolean} True if the marker lies in the bounds.
+ * @ignore
+ */
+Cluster.prototype.isMarkerInClusterBounds = function (marker) {
+ return this.bounds_.contains(marker.getPosition());
+};
+
+
+/**
+ * Calculates the extended bounds of the cluster with the grid.
+ */
+Cluster.prototype.calculateBounds_ = function () {
+ var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
+ this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds);
+};
+
+
+/**
+ * Updates the cluster icon.
+ */
+Cluster.prototype.updateIcon_ = function () {
+ var mCount = this.markers_.length;
+ var mz = this.markerClusterer_.getMaxZoom();
+
+ if (mz !== null && this.map_.getZoom() > mz) {
+ this.clusterIcon_.hide();
+ return;
+ }
+
+ if (mCount < this.minClusterSize_) {
+ // Min cluster size not yet reached.
+ this.clusterIcon_.hide();
+ return;
+ }
+
+ var numStyles = this.markerClusterer_.getStyles().length;
+ var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles);
+ this.clusterIcon_.setCenter(this.center_);
+ this.clusterIcon_.useStyle(sums);
+ this.clusterIcon_.show();
+};
+
+
+/**
+ * Determines if a marker has already been added to the cluster.
+ *
+ * @param {google.maps.Marker} marker The marker to check.
+ * @return {boolean} True if the marker has already been added.
+ */
+Cluster.prototype.isMarkerAlreadyAdded_ = function (marker) {
+ var i;
+ if (this.markers_.indexOf) {
+ return this.markers_.indexOf(marker) !== -1;
+ } else {
+ for (i = 0; i < this.markers_.length; i++) {
+ if (marker === this.markers_[i]) {
+ return true;
+ }
+ }
+ }
+ return false;
+};
+
+
+/**
+ * @name MarkerClustererOptions
+ * @class This class represents the optional parameter passed to
+ * the {@link MarkerClusterer} constructor.
+ * @property {number} [gridSize=60] The grid size of a cluster in pixels. The grid is a square.
+ * @property {number} [maxZoom=null] The maximum zoom level at which clustering is enabled or
+ * <code>null</code> if clustering is to be enabled at all zoom levels.
+ * @property {boolean} [zoomOnClick=true] Whether to zoom the map when a cluster marker is
+ * clicked. You may want to set this to <code>false</code> if you have installed a handler
+ * for the <code>click</code> event and it deals with zooming on its own.
+ * @property {boolean} [averageCenter=false] Whether the position of a cluster marker should be
+ * the average position of all markers in the cluster. If set to <code>false</code>, the
+ * cluster marker is positioned at the location of the first marker added to the cluster.
+ * @property {number} [minimumClusterSize=2] The minimum number of markers needed in a cluster
+ * before the markers are hidden and a cluster marker appears.
+ * @property {boolean} [ignoreHidden=false] Whether to ignore hidden markers in clusters. You
+ * may want to set this to <code>true</code> to ensure that hidden markers are not included
+ * in the marker count that appears on a cluster marker (this count is the value of the
+ * <code>text</code> property of the result returned by the default <code>calculator</code>).
+ * If set to <code>true</code> and you change the visibility of a marker being clustered, be
+ * sure to also call <code>MarkerClusterer.repaint()</code>.
+ * @property {string} [title=""] The tooltip to display when the mouse moves over a cluster
+ * marker. (Alternatively, you can use a custom <code>calculator</code> function to specify a
+ * different tooltip for each cluster marker.)
+ * @property {function} [calculator=MarkerClusterer.CALCULATOR] The function used to determine
+ * the text to be displayed on a cluster marker and the index indicating which style to use
+ * for the cluster marker. The input parameters for the function are (1) the array of markers
+ * represented by a cluster marker and (2) the number of cluster icon styles. It returns a
+ * {@link ClusterIconInfo} object. The default <code>calculator</code> returns a
+ * <code>text</code> property which is the number of markers in the cluster and an
+ * <code>index</code> property which is one higher than the lowest integer such that
+ * <code>10^i</code> exceeds the number of markers in the cluster, or the size of the styles
+ * array, whichever is less. The <code>styles</code> array element used has an index of
+ * <code>index</code> minus 1. For example, the default <code>calculator</code> returns a
+ * <code>text</code> value of <code>"125"</code> and an <code>index</code> of <code>3</code>
+ * for a cluster icon representing 125 markers so the element used in the <code>styles</code>
+ * array is <code>2</code>. A <code>calculator</code> may also return a <code>title</code>
+ * property that contains the text of the tooltip to be used for the cluster marker. If
+ * <code>title</code> is not defined, the tooltip is set to the value of the <code>title</code>
+ * property for the MarkerClusterer.
+ * @property {string} [clusterClass="cluster"] The name of the CSS class defining general styles
+ * for the cluster markers. Use this class to define CSS styles that are not set up by the code
+ * that processes the <code>styles</code> array.
+ * @property {Array} [styles] An array of {@link ClusterIconStyle} elements defining the styles
+ * of the cluster markers to be used. The element to be used to style a given cluster marker
+ * is determined by the function defined by the <code>calculator</code> property.
+ * The default is an array of {@link ClusterIconStyle} elements whose properties are derived
+ * from the values for <code>imagePath</code>, <code>imageExtension</code>, and
+ * <code>imageSizes</code>.
+ * @property {boolean} [enableRetinaIcons=false] Whether to allow the use of cluster icons that
+ * have sizes that are some multiple (typically double) of their actual display size. Icons such
+ * as these look better when viewed on high-resolution monitors such as Apple's Retina displays.
+ * Note: if this property is <code>true</code>, sprites cannot be used as cluster icons.
+ * @property {number} [batchSize=MarkerClusterer.BATCH_SIZE] Set this property to the
+ * number of markers to be processed in a single batch when using a browser other than
+ * Internet Explorer (for Internet Explorer, use the batchSizeIE property instead).
+ * @property {number} [batchSizeIE=MarkerClusterer.BATCH_SIZE_IE] When Internet Explorer is
+ * being used, markers are processed in several batches with a small delay inserted between
+ * each batch in an attempt to avoid Javascript timeout errors. Set this property to the
+ * number of markers to be processed in a single batch; select as high a number as you can
+ * without causing a timeout error in the browser. This number might need to be as low as 100
+ * if 15,000 markers are being managed, for example.
+ * @property {string} [imagePath=MarkerClusterer.IMAGE_PATH]
+ * The full URL of the root name of the group of image files to use for cluster icons.
+ * The complete file name is of the form <code>imagePath</code>n.<code>imageExtension</code>
+ * where n is the image file number (1, 2, etc.).
+ * @property {string} [imageExtension=MarkerClusterer.IMAGE_EXTENSION]
+ * The extension name for the cluster icon image files (e.g., <code>"png"</code> or
+ * <code>"jpg"</code>).
+ * @property {Array} [imageSizes=MarkerClusterer.IMAGE_SIZES]
+ * An array of numbers containing the widths of the group of
+ * <code>imagePath</code>n.<code>imageExtension</code> image files.
+ * (The images are assumed to be square.)
+ */
+/**
+ * Creates a MarkerClusterer object with the options specified in {@link MarkerClustererOptions}.
+ * @constructor
+ * @extends google.maps.OverlayView
+ * @param {google.maps.Map} map The Google map to attach to.
+ * @param {Array.<google.maps.Marker>} [opt_markers] The markers to be added to the cluster.
+ * @param {MarkerClustererOptions} [opt_options] The optional parameters.
+ */
+function MarkerClusterer(map, opt_markers, opt_options) {
+ // MarkerClusterer implements google.maps.OverlayView interface. We use the
+ // extend function to extend MarkerClusterer with google.maps.OverlayView
+ // because it might not always be available when the code is defined so we
+ // look for it at the last possible moment. If it doesn't exist now then
+ // there is no point going ahead :)
+ this.extend(MarkerClusterer, google.maps.OverlayView);
+
+ opt_markers = opt_markers || [];
+ opt_options = opt_options || {};
+
+ this.markers_ = [];
+ this.clusters_ = [];
+ this.listeners_ = [];
+ this.activeMap_ = null;
+ this.ready_ = false;
+
+ this.gridSize_ = opt_options.gridSize || 60;
+ this.minClusterSize_ = opt_options.minimumClusterSize || 2;
+ this.maxZoom_ = opt_options.maxZoom || null;
+ this.styles_ = opt_options.styles || [];
+ this.title_ = opt_options.title || "";
+ this.zoomOnClick_ = true;
+ if (opt_options.zoomOnClick !== undefined) {
+ this.zoomOnClick_ = opt_options.zoomOnClick;
+ }
+ this.averageCenter_ = false;
+ if (opt_options.averageCenter !== undefined) {
+ this.averageCenter_ = opt_options.averageCenter;
+ }
+ this.ignoreHidden_ = false;
+ if (opt_options.ignoreHidden !== undefined) {
+ this.ignoreHidden_ = opt_options.ignoreHidden;
+ }
+ this.enableRetinaIcons_ = false;
+ if (opt_options.enableRetinaIcons !== undefined) {
+ this.enableRetinaIcons_ = opt_options.enableRetinaIcons;
+ }
+ this.imagePath_ = opt_options.imagePath || MarkerClusterer.IMAGE_PATH;
+ this.imageExtension_ = opt_options.imageExtension || MarkerClusterer.IMAGE_EXTENSION;
+ this.imageSizes_ = opt_options.imageSizes || MarkerClusterer.IMAGE_SIZES;
+ this.calculator_ = opt_options.calculator || MarkerClusterer.CALCULATOR;
+ this.batchSize_ = opt_options.batchSize || MarkerClusterer.BATCH_SIZE;
+ this.batchSizeIE_ = opt_options.batchSizeIE || MarkerClusterer.BATCH_SIZE_IE;
+ this.clusterClass_ = opt_options.clusterClass || "cluster";
+
+ if (navigator.userAgent.toLowerCase().indexOf("msie") !== -1) {
+ // Try to avoid IE timeout when processing a huge number of markers:
+ this.batchSize_ = this.batchSizeIE_;
+ }
+
+ this.setupStyles_();
+
+ this.addMarkers(opt_markers, true);
+ this.setMap(map); // Note: this causes onAdd to be called
+}
+
+
+/**
+ * Implementation of the onAdd interface method.
+ * @ignore
+ */
+MarkerClusterer.prototype.onAdd = function () {
+ var cMarkerClusterer = this;
+
+ this.activeMap_ = this.getMap();
+ this.ready_ = true;
+
+ this.repaint();
+
+ // Add the map event listeners
+ this.listeners_ = [
+ google.maps.event.addListener(this.getMap(), "zoom_changed", function () {
+ cMarkerClusterer.resetViewport_(false);
+ // Workaround for this Google bug: when map is at level 0 and "-" of
+ // zoom slider is clicked, a "zoom_changed" event is fired even though
+ // the map doesn't zoom out any further. In this situation, no "idle"
+ // event is triggered so the cluster markers that have been removed
+ // do not get redrawn. Same goes for a zoom in at maxZoom.
+ if (this.getZoom() === (this.get("minZoom") || 0) || this.getZoom() === this.get("maxZoom")) {
+ google.maps.event.trigger(this, "idle");
+ }
+ }),
+ google.maps.event.addListener(this.getMap(), "idle", function () {
+ cMarkerClusterer.redraw_();
+ })
+ ];
+};
+
+
+/**
+ * Implementation of the onRemove interface method.
+ * Removes map event listeners and all cluster icons from the DOM.
+ * All managed markers are also put back on the map.
+ * @ignore
+ */
+MarkerClusterer.prototype.onRemove = function () {
+ var i;
+
+ // Put all the managed markers back on the map:
+ for (i = 0; i < this.markers_.length; i++) {
+ if (this.markers_[i].getMap() !== this.activeMap_) {
+ this.markers_[i].setMap(this.activeMap_);
+ }
+ }
+
+ // Remove all clusters:
+ for (i = 0; i < this.clusters_.length; i++) {
+ this.clusters_[i].remove();
+ }
+ this.clusters_ = [];
+
+ // Remove map event listeners:
+ for (i = 0; i < this.listeners_.length; i++) {
+ google.maps.event.removeListener(this.listeners_[i]);
+ }
+ this.listeners_ = [];
+
+ this.activeMap_ = null;
+ this.ready_ = false;
+};
+
+
+/**
+ * Implementation of the draw interface method.
+ * @ignore
+ */
+MarkerClusterer.prototype.draw = function () {};
+
+
+/**
+ * Sets up the styles object.
+ */
+MarkerClusterer.prototype.setupStyles_ = function () {
+ var i, size;
+ if (this.styles_.length > 0) {
+ return;
+ }
+
+ for (i = 0; i < this.imageSizes_.length; i++) {
+ size = this.imageSizes_[i];
+ this.styles_.push({
+ url: this.imagePath_ + (i + 1) + "." + this.imageExtension_,
+ height: size,
+ width: size
+ });
+ }
+};
+
+
+/**
+ * Fits the map to the bounds of the markers managed by the clusterer.
+ */
+MarkerClusterer.prototype.fitMapToMarkers = function () {
+ var i;
+ var markers = this.getMarkers();
+ var bounds = new google.maps.LatLngBounds();
+ for (i = 0; i < markers.length; i++) {
+ bounds.extend(markers[i].getPosition());
+ }
+
+ this.getMap().fitBounds(bounds);
+};
+
+
+/**
+ * Returns the value of the <code>gridSize</code> property.
+ *
+ * @return {number} The grid size.
+ */
+MarkerClusterer.prototype.getGridSize = function () {
+ return this.gridSize_;
+};
+
+
+/**
+ * Sets the value of the <code>gridSize</code> property.
+ *
+ * @param {number} gridSize The grid size.
+ */
+MarkerClusterer.prototype.setGridSize = function (gridSize) {
+ this.gridSize_ = gridSize;
+};
+
+
+/**
+ * Returns the value of the <code>minimumClusterSize</code> property.
+ *
+ * @return {number} The minimum cluster size.
+ */
+MarkerClusterer.prototype.getMinimumClusterSize = function () {
+ return this.minClusterSize_;
+};
+
+/**
+ * Sets the value of the <code>minimumClusterSize</code> property.
+ *
+ * @param {number} minimumClusterSize The minimum cluster size.
+ */
+MarkerClusterer.prototype.setMinimumClusterSize = function (minimumClusterSize) {
+ this.minClusterSize_ = minimumClusterSize;
+};
+
+
+/**
+ * Returns the value of the <code>maxZoom</code> property.
+ *
+ * @return {number} The maximum zoom level.
+ */
+MarkerClusterer.prototype.getMaxZoom = function () {
+ return this.maxZoom_;
+};
+
+
+/**
+ * Sets the value of the <code>maxZoom</code> property.
+ *
+ * @param {number} maxZoom The maximum zoom level.
+ */
+MarkerClusterer.prototype.setMaxZoom = function (maxZoom) {
+ this.maxZoom_ = maxZoom;
+};
+
+
+/**
+ * Returns the value of the <code>styles</code> property.
+ *
+ * @return {Array} The array of styles defining the cluster markers to be used.
+ */
+MarkerClusterer.prototype.getStyles = function () {
+ return this.styles_;
+};
+
+
+/**
+ * Sets the value of the <code>styles</code> property.
+ *
+ * @param {Array.<ClusterIconStyle>} styles The array of styles to use.
+ */
+MarkerClusterer.prototype.setStyles = function (styles) {
+ this.styles_ = styles;
+};
+
+
+/**
+ * Returns the value of the <code>title</code> property.
+ *
+ * @return {string} The content of the title text.
+ */
+MarkerClusterer.prototype.getTitle = function () {
+ return this.title_;
+};
+
+
+/**
+ * Sets the value of the <code>title</code> property.
+ *
+ * @param {string} title The value of the title property.
+ */
+MarkerClusterer.prototype.setTitle = function (title) {
+ this.title_ = title;
+};
+
+
+/**
+ * Returns the value of the <code>zoomOnClick</code> property.
+ *
+ * @return {boolean} True if zoomOnClick property is set.
+ */
+MarkerClusterer.prototype.getZoomOnClick = function () {
+ return this.zoomOnClick_;
+};
+
+
+/**
+ * Sets the value of the <code>zoomOnClick</code> property.
+ *
+ * @param {boolean} zoomOnClick The value of the zoomOnClick property.
+ */
+MarkerClusterer.prototype.setZoomOnClick = function (zoomOnClick) {
+ this.zoomOnClick_ = zoomOnClick;
+};
+
+
+/**
+ * Returns the value of the <code>averageCenter</code> property.
+ *
+ * @return {boolean} True if averageCenter property is set.
+ */
+MarkerClusterer.prototype.getAverageCenter = function () {
+ return this.averageCenter_;
+};
+
+
+/**
+ * Sets the value of the <code>averageCenter</code> property.
+ *
+ * @param {boolean} averageCenter The value of the averageCenter property.
+ */
+MarkerClusterer.prototype.setAverageCenter = function (averageCenter) {
+ this.averageCenter_ = averageCenter;
+};
+
+
+/**
+ * Returns the value of the <code>ignoreHidden</code> property.
+ *
+ * @return {boolean} True if ignoreHidden property is set.
+ */
+MarkerClusterer.prototype.getIgnoreHidden = function () {
+ return this.ignoreHidden_;
+};
+
+
+/**
+ * Sets the value of the <code>ignoreHidden</code> property.
+ *
+ * @param {boolean} ignoreHidden The value of the ignoreHidden property.
+ */
+MarkerClusterer.prototype.setIgnoreHidden = function (ignoreHidden) {
+ this.ignoreHidden_ = ignoreHidden;
+};
+
+
+/**
+ * Returns the value of the <code>enableRetinaIcons</code> property.
+ *
+ * @return {boolean} True if enableRetinaIcons property is set.
+ */
+MarkerClusterer.prototype.getEnableRetinaIcons = function () {
+ return this.enableRetinaIcons_;
+};
+
+
+/**
+ * Sets the value of the <code>enableRetinaIcons</code> property.
+ *
+ * @param {boolean} enableRetinaIcons The value of the enableRetinaIcons property.
+ */
+MarkerClusterer.prototype.setEnableRetinaIcons = function (enableRetinaIcons) {
+ this.enableRetinaIcons_ = enableRetinaIcons;
+};
+
+
+/**
+ * Returns the value of the <code>imageExtension</code> property.
+ *
+ * @return {string} The value of the imageExtension property.
+ */
+MarkerClusterer.prototype.getImageExtension = function () {
+ return this.imageExtension_;
+};
+
+
+/**
+ * Sets the value of the <code>imageExtension</code> property.
+ *
+ * @param {string} imageExtension The value of the imageExtension property.
+ */
+MarkerClusterer.prototype.setImageExtension = function (imageExtension) {
+ this.imageExtension_ = imageExtension;
+};
+
+
+/**
+ * Returns the value of the <code>imagePath</code> property.
+ *
+ * @return {string} The value of the imagePath property.
+ */
+MarkerClusterer.prototype.getImagePath = function () {
+ return this.imagePath_;
+};
+
+
+/**
+ * Sets the value of the <code>imagePath</code> property.
+ *
+ * @param {string} imagePath The value of the imagePath property.
+ */
+MarkerClusterer.prototype.setImagePath = function (imagePath) {
+ this.imagePath_ = imagePath;
+};
+
+
+/**
+ * Returns the value of the <code>imageSizes</code> property.
+ *
+ * @return {Array} The value of the imageSizes property.
+ */
+MarkerClusterer.prototype.getImageSizes = function () {
+ return this.imageSizes_;
+};
+
+
+/**
+ * Sets the value of the <code>imageSizes</code> property.
+ *
+ * @param {Array} imageSizes The value of the imageSizes property.
+ */
+MarkerClusterer.prototype.setImageSizes = function (imageSizes) {
+ this.imageSizes_ = imageSizes;
+};
+
+
+/**
+ * Returns the value of the <code>calculator</code> property.
+ *
+ * @return {function} the value of the calculator property.
+ */
+MarkerClusterer.prototype.getCalculator = function () {
+ return this.calculator_;
+};
+
+
+/**
+ * Sets the value of the <code>calculator</code> property.
+ *
+ * @param {function(Array.<google.maps.Marker>, number)} calculator The value
+ * of the calculator property.
+ */
+MarkerClusterer.prototype.setCalculator = function (calculator) {
+ this.calculator_ = calculator;
+};
+
+
+/**
+ * Returns the value of the <code>batchSizeIE</code> property.
+ *
+ * @return {number} the value of the batchSizeIE property.
+ */
+MarkerClusterer.prototype.getBatchSizeIE = function () {
+ return this.batchSizeIE_;
+};
+
+
+/**
+ * Sets the value of the <code>batchSizeIE</code> property.
+ *
+ * @param {number} batchSizeIE The value of the batchSizeIE property.
+ */
+MarkerClusterer.prototype.setBatchSizeIE = function (batchSizeIE) {
+ this.batchSizeIE_ = batchSizeIE;
+};
+
+
+/**
+ * Returns the value of the <code>clusterClass</code> property.
+ *
+ * @return {string} the value of the clusterClass property.
+ */
+MarkerClusterer.prototype.getClusterClass = function () {
+ return this.clusterClass_;
+};
+
+
+/**
+ * Sets the value of the <code>clusterClass</code> property.
+ *
+ * @param {string} clusterClass The value of the clusterClass property.
+ */
+MarkerClusterer.prototype.setClusterClass = function (clusterClass) {
+ this.clusterClass_ = clusterClass;
+};
+
+
+/**
+ * Returns the array of markers managed by the clusterer.
+ *
+ * @return {Array} The array of markers managed by the clusterer.
+ */
+MarkerClusterer.prototype.getMarkers = function () {
+ return this.markers_;
+};
+
+
+/**
+ * Returns the number of markers managed by the clusterer.
+ *
+ * @return {number} The number of markers.
+ */
+MarkerClusterer.prototype.getTotalMarkers = function () {
+ return this.markers_.length;
+};
+
+
+/**
+ * Returns the current array of clusters formed by the clusterer.
+ *
+ * @return {Array} The array of clusters formed by the clusterer.
+ */
+MarkerClusterer.prototype.getClusters = function () {
+ return this.clusters_;
+};
+
+
+/**
+ * Returns the number of clusters formed by the clusterer.
+ *
+ * @return {number} The number of clusters formed by the clusterer.
+ */
+MarkerClusterer.prototype.getTotalClusters = function () {
+ return this.clusters_.length;
+};
+
+
+/**
+ * Adds a marker to the clusterer. The clusters are redrawn unless
+ * <code>opt_nodraw</code> is set to <code>true</code>.
+ *
+ * @param {google.maps.Marker} marker The marker to add.
+ * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing.
+ */
+MarkerClusterer.prototype.addMarker = function (marker, opt_nodraw) {
+ this.pushMarkerTo_(marker);
+ if (!opt_nodraw) {
+ this.redraw_();
+ }
+};
+
+
+/**
+ * Adds an array of markers to the clusterer. The clusters are redrawn unless
+ * <code>opt_nodraw</code> is set to <code>true</code>.
+ *
+ * @param {Array.<google.maps.Marker>} markers The markers to add.
+ * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing.
+ */
+MarkerClusterer.prototype.addMarkers = function (markers, opt_nodraw) {
+ var key;
+ for (key in markers) {
+ if (markers.hasOwnProperty(key)) {
+ this.pushMarkerTo_(markers[key]);
+ }
+ }
+ if (!opt_nodraw) {
+ this.redraw_();
+ }
+};
+
+
+/**
+ * Pushes a marker to the clusterer.
+ *
+ * @param {google.maps.Marker} marker The marker to add.
+ */
+MarkerClusterer.prototype.pushMarkerTo_ = function (marker) {
+ // If the marker is draggable add a listener so we can update the clusters on the dragend:
+ if (marker.getDraggable()) {
+ var cMarkerClusterer = this;
+ google.maps.event.addListener(marker, "dragend", function () {
+ if (cMarkerClusterer.ready_) {
+ this.isAdded = false;
+ cMarkerClusterer.repaint();
+ }
+ });
+ }
+ marker.isAdded = false;
+ this.markers_.push(marker);
+};
+
+
+/**
+ * Removes a marker from the cluster. The clusters are redrawn unless
+ * <code>opt_nodraw</code> is set to <code>true</code>. Returns <code>true</code> if the
+ * marker was removed from the clusterer.
+ *
+ * @param {google.maps.Marker} marker The marker to remove.
+ * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing.
+ * @return {boolean} True if the marker was removed from the clusterer.
+ */
+MarkerClusterer.prototype.removeMarker = function (marker, opt_nodraw) {
+ var removed = this.removeMarker_(marker);
+
+ if (!opt_nodraw && removed) {
+ this.repaint();
+ }
+
+ return removed;
+};
+
+
+/**
+ * Removes an array of markers from the cluster. The clusters are redrawn unless
+ * <code>opt_nodraw</code> is set to <code>true</code>. Returns <code>true</code> if markers
+ * were removed from the clusterer.
+ *
+ * @param {Array.<google.maps.Marker>} markers The markers to remove.
+ * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing.
+ * @return {boolean} True if markers were removed from the clusterer.
+ */
+MarkerClusterer.prototype.removeMarkers = function (markers, opt_nodraw) {
+ var i, r;
+ var removed = false;
+
+ for (i = 0; i < markers.length; i++) {
+ r = this.removeMarker_(markers[i]);
+ removed = removed || r;
+ }
+
+ if (!opt_nodraw && removed) {
+ this.repaint();
+ }
+
+ return removed;
+};
+
+
+/**
+ * Removes a marker and returns true if removed, false if not.
+ *
+ * @param {google.maps.Marker} marker The marker to remove
+ * @return {boolean} Whether the marker was removed or not
+ */
+MarkerClusterer.prototype.removeMarker_ = function (marker) {
+ var i;
+ var index = -1;
+ if (this.markers_.indexOf) {
+ index = this.markers_.indexOf(marker);
+ } else {
+ for (i = 0; i < this.markers_.length; i++) {
+ if (marker === this.markers_[i]) {
+ index = i;
+ break;
+ }
+ }
+ }
+
+ if (index === -1) {
+ // Marker is not in our list of markers, so do nothing:
+ return false;
+ }
+
+ marker.setMap(null);
+ this.markers_.splice(index, 1); // Remove the marker from the list of managed markers
+ return true;
+};
+
+
+/**
+ * Removes all clusters and markers from the map and also removes all markers
+ * managed by the clusterer.
+ */
+MarkerClusterer.prototype.clearMarkers = function () {
+ this.resetViewport_(true);
+ this.markers_ = [];
+};
+
+
+/**
+ * Recalculates and redraws all the marker clusters from scratch.
+ * Call this after changing any properties.
+ */
+MarkerClusterer.prototype.repaint = function () {
+ var oldClusters = this.clusters_.slice();
+ this.clusters_ = [];
+ this.resetViewport_(false);
+ this.redraw_();
+
+ // Remove the old clusters.
+ // Do it in a timeout to prevent blinking effect.
+ setTimeout(function () {
+ var i;
+ for (i = 0; i < oldClusters.length; i++) {
+ oldClusters[i].remove();
+ }
+ }, 0);
+};
+
+
+/**
+ * Returns the current bounds extended by the grid size.
+ *
+ * @param {google.maps.LatLngBounds} bounds The bounds to extend.
+ * @return {google.maps.LatLngBounds} The extended bounds.
+ * @ignore
+ */
+MarkerClusterer.prototype.getExtendedBounds = function (bounds) {
+ var projection = this.getProjection();
+
+ // Turn the bounds into latlng.
+ var tr = new google.maps.LatLng(bounds.getNorthEast().lat(),
+ bounds.getNorthEast().lng());
+ var bl = new google.maps.LatLng(bounds.getSouthWest().lat(),
+ bounds.getSouthWest().lng());
+
+ // Convert the points to pixels and the extend out by the grid size.
+ var trPix = projection.fromLatLngToDivPixel(tr);
+ trPix.x += this.gridSize_;
+ trPix.y -= this.gridSize_;
+
+ var blPix = projection.fromLatLngToDivPixel(bl);
+ blPix.x -= this.gridSize_;
+ blPix.y += this.gridSize_;
+
+ // Convert the pixel points back to LatLng
+ var ne = projection.fromDivPixelToLatLng(trPix);
+ var sw = projection.fromDivPixelToLatLng(blPix);
+
+ // Extend the bounds to contain the new bounds.
+ bounds.extend(ne);
+ bounds.extend(sw);
+
+ return bounds;
+};
+
+
+/**
+ * Redraws all the clusters.
+ */
+MarkerClusterer.prototype.redraw_ = function () {
+ this.createClusters_(0);
+};
+
+
+/**
+ * Removes all clusters from the map. The markers are also removed from the map
+ * if <code>opt_hide</code> is set to <code>true</code>.
+ *
+ * @param {boolean} [opt_hide] Set to <code>true</code> to also remove the markers
+ * from the map.
+ */
+MarkerClusterer.prototype.resetViewport_ = function (opt_hide) {
+ var i, marker;
+ // Remove all the clusters
+ for (i = 0; i < this.clusters_.length; i++) {
+ this.clusters_[i].remove();
+ }
+ this.clusters_ = [];
+
+ // Reset the markers to not be added and to be removed from the map.
+ for (i = 0; i < this.markers_.length; i++) {
+ marker = this.markers_[i];
+ marker.isAdded = false;
+ if (opt_hide) {
+ marker.setMap(null);
+ }
+ }
+};
+
+
+/**
+ * Calculates the distance between two latlng locations in km.
+ *
+ * @param {google.maps.LatLng} p1 The first lat lng point.
+ * @param {google.maps.LatLng} p2 The second lat lng point.
+ * @return {number} The distance between the two points in km.
+ * @see http://www.movable-type.co.uk/scripts/latlong.html
+ */
+MarkerClusterer.prototype.distanceBetweenPoints_ = function (p1, p2) {
+ var R = 6371; // Radius of the Earth in km
+ var dLat = (p2.lat() - p1.lat()) * Math.PI / 180;
+ var dLon = (p2.lng() - p1.lng()) * Math.PI / 180;
+ var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
+ Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) *
+ Math.sin(dLon / 2) * Math.sin(dLon / 2);
+ var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+ var d = R * c;
+ return d;
+};
+
+
+/**
+ * Determines if a marker is contained in a bounds.
+ *
+ * @param {google.maps.Marker} marker The marker to check.
+ * @param {google.maps.LatLngBounds} bounds The bounds to check against.
+ * @return {boolean} True if the marker is in the bounds.
+ */
+MarkerClusterer.prototype.isMarkerInBounds_ = function (marker, bounds) {
+ return bounds.contains(marker.getPosition());
+};
+
+
+/**
+ * Adds a marker to a cluster, or creates a new cluster.
+ *
+ * @param {google.maps.Marker} marker The marker to add.
+ */
+MarkerClusterer.prototype.addToClosestCluster_ = function (marker) {
+ var i, d, cluster, center;
+ var distance = 40000; // Some large number
+ var clusterToAddTo = null;
+ for (i = 0; i < this.clusters_.length; i++) {
+ cluster = this.clusters_[i];
+ center = cluster.getCenter();
+ if (center) {
+ d = this.distanceBetweenPoints_(center, marker.getPosition());
+ if (d < distance) {
+ distance = d;
+ clusterToAddTo = cluster;
+ }
+ }
+ }
+
+ if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) {
+ clusterToAddTo.addMarker(marker);
+ } else {
+ cluster = new Cluster(this);
+ cluster.addMarker(marker);
+ this.clusters_.push(cluster);
+ }
+};
+
+
+/**
+ * Creates the clusters. This is done in batches to avoid timeout errors
+ * in some browsers when there is a huge number of markers.
+ *
+ * @param {number} iFirst The index of the first marker in the batch of
+ * markers to be added to clusters.
+ */
+MarkerClusterer.prototype.createClusters_ = function (iFirst) {
+ var i, marker;
+ var mapBounds;
+ var cMarkerClusterer = this;
+ if (!this.ready_) {
+ return;
+ }
+
+ // Cancel previous batch processing if we're working on the first batch:
+ if (iFirst === 0) {
+ /**
+ * This event is fired when the <code>MarkerClusterer</code> begins
+ * clustering markers.
+ * @name MarkerClusterer#clusteringbegin
+ * @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered.
+ * @event
+ */
+ google.maps.event.trigger(this, "clusteringbegin", this);
+
+ if (typeof this.timerRefStatic !== "undefined") {
+ clearTimeout(this.timerRefStatic);
+ delete this.timerRefStatic;
+ }
+ }
+
+ // Get our current map view bounds.
+ // Create a new bounds object so we don't affect the map.
+ //
+ // See Comments 9 & 11 on Issue 3651 relating to this workaround for a Google Maps bug:
+ if (this.getMap().getZoom() > 3) {
+ mapBounds = new google.maps.LatLngBounds(this.getMap().getBounds().getSouthWest(),
+ this.getMap().getBounds().getNorthEast());
+ } else {
+ mapBounds = new google.maps.LatLngBounds(new google.maps.LatLng(85.02070771743472, -178.48388434375), new google.maps.LatLng(-85.08136444384544, 178.00048865625));
+ }
+ var bounds = this.getExtendedBounds(mapBounds);
+
+ var iLast = Math.min(iFirst + this.batchSize_, this.markers_.length);
+
+ for (i = iFirst; i < iLast; i++) {
+ marker = this.markers_[i];
+ if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) {
+ if (!this.ignoreHidden_ || (this.ignoreHidden_ && marker.getVisible())) {
+ this.addToClosestCluster_(marker);
+ }
+ }
+ }
+
+ if (iLast < this.markers_.length) {
+ this.timerRefStatic = setTimeout(function () {
+ cMarkerClusterer.createClusters_(iLast);
+ }, 0);
+ } else {
+ delete this.timerRefStatic;
+
+ /**
+ * This event is fired when the <code>MarkerClusterer</code> stops
+ * clustering markers.
+ * @name MarkerClusterer#clusteringend
+ * @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered.
+ * @event
+ */
+ google.maps.event.trigger(this, "clusteringend", this);
+ }
+};
+
+
+/**
+ * Extends an object's prototype by another's.
+ *
+ * @param {Object} obj1 The object to be extended.
+ * @param {Object} obj2 The object to extend with.
+ * @return {Object} The new extended object.
+ * @ignore
+ */
+MarkerClusterer.prototype.extend = function (obj1, obj2) {
+ return (function (object) {
+ var property;
+ for (property in object.prototype) {
+ this.prototype[property] = object.prototype[property];
+ }
+ return this;
+ }).apply(obj1, [obj2]);
+};
+
+
+/**
+ * The default function for determining the label text and style
+ * for a cluster icon.
+ *
+ * @param {Array.<google.maps.Marker>} markers The array of markers represented by the cluster.
+ * @param {number} numStyles The number of marker styles available.
+ * @return {ClusterIconInfo} The information resource for the cluster.
+ * @constant
+ * @ignore
+ */
+MarkerClusterer.CALCULATOR = function (markers, numStyles) {
+ var index = 0;
+ var title = "";
+ var count = markers.length.toString();
+
+ var dv = count;
+ while (dv !== 0) {
+ dv = parseInt(dv / 10, 10);
+ index++;
+ }
+
+ index = Math.min(index, numStyles);
+ return {
+ text: count,
+ index: index,
+ title: title
+ };
+};
+
+
+/**
+ * The number of markers to process in one batch.
+ *
+ * @type {number}
+ * @constant
+ */
+MarkerClusterer.BATCH_SIZE = 2000;
+
+
+/**
+ * The number of markers to process in one batch (IE only).
+ *
+ * @type {number}
+ * @constant
+ */
+MarkerClusterer.BATCH_SIZE_IE = 500;
+
+
+/**
+ * The default root name for the marker cluster images.
+ *
+ * @type {string}
+ * @constant
+ */
+MarkerClusterer.IMAGE_PATH = "../images/m";
+
+
+/**
+ * The default extension name for the marker cluster images.
+ *
+ * @type {string}
+ * @constant
+ */
+MarkerClusterer.IMAGE_EXTENSION = "png";
+
+
+/**
+ * The default array of sizes for the marker cluster images.
+ *
+ * @type {Array.<number>}
+ * @constant
+ */
+MarkerClusterer.IMAGE_SIZES = [53, 56, 66, 78, 90];
+
+window.MarkerClusterer = MarkerClusterer;
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/markerwithlabel.css b/www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/markerwithlabel.css
new file mode 100644
index 00000000..5ba559de
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/markerwithlabel.css
@@ -0,0 +1,13 @@
+.markerwithlabel {
+ color: black;
+ background-color: white;
+ font-family: "Lucida Grande", "Arial", sans-serif;
+ font-size: 12px;
+ text-align: center;
+ white-space: nowrap;
+ padding: 3px;
+ border-radius: 4px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ border: 1px solid darkred;
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/markerwithlabel.js b/www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/markerwithlabel.js
new file mode 100644
index 00000000..90efe70e
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/gm3-util-library/markerwithlabel.js
@@ -0,0 +1,568 @@
+/**
+ * @name MarkerWithLabel for V3
+ * @version 1.1.5 [July 11, 2011]
+ * @author Gary Little (inspired by code from Marc Ridey of Google).
+ * @copyright Copyright 2010 Gary Little [gary at luxcentral.com]
+ * @fileoverview MarkerWithLabel extends the Google Maps JavaScript API V3
+ * <code>google.maps.Marker</code> class.
+ * <p>
+ * MarkerWithLabel allows you to define markers with associated labels. As you would expect,
+ * if the marker is draggable, so too will be the label. In addition, a marker with a label
+ * responds to all mouse events in the same manner as a regular marker. It also fires mouse
+ * events and "property changed" events just as a regular marker would. Version 1.1 adds
+ * support for the raiseOnDrag feature introduced in API V3.3.
+ * <p>
+ * If you drag a marker by its label, you can cancel the drag and return the marker to its
+ * original position by pressing the <code>Esc</code> key. This doesn't work if you drag the marker
+ * itself because this feature is not (yet) supported in the <code>google.maps.Marker</code> class.
+ */
+
+/*!
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*jslint browser:true */
+/*global document,google */
+
+/**
+ * This constructor creates a label and associates it with a marker.
+ * It is for the private use of the MarkerWithLabel class.
+ * @constructor
+ * @param {Marker} marker The marker with which the label is to be associated.
+ * @param {string} crossURL The URL of the cross image =.
+ * @param {string} handCursor The URL of the hand cursor.
+ * @private
+ */
+function MarkerLabel_(marker, crossURL, handCursorURL) {
+ this.marker_ = marker;
+ this.handCursorURL_ = marker.handCursorURL;
+
+ this.labelDiv_ = document.createElement("div");
+ this.labelDiv_.style.cssText = "position: absolute; overflow: hidden;";
+
+ // Set up the DIV for handling mouse events in the label. This DIV forms a transparent veil
+ // in the "overlayMouseTarget" pane, a veil that covers just the label. This is done so that
+ // events can be captured even if the label is in the shadow of a google.maps.InfoWindow.
+ // Code is included here to ensure the veil is always exactly the same size as the label.
+ this.eventDiv_ = document.createElement("div");
+ this.eventDiv_.style.cssText = this.labelDiv_.style.cssText;
+
+ // This is needed for proper behavior on MSIE:
+ this.eventDiv_.setAttribute("onselectstart", "return false;");
+ this.eventDiv_.setAttribute("ondragstart", "return false;");
+
+ // Get the DIV for the "X" to be displayed when the marker is raised.
+ this.crossDiv_ = MarkerLabel_.getSharedCross(crossURL);
+}
+
+// MarkerLabel_ inherits from OverlayView:
+MarkerLabel_.prototype = new google.maps.OverlayView();
+
+/**
+ * Returns the DIV for the cross used when dragging a marker when the
+ * raiseOnDrag parameter set to true. One cross is shared with all markers.
+ * @param {string} crossURL The URL of the cross image =.
+ * @private
+ */
+MarkerLabel_.getSharedCross = function (crossURL) {
+ var div;
+ if (typeof MarkerLabel_.getSharedCross.crossDiv === "undefined") {
+ div = document.createElement("img");
+ div.style.cssText = "position: absolute; z-index: 1000002; display: none;";
+ // Hopefully Google never changes the standard "X" attributes:
+ div.style.marginLeft = "-8px";
+ div.style.marginTop = "-9px";
+ div.src = crossURL;
+ MarkerLabel_.getSharedCross.crossDiv = div;
+ }
+ return MarkerLabel_.getSharedCross.crossDiv;
+};
+
+/**
+ * Adds the DIV representing the label to the DOM. This method is called
+ * automatically when the marker's <code>setMap</code> method is called.
+ * @private
+ */
+MarkerLabel_.prototype.onAdd = function () {
+ var me = this;
+ var cMouseIsDown = false;
+ var cDraggingLabel = false;
+ var cSavedZIndex;
+ var cLatOffset, cLngOffset;
+ var cIgnoreClick;
+ var cRaiseEnabled;
+ var cStartPosition;
+ var cStartCenter;
+ // Constants:
+ var cRaiseOffset = 20;
+ var cDraggingCursor = "url(" + this.handCursorURL_ + ")";
+
+ // Stops all processing of an event.
+ //
+ var cAbortEvent = function (e) {
+ if (e.preventDefault) {
+ e.preventDefault();
+ }
+ e.cancelBubble = true;
+ if (e.stopPropagation) {
+ e.stopPropagation();
+ }
+ };
+
+ var cStopBounce = function () {
+ me.marker_.setAnimation(null);
+ };
+
+ this.getPanes().overlayImage.appendChild(this.labelDiv_);
+ this.getPanes().overlayMouseTarget.appendChild(this.eventDiv_);
+ // One cross is shared with all markers, so only add it once:
+ if (typeof MarkerLabel_.getSharedCross.processed === "undefined") {
+ this.getPanes().overlayImage.appendChild(this.crossDiv_);
+ MarkerLabel_.getSharedCross.processed = true;
+ }
+
+ this.listeners_ = [
+ google.maps.event.addDomListener(this.eventDiv_, "mouseover", function (e) {
+ if (me.marker_.getDraggable() || me.marker_.getClickable()) {
+ this.style.cursor = "pointer";
+ google.maps.event.trigger(me.marker_, "mouseover", e);
+ }
+ }),
+ google.maps.event.addDomListener(this.eventDiv_, "mouseout", function (e) {
+ if ((me.marker_.getDraggable() || me.marker_.getClickable()) && !cDraggingLabel) {
+ this.style.cursor = me.marker_.getCursor();
+ google.maps.event.trigger(me.marker_, "mouseout", e);
+ }
+ }),
+ google.maps.event.addDomListener(this.eventDiv_, "mousedown", function (e) {
+ cDraggingLabel = false;
+ if (me.marker_.getDraggable()) {
+ cMouseIsDown = true;
+ this.style.cursor = cDraggingCursor;
+ }
+ if (me.marker_.getDraggable() || me.marker_.getClickable()) {
+ google.maps.event.trigger(me.marker_, "mousedown", e);
+ cAbortEvent(e); // Prevent map pan when starting a drag on a label
+ }
+ }),
+ google.maps.event.addDomListener(document, "mouseup", function (mEvent) {
+ var position;
+ if (cMouseIsDown) {
+ cMouseIsDown = false;
+ me.eventDiv_.style.cursor = "pointer";
+ google.maps.event.trigger(me.marker_, "mouseup", mEvent);
+ }
+ if (cDraggingLabel) {
+ if (cRaiseEnabled) { // Lower the marker & label
+ position = me.getProjection().fromLatLngToDivPixel(me.marker_.getPosition());
+ position.y += cRaiseOffset;
+ me.marker_.setPosition(me.getProjection().fromDivPixelToLatLng(position));
+ // This is not the same bouncing style as when the marker portion is dragged,
+ // but it will have to do:
+ try { // Will fail if running Google Maps API earlier than V3.3
+ me.marker_.setAnimation(google.maps.Animation.BOUNCE);
+ setTimeout(cStopBounce, 1406);
+ } catch (e) {}
+ }
+ me.crossDiv_.style.display = "none";
+ me.marker_.setZIndex(cSavedZIndex);
+ cIgnoreClick = true; // Set flag to ignore the click event reported after a label drag
+ cDraggingLabel = false;
+ mEvent.latLng = me.marker_.getPosition();
+ google.maps.event.trigger(me.marker_, "dragend", mEvent);
+ }
+ }),
+ google.maps.event.addListener(me.marker_.getMap(), "mousemove", function (mEvent) {
+ var position;
+ if (cMouseIsDown) {
+ if (cDraggingLabel) {
+ // Change the reported location from the mouse position to the marker position:
+ mEvent.latLng = new google.maps.LatLng(mEvent.latLng.lat() - cLatOffset, mEvent.latLng.lng() - cLngOffset);
+ position = me.getProjection().fromLatLngToDivPixel(mEvent.latLng);
+ if (cRaiseEnabled) {
+ me.crossDiv_.style.left = position.x + "px";
+ me.crossDiv_.style.top = position.y + "px";
+ me.crossDiv_.style.display = "";
+ position.y -= cRaiseOffset;
+ }
+ me.marker_.setPosition(me.getProjection().fromDivPixelToLatLng(position));
+ if (cRaiseEnabled) { // Don't raise the veil; this hack needed to make MSIE act properly
+ me.eventDiv_.style.top = (position.y + cRaiseOffset) + "px";
+ }
+ google.maps.event.trigger(me.marker_, "drag", mEvent);
+ } else {
+ // Calculate offsets from the click point to the marker position:
+ cLatOffset = mEvent.latLng.lat() - me.marker_.getPosition().lat();
+ cLngOffset = mEvent.latLng.lng() - me.marker_.getPosition().lng();
+ cSavedZIndex = me.marker_.getZIndex();
+ cStartPosition = me.marker_.getPosition();
+ cStartCenter = me.marker_.getMap().getCenter();
+ cRaiseEnabled = me.marker_.get("raiseOnDrag");
+ cDraggingLabel = true;
+ me.marker_.setZIndex(1000000); // Moves the marker & label to the foreground during a drag
+ mEvent.latLng = me.marker_.getPosition();
+ google.maps.event.trigger(me.marker_, "dragstart", mEvent);
+ }
+ }
+ }),
+ google.maps.event.addDomListener(document, "keydown", function (e) {
+ if (cDraggingLabel) {
+ if (e.keyCode === 27) { // Esc key
+ cRaiseEnabled = false;
+ me.marker_.setPosition(cStartPosition);
+ me.marker_.getMap().setCenter(cStartCenter);
+ google.maps.event.trigger(document, "mouseup", e);
+ }
+ }
+ }),
+ google.maps.event.addDomListener(this.eventDiv_, "click", function (e) {
+ if (me.marker_.getDraggable() || me.marker_.getClickable()) {
+ if (cIgnoreClick) { // Ignore the click reported when a label drag ends
+ cIgnoreClick = false;
+ } else {
+ google.maps.event.trigger(me.marker_, "click", e);
+ cAbortEvent(e); // Prevent click from being passed on to map
+ }
+ }
+ }),
+ google.maps.event.addDomListener(this.eventDiv_, "dblclick", function (e) {
+ if (me.marker_.getDraggable() || me.marker_.getClickable()) {
+ google.maps.event.trigger(me.marker_, "dblclick", e);
+ cAbortEvent(e); // Prevent map zoom when double-clicking on a label
+ }
+ }),
+ google.maps.event.addListener(this.marker_, "dragstart", function (mEvent) {
+ if (!cDraggingLabel) {
+ cRaiseEnabled = this.get("raiseOnDrag");
+ }
+ }),
+ google.maps.event.addListener(this.marker_, "drag", function (mEvent) {
+ if (!cDraggingLabel) {
+ if (cRaiseEnabled) {
+ me.setPosition(cRaiseOffset);
+ // During a drag, the marker's z-index is temporarily set to 1000000 to
+ // ensure it appears above all other markers. Also set the label's z-index
+ // to 1000000 (plus or minus 1 depending on whether the label is supposed
+ // to be above or below the marker).
+ me.labelDiv_.style.zIndex = 1000000 + (this.get("labelInBackground") ? -1 : +1);
+ }
+ }
+ }),
+ google.maps.event.addListener(this.marker_, "dragend", function (mEvent) {
+ if (!cDraggingLabel) {
+ if (cRaiseEnabled) {
+ me.setPosition(0); // Also restores z-index of label
+ }
+ }
+ }),
+ google.maps.event.addListener(this.marker_, "position_changed", function () {
+ me.setPosition();
+ }),
+ google.maps.event.addListener(this.marker_, "zindex_changed", function () {
+ me.setZIndex();
+ }),
+ google.maps.event.addListener(this.marker_, "visible_changed", function () {
+ me.setVisible();
+ }),
+ google.maps.event.addListener(this.marker_, "labelvisible_changed", function () {
+ me.setVisible();
+ }),
+ google.maps.event.addListener(this.marker_, "title_changed", function () {
+ me.setTitle();
+ }),
+ google.maps.event.addListener(this.marker_, "labelcontent_changed", function () {
+ me.setContent();
+ }),
+ google.maps.event.addListener(this.marker_, "labelanchor_changed", function () {
+ me.setAnchor();
+ }),
+ google.maps.event.addListener(this.marker_, "labelclass_changed", function () {
+ me.setStyles();
+ }),
+ google.maps.event.addListener(this.marker_, "labelstyle_changed", function () {
+ me.setStyles();
+ })
+ ];
+};
+
+/**
+ * Removes the DIV for the label from the DOM. It also removes all event handlers.
+ * This method is called automatically when the marker's <code>setMap(null)</code>
+ * method is called.
+ * @private
+ */
+MarkerLabel_.prototype.onRemove = function () {
+ var i;
+ this.labelDiv_.parentNode.removeChild(this.labelDiv_);
+ this.eventDiv_.parentNode.removeChild(this.eventDiv_);
+
+ // Remove event listeners:
+ for (i = 0; i < this.listeners_.length; i++) {
+ google.maps.event.removeListener(this.listeners_[i]);
+ }
+};
+
+/**
+ * Draws the label on the map.
+ * @private
+ */
+MarkerLabel_.prototype.draw = function () {
+ this.setContent();
+ this.setTitle();
+ this.setStyles();
+};
+
+/**
+ * Sets the content of the label.
+ * The content can be plain text or an HTML DOM node.
+ * @private
+ */
+MarkerLabel_.prototype.setContent = function () {
+ var content = this.marker_.get("labelContent");
+ if (typeof content.nodeType === "undefined") {
+ this.labelDiv_.innerHTML = content;
+ this.eventDiv_.innerHTML = this.labelDiv_.innerHTML;
+ } else {
+ this.labelDiv_.innerHTML = ""; // Remove current content
+ this.labelDiv_.appendChild(content);
+ content = content.cloneNode(true);
+ this.eventDiv_.appendChild(content);
+ }
+};
+
+/**
+ * Sets the content of the tool tip for the label. It is
+ * always set to be the same as for the marker itself.
+ * @private
+ */
+MarkerLabel_.prototype.setTitle = function () {
+ this.eventDiv_.title = this.marker_.getTitle() || "";
+};
+
+/**
+ * Sets the style of the label by setting the style sheet and applying
+ * other specific styles requested.
+ * @private
+ */
+MarkerLabel_.prototype.setStyles = function () {
+ var i, labelStyle;
+
+ // Apply style values from the style sheet defined in the labelClass parameter:
+ this.labelDiv_.className = this.marker_.get("labelClass");
+ this.eventDiv_.className = this.labelDiv_.className;
+
+ // Clear existing inline style values:
+ this.labelDiv_.style.cssText = "";
+ this.eventDiv_.style.cssText = "";
+ // Apply style values defined in the labelStyle parameter:
+ labelStyle = this.marker_.get("labelStyle");
+ for (i in labelStyle) {
+ if (labelStyle.hasOwnProperty(i)) {
+ this.labelDiv_.style[i] = labelStyle[i];
+ this.eventDiv_.style[i] = labelStyle[i];
+ }
+ }
+ this.setMandatoryStyles();
+};
+
+/**
+ * Sets the mandatory styles to the DIV representing the label as well as to the
+ * associated event DIV. This includes setting the DIV position, z-index, and visibility.
+ * @private
+ */
+MarkerLabel_.prototype.setMandatoryStyles = function () {
+ this.labelDiv_.style.position = "absolute";
+ this.labelDiv_.style.overflow = "hidden";
+ // Make sure the opacity setting causes the desired effect on MSIE:
+ if (typeof this.labelDiv_.style.opacity !== "undefined" && this.labelDiv_.style.opacity !== "") {
+ this.labelDiv_.style.filter = "alpha(opacity=" + (this.labelDiv_.style.opacity * 100) + ")";
+ }
+
+ this.eventDiv_.style.position = this.labelDiv_.style.position;
+ this.eventDiv_.style.overflow = this.labelDiv_.style.overflow;
+ this.eventDiv_.style.opacity = 0.01; // Don't use 0; DIV won't be clickable on MSIE
+ this.eventDiv_.style.filter = "alpha(opacity=1)"; // For MSIE
+
+ this.setAnchor();
+ this.setPosition(); // This also updates z-index, if necessary.
+ this.setVisible();
+};
+
+/**
+ * Sets the anchor point of the label.
+ * @private
+ */
+MarkerLabel_.prototype.setAnchor = function () {
+ var anchor = this.marker_.get("labelAnchor");
+ this.labelDiv_.style.marginLeft = -anchor.x + "px";
+ this.labelDiv_.style.marginTop = -anchor.y + "px";
+ this.eventDiv_.style.marginLeft = -anchor.x + "px";
+ this.eventDiv_.style.marginTop = -anchor.y + "px";
+};
+
+/**
+ * Sets the position of the label. The z-index is also updated, if necessary.
+ * @private
+ */
+MarkerLabel_.prototype.setPosition = function (yOffset) {
+ var position = this.getProjection().fromLatLngToDivPixel(this.marker_.getPosition());
+ if (typeof yOffset === "undefined") {
+ yOffset = 0;
+ }
+ this.labelDiv_.style.left = Math.round(position.x) + "px";
+ this.labelDiv_.style.top = Math.round(position.y - yOffset) + "px";
+ this.eventDiv_.style.left = this.labelDiv_.style.left;
+ this.eventDiv_.style.top = this.labelDiv_.style.top;
+
+ this.setZIndex();
+};
+
+/**
+ * Sets the z-index of the label. If the marker's z-index property has not been defined, the z-index
+ * of the label is set to the vertical coordinate of the label. This is in keeping with the default
+ * stacking order for Google Maps: markers to the south are in front of markers to the north.
+ * @private
+ */
+MarkerLabel_.prototype.setZIndex = function () {
+ var zAdjust = (this.marker_.get("labelInBackground") ? -1 : +1);
+ if (typeof this.marker_.getZIndex() === "undefined") {
+ this.labelDiv_.style.zIndex = parseInt(this.labelDiv_.style.top, 10) + zAdjust;
+ this.eventDiv_.style.zIndex = this.labelDiv_.style.zIndex;
+ } else {
+ this.labelDiv_.style.zIndex = this.marker_.getZIndex() + zAdjust;
+ this.eventDiv_.style.zIndex = this.labelDiv_.style.zIndex;
+ }
+};
+
+/**
+ * Sets the visibility of the label. The label is visible only if the marker itself is
+ * visible (i.e., its visible property is true) and the labelVisible property is true.
+ * @private
+ */
+MarkerLabel_.prototype.setVisible = function () {
+ if (this.marker_.get("labelVisible")) {
+ this.labelDiv_.style.display = this.marker_.getVisible() ? "block" : "none";
+ } else {
+ this.labelDiv_.style.display = "none";
+ }
+ this.eventDiv_.style.display = this.labelDiv_.style.display;
+};
+
+/**
+ * @name MarkerWithLabelOptions
+ * @class This class represents the optional parameter passed to the {@link MarkerWithLabel} constructor.
+ * The properties available are the same as for <code>google.maps.Marker</code> with the addition
+ * of the properties listed below. To change any of these additional properties after the labeled
+ * marker has been created, call <code>google.maps.Marker.set(propertyName, propertyValue)</code>.
+ * <p>
+ * When any of these properties changes, a property changed event is fired. The names of these
+ * events are derived from the name of the property and are of the form <code>propertyname_changed</code>.
+ * For example, if the content of the label changes, a <code>labelcontent_changed</code> event
+ * is fired.
+ * <p>
+ * @property {string|Node} [labelContent] The content of the label (plain text or an HTML DOM node).
+ * @property {Point} [labelAnchor] By default, a label is drawn with its anchor point at (0,0) so
+ * that its top left corner is positioned at the anchor point of the associated marker. Use this
+ * property to change the anchor point of the label. For example, to center a 50px-wide label
+ * beneath a marker, specify a <code>labelAnchor</code> of <code>google.maps.Point(25, 0)</code>.
+ * (Note: x-values increase to the right and y-values increase to the top.)
+ * @property {string} [labelClass] The name of the CSS class defining the styles for the label.
+ * Note that style values for <code>position</code>, <code>overflow</code>, <code>top</code>,
+ * <code>left</code>, <code>zIndex</code>, <code>display</code>, <code>marginLeft</code>, and
+ * <code>marginTop</code> are ignored; these styles are for internal use only.
+ * @property {Object} [labelStyle] An object literal whose properties define specific CSS
+ * style values to be applied to the label. Style values defined here override those that may
+ * be defined in the <code>labelClass</code> style sheet. If this property is changed after the
+ * label has been created, all previously set styles (except those defined in the style sheet)
+ * are removed from the label before the new style values are applied.
+ * Note that style values for <code>position</code>, <code>overflow</code>, <code>top</code>,
+ * <code>left</code>, <code>zIndex</code>, <code>display</code>, <code>marginLeft</code>, and
+ * <code>marginTop</code> are ignored; these styles are for internal use only.
+ * @property {boolean} [labelInBackground] A flag indicating whether a label that overlaps its
+ * associated marker should appear in the background (i.e., in a plane below the marker).
+ * The default is <code>false</code>, which causes the label to appear in the foreground.
+ * @property {boolean} [labelVisible] A flag indicating whether the label is to be visible.
+ * The default is <code>true</code>. Note that even if <code>labelVisible</code> is
+ * <code>true</code>, the label will <i>not</i> be visible unless the associated marker is also
+ * visible (i.e., unless the marker's <code>visible</code> property is <code>true</code>).
+ * @property {boolean} [raiseOnDrag] A flag indicating whether the label and marker are to be
+ * raised when the marker is dragged. The default is <code>true</code>. If a draggable marker is
+ * being created and a version of Google Maps API earlier than V3.3 is being used, this property
+ * must be set to <code>false</code>.
+ * @property {boolean} [optimized] A flag indicating whether rendering is to be optimized for the
+ * marker. <b>Important: The optimized rendering technique is not supported by MarkerWithLabel,
+ * so the value of this parameter is always forced to <code>false</code>.
+ * @property {string} [crossImage="http://maps.gstatic.com/intl/en_us/mapfiles/drag_cross_67_16.png"]
+ * The URL of the cross image to be displayed while dragging a marker.
+ * @property {string} [handCursor="http://maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur"]
+ * The URL of the cursor to be displayed while dragging a marker.
+ */
+/**
+ * Creates a MarkerWithLabel with the options specified in {@link MarkerWithLabelOptions}.
+ * @constructor
+ * @param {MarkerWithLabelOptions} [opt_options] The optional parameters.
+ */
+function MarkerWithLabel(opt_options) {
+ opt_options = opt_options || {};
+ opt_options.labelContent = opt_options.labelContent || "";
+ opt_options.labelAnchor = opt_options.labelAnchor || new google.maps.Point(0, 0);
+ opt_options.labelClass = opt_options.labelClass || "markerLabels";
+ opt_options.labelStyle = opt_options.labelStyle || {};
+ opt_options.labelInBackground = opt_options.labelInBackground || false;
+ if (typeof opt_options.labelVisible === "undefined") {
+ opt_options.labelVisible = true;
+ }
+ if (typeof opt_options.raiseOnDrag === "undefined") {
+ opt_options.raiseOnDrag = true;
+ }
+ if (typeof opt_options.clickable === "undefined") {
+ opt_options.clickable = true;
+ }
+ if (typeof opt_options.draggable === "undefined") {
+ opt_options.draggable = false;
+ }
+ if (typeof opt_options.optimized === "undefined") {
+ opt_options.optimized = false;
+ }
+ opt_options.crossImage = opt_options.crossImage || "//maps.gstatic.com/intl/en_us/mapfiles/drag_cross_67_16.png";
+ opt_options.handCursor = opt_options.handCursor || "//maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur";
+ opt_options.optimized = false; // Optimized rendering is not supported
+
+ this.label = new MarkerLabel_(this, opt_options.crossImage, opt_options.handCursor); // Bind the label to the marker
+
+ // Call the parent constructor. It calls Marker.setValues to initialize, so all
+ // the new parameters are conveniently saved and can be accessed with get/set.
+ // Marker.set triggers a property changed event (called "propertyname_changed")
+ // that the marker label listens for in order to react to state changes.
+ google.maps.Marker.apply(this, arguments);
+}
+
+// MarkerWithLabel inherits from <code>Marker</code>:
+MarkerWithLabel.prototype = new google.maps.Marker();
+
+/**
+ * Overrides the standard Marker setMap function.
+ * @param {Map} marker The map to which the marker is to be added.
+ * @private
+ */
+MarkerWithLabel.prototype.setMap = function (theMap) {
+
+ // Call the inherited function...
+ google.maps.Marker.prototype.setMap.apply(this, arguments);
+
+ // ... then deal with the label:
+ this.label.setMap(theMap);
+};
+
+window.MarkerWithLabel = MarkerWithLabel;
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/img/blue-dot.png b/www/wiki/extensions/Maps/resources/GoogleMaps/img/blue-dot.png
new file mode 100644
index 00000000..845055e7
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/img/blue-dot.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/GoogleMaps/jquery.googlemap.js b/www/wiki/extensions/Maps/resources/GoogleMaps/jquery.googlemap.js
new file mode 100644
index 00000000..d0317d16
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/GoogleMaps/jquery.googlemap.js
@@ -0,0 +1,900 @@
+/**
+ * JavaScript for Google Maps v3 maps in the Maps extension.
+ * @see http://www.mediawiki.org/wiki/Extension:Maps
+ *
+ * @author Jeroen De Dauw <jeroendedauw at gmail dot com>
+ * @author Peter Grassberger < petertheone@gmail.com >
+ */
+
+(function ($, mw) {
+ $.fn.googlemaps = function (options) {
+
+ var _this = this;
+ this.map = null;
+ this.markercluster = null;
+ this.options = options;
+
+ /**
+ * All markers that are currently on the map.
+ * @type {Array}
+ * @private
+ */
+ this.markers = [];
+
+ /**
+ * All Polylines currently on the map,
+ * @type {Array}
+ * @private
+ */
+ this.lines = [];
+
+ /**
+ * All polygons currently on the map,
+ */
+ this.polygons = [];
+
+
+ /**
+ * All circles on the map
+ */
+ this.circles = [];
+
+
+ /**
+ * All rectangles on the map
+ */
+ this.rectangles = [];
+
+
+ /**
+ * All image overlays on the map
+ */
+ this.imageoverlays = [];
+
+
+ var getBounds = function() {
+ if (( options.centre === false || options.zoom === false ) && options.locations.length > 1) {
+ var bounds = new google.maps.LatLngBounds();
+
+ for (var i = _this.markers.length - 1; i >= 0; i--) {
+ bounds.extend(_this.markers[i].getPosition());
+ }
+ return bounds;
+ }
+ return null;
+ };
+
+ var setZoom = function(bounds) {
+ if (options.zoom === false) {
+ _this.map.fitBounds(bounds);
+ }
+ else {
+ _this.map.setZoom(options.zoom);
+ }
+ };
+
+
+ /**
+ * Creates a new marker with the provided data,
+ * adds it to the map, and returns it.
+ * @param {Object} markerData Contains the fields lat, lon, title, text and icon
+ * @return {google.maps.Marker}
+ */
+ this.addMarker = function (markerData) {
+ var markerOptions = {
+ position: new google.maps.LatLng(markerData.lat, markerData.lon),
+ title: markerData.title,
+ text: markerData.text,
+ labelContent: markerData.inlineLabel,
+ labelAnchor: new google.maps.Point(-15, 34),
+ labelClass: 'markerwithlabel'
+ };
+
+ if ( markerOptions.text !== '' ) {
+ markerOptions.text = $('<div>' + markerOptions.text + '</div>').text();
+ }
+
+ if (!markerData.hasOwnProperty('icon') || markerData.icon !== '') {
+ markerOptions.icon = markerData.icon;
+ }
+
+ if (markerData.visitedicon !== '') {
+ if(markerData.visitedicon === 'on'){
+ //when keyword 'on' is set, set visitedicon to a default official marker
+ markerOptions.visitedicon = mw.config.get('wgScriptPath')+'/extensions/Maps/resources/GoogleMaps/img/blue-dot.png';
+ }else{
+ markerOptions.visitedicon = markerData.visitedicon;
+ }
+ }
+
+ var addToMapAndHandlers = function( marker ) {
+ //Add onclick listener
+ google.maps.event.addListener(marker, 'click', function (e) {
+ if (e.target !== undefined && (e.target instanceof HTMLAnchorElement || e.target.tagName == 'A')) {
+ //click link defined in inlinelabel
+ window.location.href = e.target.href;
+ } else {
+ openBubbleOrLink.call(this, markerData, e, marker);
+ }
+
+ if (markerOptions.visitedicon) {
+ marker.setIcon(markerOptions.visitedicon);
+ markerOptions.visitedicon = undefined;
+ }
+ });
+
+ marker.setMap( _this.map );
+ _this.markers.push( marker );
+
+ return marker;
+ };
+
+ var marker;
+ if (markerData.inlineLabel === undefined || markerData.inlineLabel === null || markerData.inlineLabel.length == 0) {
+ marker = new google.maps.Marker( markerOptions );
+ return addToMapAndHandlers( marker );
+ } else {
+ mw.loader.using(
+ 'ext.maps.gm3.markerwithlabel',
+ function() {
+ marker = new MarkerWithLabel( markerOptions );
+ addToMapAndHandlers( marker );
+ setZoom(getBounds());
+ }
+ );
+ }
+ };
+
+ /**
+ * Removes a single marker from the map.
+ * @param {google.maps.Marker} marker The marker to remove.
+ */
+ this.removeMarker = function (marker) {
+ marker.setMap(null);
+
+ for (var i = this.markers.length - 1; i >= 0; i--) {
+ if (this.markers[i] === marker) {
+ delete this.markers[i];
+ break;
+ }
+ }
+
+ delete marker;
+ };
+
+ /**
+ * Removes all markers from the map.
+ */
+ this.removeMarkers = function () {
+ if (this.markercluster) {
+ this.markercluster.setMap(null);
+ this.markercluster = null;
+ }
+ for (var i = this.markers.length - 1; i >= 0; i--) {
+ this.markers[i].setMap(null);
+ }
+ this.markers = [];
+ };
+
+ /**
+ * Remove the "earth" type from options.types if it's present.
+ *
+ * @since 1.0.1
+ */
+ this.removeEarthType = function () {
+ if (Array.prototype.filter) {
+ options.types = options.types.filter(function (element, index, array) {
+ return element !== 'earth';
+ });
+ }
+ else {
+ // Seems someone is using the o-so-awesome browser that is IE.
+ var types = [];
+
+ for (i in options.types) {
+ if (typeof( options.types[i] ) !== 'function' && options.types[i] !== 'earth') {
+ types.push(options.types[i]);
+ }
+ }
+
+ options.types = types;
+ }
+ };
+
+ this.addOverlays = function () {
+ // Add the Google KML/KMZ layers.
+ for (i = options.gkml.length - 1; i >= 0; i--) {
+ var kmlLayer = new google.maps.KmlLayer(
+ options.gkml[i],
+ {
+ map:this.map,
+ preserveViewport:!options.kmlrezoom
+ }
+ );
+ }
+
+ // If there are any non-Google KML/KMZ layers, load the geoxml library and use it to add these layers.
+ if (options.kml.length != 0) {
+ mw.loader.using('ext.maps.gm3.geoxml', function () {
+
+ function addToCopyCoords(doc){
+ if(options.copycoords){
+ for(var i = 0; i < doc.length; i++){
+ addCopyCoordsOnRightClick([
+ doc[i].gpolygons,
+ doc[i].gpolylines,
+ doc[i].ggroundoverlays
+ ]);
+ }
+ }
+ }
+
+
+ var geoXml = new geoXML3.parser({
+ map:_this.map,
+ zoom:options.kmlrezoom,
+ failedParse:function(){
+ alert(mw.msg('maps-kml-parsing-failed'));
+ }
+ });
+ geoXml.options.afterParse = function(docs){
+ //add toggle functionality
+ var toggleDiv = document.createElement('div');
+ toggleDiv.style.backgroundColor = 'white';
+ toggleDiv.style.marginTop = '5px';
+ toggleDiv.style.padding = '5px';
+ toggleDiv.style.border = '1px solid grey';
+ for(var i = docs.length-1; i >= 0; i--){
+ (function(doc){
+ var label = document.createElement('label');
+ label.style.display = 'block';
+ var text = document.createTextNode(doc.baseUrl.substring(doc.baseDir.length));
+ var checkbox = document.createElement('input');
+ checkbox.setAttribute('type','checkbox');
+ checkbox.style.verticalAlign = '-0.2em';
+ checkbox.checked = true;
+ checkbox.onclick = function(){
+ if(this.checked){
+ geoXml.showDocument(doc);
+ }else{
+ geoXml.hideDocument(doc);
+ }
+ };
+ label.appendChild(checkbox);
+ label.appendChild(text);
+
+ toggleDiv.appendChild(label);
+ })(docs[i]);
+ }
+ _this.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleDiv);
+ };
+
+ geoXml.parse(options.kml);
+ });
+ }
+ };
+
+ this.addLine = function (properties) {
+ var paths = new google.maps.MVCArray();
+ for (var x = 0; x < properties.pos.length; x++) {
+ paths.push(new google.maps.LatLng(properties.pos[x].lat, properties.pos[x].lon));
+ }
+
+ var line = new google.maps.Polyline({
+ map:this.map,
+ path:paths,
+ strokeColor:properties.strokeColor,
+ strokeOpacity:properties.strokeOpacity,
+ strokeWeight:properties.strokeWeight
+ });
+ this.lines.push(line);
+
+ google.maps.event.addListener(line, "click", function (event) {
+ openBubbleOrLink.call(this, properties, event, line);
+ });
+ };
+
+ this.removeLine = function (line) {
+ line.setMap(null);
+
+ for (var i = this.line.length - 1; i >= 0; i--) {
+ if (this.line[i] === line) {
+ delete this.line[i];
+ break;
+ }
+ }
+
+ delete line;
+ };
+
+ this.removeLines = function () {
+ for (var i = this.lines.length - 1; i >= 0; i--) {
+ this.lines[i].setMap(null);
+ }
+ this.lines = [];
+ };
+
+ this.addPolygon = function (properties) {
+ var paths = new google.maps.MVCArray();
+ for (var x = 0; x < properties.pos.length; x++) {
+ paths.push(new google.maps.LatLng(properties.pos[x].lat, properties.pos[x].lon));
+ }
+
+ var polygon = new google.maps.Polygon({
+ map:this.map,
+ path:paths,
+ strokeColor:properties.strokeColor,
+ strokeOpacity:properties.strokeOpacity,
+ strokeWeight:properties.strokeWeight,
+ fillColor:properties.fillColor,
+ fillOpacity:properties.fillOpacity
+ });
+ this.polygons.push(polygon);
+
+ //add hover event/effect
+ if (properties.onlyVisibleOnHover === true) {
+
+ function hidePolygon(polygon) {
+ polygon.setOptions({
+ fillOpacity:0,
+ strokeOpacity:0
+ });
+ }
+
+ hidePolygon(polygon);
+
+ google.maps.event.addListener(polygon, "mouseover", function () {
+ this.setOptions({
+ fillOpacity:properties.fillOpacity,
+ strokeOpacity:properties.strokeOpacity
+ });
+ });
+
+ google.maps.event.addListener(polygon, "mouseout", function () {
+ hidePolygon(this);
+ });
+
+ }
+
+ //add click event
+ google.maps.event.addListener(polygon, "click", function (event) {
+ openBubbleOrLink.call(this, properties, event, polygon);
+ });
+ };
+
+ this.addCircle = function (properties) {
+ var circle = new google.maps.Circle({
+ map:this.map,
+ center:new google.maps.LatLng(properties.centre.lat, properties.centre.lon),
+ radius:properties.radius,
+ fillColor:properties.fillColor,
+ fillOpacity:properties.fillOpacity,
+ strokeColor:properties.strokeColor,
+ strokeOpacity:properties.strokeOpacity,
+ strokeWeight:properties.strokeWeight
+ });
+ this.circles.push(circle);
+
+ //add click event
+ google.maps.event.addListener(circle, "click", function (event) {
+ openBubbleOrLink.call(this, properties, event, circle);
+ });
+ };
+
+
+ this.addRectangle = function (properties) {
+ var rectangle = new google.maps.Rectangle({
+ map:this.map,
+ bounds:new google.maps.LatLngBounds(
+ new google.maps.LatLng(properties.sw.lat, properties.sw.lon), //sw
+ new google.maps.LatLng(properties.ne.lat, properties.ne.lon) //ne
+ ),
+ fillColor:properties.fillColor,
+ fillOpacity:properties.fillOpacity,
+ strokeColor:properties.strokeColor,
+ strokeOpacity:properties.strokeOpacity,
+ strokeWeight:properties.strokeWeight
+ });
+ this.rectangles.push(rectangle);
+
+ //add click event
+ google.maps.event.addListener(rectangle, "click", function (event) {
+ openBubbleOrLink.call(this, properties, event, rectangle);
+ });
+ };
+
+ this.addImageOverlay = function(properties){
+ var imageBounds = new google.maps.LatLngBounds(
+ new google.maps.LatLng(properties.sw.lat,properties.sw.lon),
+ new google.maps.LatLng(properties.ne.lat,properties.ne.lon)
+ );
+
+ var image = new google.maps.GroundOverlay(properties.image,imageBounds);
+ image.setMap(this.map);
+
+ this.imageoverlays.push(image);
+
+ //add click event
+ google.maps.event.addListener(image, "click", function (event) {
+ openBubbleOrLink.call(this, properties, event, image);
+ });
+ };
+
+
+ this.removePolygon = function (polygon) {
+ polygon.setMap(null);
+
+ for (var i = this.polygon.length - 1; i >= 0; i--) {
+ if (this.polygon[i] === polygon) {
+ delete this.polygon[i];
+ break;
+ }
+ }
+
+ delete polygon;
+ };
+
+ this.removePolygons = function () {
+ for (var i = this.polygon.length - 1; i >= 0; i--) {
+ this.polygon[i].setMap(null);
+ }
+ this.polygon = [];
+ };
+
+ //Rezoom's the map to show all visible markers.
+ this.reZoom = function(){
+ var bounds = new google.maps.LatLngBounds();
+ for(var x = 0; x < this.markers.length; x++){
+ var marker = this.markers[x];
+ if (marker.getVisible() === true) {
+ bounds.extend(marker.getPosition());
+ }
+ }
+ this.map.fitBounds(bounds);
+ };
+
+ this.createMarkerCluster = function() {
+ if ( !options.markercluster ) {
+ return;
+ }
+ if (this.markercluster) {
+ this.markercluster.setMap(null);
+ this.markercluster = null;
+ }
+ this.markercluster = new MarkerClusterer( this.map, this.markers, {
+ imagePath: mw.config.get( 'wgScriptPath' ) +
+ '/extensions/Maps/resources/leaflet/cluster/m',
+ gridSize: this.options.clustergridsize,
+ maxZoom: this.options.clustermaxzoom,
+ zoomOnClick: this.options.clusterzoomonclick,
+ averageCenter: this.options.clusteraveragecenter,
+ minimumClusterSize: this.options.clusterminsize
+ } );
+ };
+
+ this.initializeMap = function () {
+ var mapOptions = {
+ disableDefaultUI:true,
+ mapTypeId:options.type == 'earth' ? google.maps.MapTypeId.SATELLITE : google.maps.MapTypeId[ options.type ]
+ };
+
+ // Map controls
+ mapOptions.panControl = $.inArray('pan', options.controls) != -1;
+ mapOptions.zoomControl = $.inArray('zoom', options.controls) != -1;
+ mapOptions.mapTypeControl = $.inArray('type', options.controls) != -1;
+ mapOptions.scaleControl = $.inArray('scale', options.controls) != -1;
+ mapOptions.streetViewControl = $.inArray('streetview', options.controls) != -1;
+ mapOptions.rotateControl = $.inArray('rotate', options.controls) != -1;
+
+ for (i in options.types) {
+ if (typeof( options.types[i] ) !== 'function') {
+ options.types[i] = google.maps.MapTypeId[ options.types[i].toUpperCase() ];
+ }
+ }
+
+ // Map control styles
+ mapOptions.zoomControlOptions = { style:google.maps.ZoomControlStyle[ options.zoomstyle ] };
+ mapOptions.mapTypeControlOptions = {
+ style:google.maps.MapTypeControlStyle[ options.typestyle ],
+ mapTypeIds:options.types
+ };
+
+
+ //max/min -zoom
+ mapOptions.maxZoom = options.maxzoom === false ? null : options.maxzoom;
+ mapOptions.minZoom = options.minzoom === false ? null : options.minzoom;
+
+ //static mode
+ if (options['static']) {
+ mapOptions.draggable = false;
+ mapOptions.disableDoubleClickZoom = true;
+ mapOptions.panControl = false;
+ mapOptions.rotateControl = false;
+ mapOptions.zoomControl = false;
+ mapOptions.scrollwheel = false;
+ mapOptions.streetViewControl = false;
+ mapOptions.overviewMapControl = false;
+ mapOptions.mapTypeControl = false;
+ }
+
+ if (options.scrollwheelzoom === true) {
+ mapOptions.gestureHandling = 'greedy';
+ }
+
+ var map = new google.maps.Map(this.get(0), mapOptions);
+
+ google.maps.event.addListenerOnce(map, 'tilesloaded', function () {
+ _this.addOverlays();
+ });
+
+
+ this.map = map;
+
+ if (options.poi === false) {
+ map.setOptions({ styles:[
+ {
+ featureType:"poi",
+ stylers:[
+ { visibility:"off" }
+ ]
+ }
+ ] });
+ }
+
+ if (!options.locations) {
+ options.locations = [];
+ }
+
+ // Add the markers.
+ for (var i = options.locations.length - 1; i >= 0; i--) {
+ this.addMarker(options.locations[i]);
+ }
+
+ for (i = options.layers.length - 1; i >= 0; i--) {
+ if ( options.layers[i] === 'traffic' ) {
+ ( new google.maps.TrafficLayer() ).setMap(map);
+ }
+ else if ( options.layers[i] === 'bicycling' ) {
+ ( new google.maps.BicyclingLayer() ).setMap(map);
+ }
+ else if ( options.layers[i] === 'transit' ) {
+ ( new google.maps.TransitLayer() ).setMap(map);
+ }
+ }
+
+ var bounds = getBounds();
+
+ setZoom(bounds);
+
+ var centre;
+
+ if (options.centre === false) {
+ if (options.locations.length > 1) {
+ centre = bounds.getCenter();
+ }
+ else if (options.locations.length == 1) {
+ centre = new google.maps.LatLng(options.locations[0].lat, options.locations[0].lon);
+ }
+ else {
+ centre = new google.maps.LatLng(0, 0);
+ }
+ }
+ else {
+ centre = new google.maps.LatLng(options.centre.lat, options.centre.lon);
+ }
+
+ map.setCenter(centre);
+
+ setTimeout(
+ function() {
+ if ( options.autoinfowindows ) {
+ for ( var i = _this.markers.length - 1; i >= 0; i-- ) {
+ google.maps.event.trigger( _this.markers[i], 'click', {} );
+ }
+ }
+ },
+ 500 // If we wait a bit, the map will re-position to accommodate for the info windows.
+ );
+
+ if (options.resizable) {
+ mw.loader.using('ext.maps.resizable', function () {
+ _this.resizable();
+ });
+ }
+
+ /**
+ * used in display_line functionality
+ * draws paths between markers
+ */
+ if (options.lines) {
+ for (var i = 0; i < options.lines.length; i++) {
+ this.addLine(options.lines[i]);
+ }
+ }
+
+ /**
+ * used in display_line to draw polygons
+ */
+ if (options.polygons) {
+ for (var i = 0; i < options.polygons.length; i++) {
+ this.addPolygon(options.polygons[i]);
+ }
+ }
+
+ /**
+ * used in display_line to draw circles
+ */
+ if (options.circles) {
+ for (var i = 0; i < options.circles.length; i++) {
+ this.addCircle(options.circles[i]);
+ }
+ }
+
+ /**
+ * used in display_line to draw rectangles
+ */
+ if (options.rectangles) {
+ for (var i = 0; i < options.rectangles.length; i++) {
+ this.addRectangle(options.rectangles[i]);
+ }
+ }
+
+ /**
+ * Allows grouping of markers.
+ */
+ this.createMarkerCluster();
+
+
+ if (options.searchmarkers) {
+ var searchBoxValue = mw.msg('maps-searchmarkers-text');
+ var searchBox = $('<input type="text" value="' + searchBoxValue + '" />');
+ var searchContainer = document.createElement('div');
+ searchContainer.style.padding = '5px';
+ searchContainer.index = 1;
+ searchBox.appendTo(searchContainer);
+ map.controls[google.maps.ControlPosition.TOP_RIGHT].push(searchContainer);
+
+ //prevents markers and other map objects to be placed beneath searchfield
+ google.maps.event.addListenerOnce(map, 'bounds_changed', function () {
+ map.panBy(0,-30);
+ });
+
+
+ searchBox.on('keyup',function (e) {
+ for (var i = 0; i < _this.markers.length; i++) {
+ var haystack = '';
+ var marker = _this.markers[i];
+ if (options.searchmarkers == 'title') {
+ haystack = marker.title;
+ } else {
+ haystack = marker.title + marker.text;
+ }
+
+ var visible = haystack.toLowerCase().indexOf(e.target.value.toLowerCase()) != -1;
+ marker.setVisible(visible);
+ }
+ _this.reZoom();
+ }).on('focusin',function () {
+ if ($(this).val() === searchBoxValue) {
+ $(this).val('');
+ }
+ }).on('focusout', function () {
+ if ($(this).val() === '') {
+ $(this).val(searchBoxValue);
+ }
+ });
+ }
+
+ if(options.imageoverlays){
+ for (var i = 0; i < options.imageoverlays.length; i++) {
+ this.addImageOverlay(options.imageoverlays[i]);
+ }
+ }
+
+ if (options.copycoords) {
+
+ addCopyCoordsOnRightClick([
+ this.lines,
+ this.circles,
+ this.polygons,
+ this.markers,
+ this.rectangles,
+ this.imageoverlays,
+ this.map
+ ]);
+ }
+
+ if (options.wmsoverlay) {
+ var wmsOptions = {
+ alt: "OpenLayers",
+ getTileUrl:function (tile, zoom) {
+ var projection = _this.map.getProjection();
+ var zpow = Math.pow(2, zoom);
+ var ul = new google.maps.Point(tile.x * 256.0 / zpow, (tile.y + 1) * 256.0 / zpow);
+ var lr = new google.maps.Point((tile.x + 1) * 256.0 / zpow, (tile.y) * 256.0 / zpow);
+ var ulw = projection.fromPointToLatLng(ul);
+ var lrw = projection.fromPointToLatLng(lr);
+ //The user will enter the address to the public WMS layer here. The data must be in WGS84
+ var baseURL = options.wmsoverlay.wmsServerUrl;
+ //The layer ID. Can be found when using the layers properties tool in ArcMap or from the WMS settings
+ var layers = options.wmsoverlay.wmsLayerName;
+
+ var style = options.wmsoverlay.wmsStyleName;
+ //With the 1.3.0 version the coordinates are read in LatLon, as opposed to LonLat in previous versions
+ var bbox = ulw.lat() + "," + ulw.lng() + "," + lrw.lat() + "," + lrw.lng();
+ //Establish the baseURL. Several elements, including &EXCEPTIONS=INIMAGE and &Service are unique to openLayers addresses.
+ var url = baseURL + "version=1.3.0&EXCEPTIONS=INIMAGE&Service=WMS" +
+ "&request=GetMap&Styles=" + encodeURI(style) + "&format=image%2Fjpeg&CRS=EPSG:4326" +
+ "&width=256&height=256" + "&Layers=" + layers + "&BBOX=" + bbox;
+ return url;
+ },
+ isPng: false,
+ maxZoom: 17,
+ minZoom: 1,
+ name: "OpenLayers",
+ tileSize: new google.maps.Size(256, 256)
+
+ };
+
+
+
+ //Creating the object to create the ImageMapType that will call the WMS Layer Options.
+
+ openlayersWMS = new google.maps.ImageMapType(wmsOptions);
+
+
+ //Layers to appear on Map A. The first string will give the map the map a name in the dropdown and the second object calls the map type
+
+ map.mapTypes.set('OpenLayers', openlayersWMS);
+ map.setMapTypeId('OpenLayers');
+ }
+
+ //Add custom controls
+ // - Fullscreen
+ if(options.enablefullscreen){
+ this.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(new FullscreenControl(this.map));
+ }
+ };
+
+ this.setup = function () {
+
+ var showEarth = $.inArray('earth', options.types) !== -1 || options.type == 'earth';
+
+ // If there are any non-Google KML/KMZ layers, load the geoxml library and use it to add these layers.
+ if (showEarth) {
+ this.removeEarthType();
+ $.getScript(
+ 'https://www.google.com/jsapi',
+ function (data, textStatus) {
+ google.load('earth', '1', { callback:function () {
+ mw.loader.using('ext.maps.gm3.earth', function () {
+ _this.initializeMap();
+ if (google.earth.isSupported()) {
+ _this.ge = new GoogleEarth(_this.map);
+ }
+ });
+ } });
+ }
+ );
+ }else{
+ this.initializeMap();
+ }
+
+
+ };
+
+ function FullscreenControl(map) {
+
+ var controlDiv = document.createElement('div');
+ controlDiv.style.padding = '5px';
+ controlDiv.index = 1;
+
+ var controlUI = document.createElement('div');
+ controlUI.style.backgroundColor = 'white';
+ controlUI.style.borderStyle = 'solid';
+ controlUI.style.borderColor = 'rgba(0, 0, 0, 0.14902)';
+ controlUI.style.borderWidth = '1px';
+ controlUI.style.borderRadius = '2px';
+ controlUI.style.cursor = 'pointer';
+ controlUI.style.textAlign = 'center';
+ controlUI.style.boxShadow = 'rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px';
+ controlUI.style.backgroundClip = 'padding-box';
+ controlUI.title = mw.msg('maps-fullscreen-button-tooltip');
+ controlDiv.appendChild(controlUI);
+
+ var controlText = document.createElement('div');
+ controlText.style.fontFamily = 'Roboto, Arial, sans-serif';
+ controlText.style.fontSize = '11px';
+ controlText.style.fontWeight = '400';
+ controlText.style.color = 'rgb(86, 86, 86)';
+ controlText.style.padding = '1px 6px';
+ controlText.innerHTML = mw.msg('maps-fullscreen-button');
+ controlUI.appendChild(controlText);
+
+ google.maps.event.addDomListener(controlUI, 'click', function() {
+ var mapDiv = $(map.getDiv());
+ if(mapDiv.data('preFullscreenCss') != null){
+ mapDiv.css(mapDiv.data('preFullscreenCss'));
+ mapDiv.removeData('preFullscreenCss');
+ }else{
+ var fullscreenCss = {
+ position:'fixed',
+ top: 0,
+ left:0,
+ width:'100%',
+ height:'100%',
+ zIndex:10000
+ };
+ var oldState = {};
+ for(var cssProp in fullscreenCss){
+ oldState[cssProp] = mapDiv.css(cssProp);
+ }
+ mapDiv.data('preFullscreenCss',oldState);
+ mapDiv.css(fullscreenCss);
+ }
+
+ google.maps.event.trigger(map, "resize");
+ });
+
+ return controlDiv;
+ }
+
+
+ function openBubbleOrLink(markerData, event, obj) {
+ if (markerData.link) {
+ window.location.href = markerData.link;
+ } else if (markerData.text.trim() !== '') {
+ openBubble.call(this, markerData, event, obj);
+ }
+ }
+
+ function openBubble( markerData, event, obj ) {
+ if ( this.openWindow !== undefined ) {
+ this.openWindow.close();
+ }
+
+ this.openWindow = new google.maps.InfoWindow();
+ this.openWindow.setContent( markerData.text );
+
+ if ( event.latLng !== undefined ) {
+ this.openWindow.setPosition( event.latLng );
+ }
+
+ this.openWindow.closeclick = function () {
+ obj.openWindow = undefined;
+ };
+
+ if ( event.latLng === undefined ) {
+ this.openWindow.open( _this.map, this );
+ }
+ else {
+ this.openWindow.open( _this.map );
+ }
+ }
+
+ function addCopyCoordsOnRightClick(object) {
+ if(object instanceof Array){
+ for (var x = 0; x < object.length; x++) {
+ addCopyCoordsOnRightClick(object[x]);
+ }
+ }else{
+ google.maps.event.addListener(object, 'rightclick', function (event) {
+ prompt(mw.msg('maps-copycoords-prompt'), event.latLng.lat() + ',' + event.latLng.lng());
+ });
+ }
+ }
+
+ //Complete path to OpenLayers WMS layer
+
+ if (!options.markercluster) {
+ this.setup();
+ } else {
+ mw.loader.using( 'ext.maps.gm3.markercluster', function() {
+ _this.setup();
+ } );
+ }
+
+ return this;
+
+ };
+})(jQuery, window.mediaWiki);
diff --git a/www/wiki/extensions/Maps/resources/editor/css/jquery.miniColors.css b/www/wiki/extensions/Maps/resources/editor/css/jquery.miniColors.css
new file mode 100644
index 00000000..664c2fd4
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/editor/css/jquery.miniColors.css
@@ -0,0 +1,70 @@
+.miniColors-trigger {
+ height: 22px;
+ width: 22px;
+ /* @embed */
+ background: url(../images/trigger.png) center no-repeat;
+ vertical-align: middle;
+ margin: 0 .25em;
+ display: inline-block;
+ outline: none;
+}
+
+.miniColors-selector {
+ position: absolute;
+ width: 175px;
+ height: 150px;
+ background: #FFF;
+ border: solid 1px #BBB;
+ -moz-box-shadow: 0 0 6px rgba(0, 0, 0, .25);
+ -webkit-box-shadow: 0 0 6px rgba(0, 0, 0, .25);
+ box-shadow: 0 0 6px rgba(0, 0, 0, .25);
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ padding: 5px;
+ z-index: 999999;
+}
+
+.miniColors-selector.black {
+ background: #000;
+ border-color: #000;
+}
+
+.miniColors-colors {
+ position: absolute;
+ top: 5px;
+ left: 5px;
+ width: 150px;
+ height: 150px;
+ /* @embed */
+ background: url(../images/gradient.png) center no-repeat;
+ cursor: crosshair;
+}
+
+.miniColors-hues {
+ position: absolute;
+ top: 5px;
+ left: 160px;
+ width: 20px;
+ height: 150px;
+ /* @embed */
+ background: url(../images/rainbow.png) center no-repeat;
+ cursor: crosshair;
+}
+
+.miniColors-colorPicker {
+ position: absolute;
+ width: 11px;
+ height: 11px;
+ /* @embed */
+ background: url(../images/circle.gif) center no-repeat;
+}
+
+.miniColors-huePicker {
+ position: absolute;
+ left: -3px;
+ width: 26px;
+ height: 3px;
+ /* @embed */
+ background: url(../images/line.gif) center no-repeat;
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/editor/css/mapeditor.css b/www/wiki/extensions/Maps/resources/editor/css/mapeditor.css
new file mode 100644
index 00000000..9c3fe7c4
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/editor/css/mapeditor.css
@@ -0,0 +1,79 @@
+#code-output-container, #code-input-container,#code-input {
+ height: 100% !important;
+ width: 100%;
+}
+
+#code-output-container {
+ overflow: visible;
+}
+
+textarea#code-input, textarea#code-output {
+ resize: none;
+}
+
+#code-output {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ border: none;
+ padding: 10px;
+}
+
+#map-canvas {
+ width: 100%;
+ height: 500px;
+}
+
+#map-canvas,#code-input {
+ display: block;
+}
+
+.mapeditor-dialog-form fieldset label{
+ display: block;
+}
+
+.mapeditor-dialog-form fieldset input{
+ width: 200px;
+}
+
+.mapeditor-dialog-form fieldset input[type="checkbox"]{
+ width: auto;
+}
+
+.mapeditor-dialog-form fieldset input[name="strokeColor"],.mapeditor-dialog-form fieldset input[name="fillColor"]{
+ width: 100px;
+}
+
+.mapeditor-dialog-form .ui-slider{
+ margin: 5px 2px;
+ width: 200px;
+}
+
+.link-title-switcher {
+ margin: 5px;
+}
+
+#map-parameter-form{
+ text-align: center;
+}
+
+.mapeditor-controls{
+ padding: 5px;
+}
+
+.mapeditor-control-element{
+ background-color:white;
+ border: 1px solid #717B87;
+ cursor: pointer;
+ text-align: center;
+ float: left;
+ padding: 2px;
+}
+
+.mapeditor-control-text{
+ font-family:"Arial","sans-serif";
+ font-size: 12px;
+ padding-left: 4px;
+ padding-right: 4px;
+ font-weight: bold;
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/editor/images/circle.gif b/www/wiki/extensions/Maps/resources/editor/images/circle.gif
new file mode 100644
index 00000000..599f7f13
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/editor/images/circle.gif
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/editor/images/gradient.png b/www/wiki/extensions/Maps/resources/editor/images/gradient.png
new file mode 100644
index 00000000..486a9f6d
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/editor/images/gradient.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/editor/images/line.gif b/www/wiki/extensions/Maps/resources/editor/images/line.gif
new file mode 100644
index 00000000..9eb19837
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/editor/images/line.gif
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/editor/images/rainbow.png b/www/wiki/extensions/Maps/resources/editor/images/rainbow.png
new file mode 100644
index 00000000..d16fcd86
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/editor/images/rainbow.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/editor/images/trigger.png b/www/wiki/extensions/Maps/resources/editor/images/trigger.png
new file mode 100644
index 00000000..20ec282b
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/editor/images/trigger.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/editor/js/README b/www/wiki/extensions/Maps/resources/editor/js/README
new file mode 100644
index 00000000..53598d48
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/editor/js/README
@@ -0,0 +1,3 @@
+== jquery.miniColors.js ==
+Is dual licensed under the MIT / GPLv2 licenses
+https://github.com/claviska/jquery-miniColors \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/editor/js/jquery.miniColors.js b/www/wiki/extensions/Maps/resources/editor/js/jquery.miniColors.js
new file mode 100644
index 00000000..dfbce542
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/editor/js/jquery.miniColors.js
@@ -0,0 +1,567 @@
+/*
+ * jQuery miniColors: A small color selector
+ *
+ * Copyright 2011 Cory LaViska for A Beautiful Site, LLC. (http://abeautifulsite.net/)
+ *
+ * Dual licensed under the MIT or GPL Version 2 licenses
+ *
+*/
+if(jQuery) (function($) {
+
+ $.extend($.fn, {
+
+ miniColors: function(o, data) {
+
+ var create = function(input, o, data) {
+ //
+ // Creates a new instance of the miniColors selector
+ //
+
+ // Determine initial color (defaults to white)
+ var color = expandHex(input.val());
+ if( !color ) color = 'ffffff';
+ var hsb = hex2hsb(color);
+
+ // Create trigger
+ var trigger = $('<a class="miniColors-trigger" style="background-color: #' + color + '" href="#"></a>');
+ trigger.insertAfter(input);
+
+ // Set input data and update attributes
+ input
+ .addClass('miniColors')
+ .data('original-maxlength', input.attr('maxlength') || null)
+ .data('original-autocomplete', input.attr('autocomplete') || null)
+ .data('letterCase', 'uppercase')
+ .data('trigger', trigger)
+ .data('hsb', hsb)
+ .data('change', o.change ? o.change : null)
+ .attr('maxlength', 7)
+ .attr('autocomplete', 'off')
+ .val('#' + convertCase(color, o.letterCase));
+
+ // Handle options
+ if( o.readonly ) input.prop('readonly', true);
+ if( o.disabled ) disable(input);
+
+ // Show selector when trigger is clicked
+ trigger.bind('click.miniColors', function(event) {
+ event.preventDefault();
+ if( input.val() === '' ) input.val('#');
+ show(input);
+
+ });
+
+ // Show selector when input receives focus
+ input.bind('focus.miniColors', function(event) {
+ if( input.val() === '' ) input.val('#');
+ show(input);
+ });
+
+ // Hide on blur
+ input.bind('blur.miniColors', function(event) {
+ var hex = expandHex(input.val());
+ input.val( hex ? '#' + convertCase(hex, input.data('letterCase')) : '' );
+ });
+
+ // Hide when tabbing out of the input
+ input.bind('keydown.miniColors', function(event) {
+ if( event.keyCode === 9 ) hide(input);
+ });
+
+ // Update when color is typed in
+ input.bind('keyup.miniColors', function(event) {
+ setColorFromInput(input);
+ });
+
+ // Handle pasting
+ input.bind('paste.miniColors', function(event) {
+ // Short pause to wait for paste to complete
+ setTimeout( function() {
+ setColorFromInput(input);
+ }, 5);
+ });
+
+ };
+
+ var destroy = function(input) {
+ //
+ // Destroys an active instance of the miniColors selector
+ //
+
+ hide();
+ input = $(input);
+
+ // Restore to original state
+ input.data('trigger').remove();
+ input
+ .attr('autocomplete', input.data('original-autocomplete'))
+ .attr('maxlength', input.data('original-maxlength'))
+ .removeData()
+ .removeClass('miniColors')
+ .unbind('.miniColors');
+ $(document).unbind('.miniColors');
+ };
+
+ var enable = function(input) {
+ //
+ // Enables the input control and the selector
+ //
+ input
+ .prop('disabled', false)
+ .data('trigger')
+ .css('opacity', 1);
+ };
+
+ var disable = function(input) {
+ //
+ // Disables the input control and the selector
+ //
+ hide(input);
+ input
+ .prop('disabled', true)
+ .data('trigger')
+ .css('opacity', 0.5);
+ };
+
+ var show = function(input) {
+ //
+ // Shows the miniColors selector
+ //
+ if( input.prop('disabled') ) return false;
+
+ // Hide all other instances
+ hide();
+
+ // Generate the selector
+ var selector = $('<div class="miniColors-selector"></div>');
+ selector
+ .append('<div class="miniColors-colors" style="background-color: #FFF;"><div class="miniColors-colorPicker"></div></div>')
+ .append('<div class="miniColors-hues"><div class="miniColors-huePicker"></div></div>')
+ .css({
+ top: input.is(':visible') ? input.offset().top + input.outerHeight() : input.data('trigger').offset().top + input.data('trigger').outerHeight(),
+ left: input.is(':visible') ? input.offset().left : input.data('trigger').offset().left,
+ display: 'none'
+ })
+ .addClass( input.attr('class') );
+
+ // Set background for colors
+ var hsb = input.data('hsb');
+ selector
+ .find('.miniColors-colors')
+ .css('backgroundColor', '#' + hsb2hex({ h: hsb.h, s: 100, b: 100 }));
+
+ // Set colorPicker position
+ var colorPosition = input.data('colorPosition');
+ if( !colorPosition ) colorPosition = getColorPositionFromHSB(hsb);
+ selector.find('.miniColors-colorPicker')
+ .css('top', colorPosition.y + 'px')
+ .css('left', colorPosition.x + 'px');
+
+ // Set huePicker position
+ var huePosition = input.data('huePosition');
+ if( !huePosition ) huePosition = getHuePositionFromHSB(hsb);
+ selector.find('.miniColors-huePicker').css('top', huePosition.y + 'px');
+
+ // Set input data
+ input
+ .data('selector', selector)
+ .data('huePicker', selector.find('.miniColors-huePicker'))
+ .data('colorPicker', selector.find('.miniColors-colorPicker'))
+ .data('mousebutton', 0);
+
+ $('BODY').append(selector);
+ selector.fadeIn(100);
+
+ // Prevent text selection in IE
+ selector.bind('selectstart', function() { return false; });
+
+ $(document).bind('mousedown.miniColors touchstart.miniColors', function(event) {
+
+ input.data('mousebutton', 1);
+
+ if( $(event.target).parents().andSelf().hasClass('miniColors-colors') ) {
+ event.preventDefault();
+ input.data('moving', 'colors');
+ moveColor(input, event);
+ }
+
+ if( $(event.target).parents().andSelf().hasClass('miniColors-hues') ) {
+ event.preventDefault();
+ input.data('moving', 'hues');
+ moveHue(input, event);
+ }
+
+ if( $(event.target).parents().andSelf().hasClass('miniColors-selector') ) {
+ event.preventDefault();
+ return;
+ }
+
+ if( $(event.target).parents().andSelf().hasClass('miniColors') ) return;
+
+ hide(input);
+ });
+
+ $(document)
+ .bind('mouseup.miniColors touchend.miniColors', function(event) {
+ event.preventDefault();
+ input.data('mousebutton', 0).removeData('moving');
+ })
+ .bind('mousemove.miniColors touchmove.miniColors', function(event) {
+ event.preventDefault();
+ if( input.data('mousebutton') === 1 ) {
+ if( input.data('moving') === 'colors' ) moveColor(input, event);
+ if( input.data('moving') === 'hues' ) moveHue(input, event);
+ }
+ });
+
+ };
+
+ var hide = function(input) {
+
+ //
+ // Hides one or more miniColors selectors
+ //
+
+ // Hide all other instances if input isn't specified
+ if( !input ) input = '.miniColors';
+
+ $(input).each( function() {
+ var selector = $(this).data('selector');
+ $(this).removeData('selector');
+ $(selector).fadeOut(100, function() {
+ $(this).remove();
+ });
+ });
+
+ $(document).unbind('.miniColors');
+
+ };
+
+ var moveColor = function(input, event) {
+
+ var colorPicker = input.data('colorPicker');
+
+ colorPicker.hide();
+
+ var position = {
+ x: event.pageX,
+ y: event.pageY
+ };
+
+ // Touch support
+ if( event.originalEvent.changedTouches ) {
+ position.x = event.originalEvent.changedTouches[0].pageX;
+ position.y = event.originalEvent.changedTouches[0].pageY;
+ }
+ position.x = position.x - input.data('selector').find('.miniColors-colors').offset().left - 5;
+ position.y = position.y - input.data('selector').find('.miniColors-colors').offset().top - 5;
+ if( position.x <= -5 ) position.x = -5;
+ if( position.x >= 144 ) position.x = 144;
+ if( position.y <= -5 ) position.y = -5;
+ if( position.y >= 144 ) position.y = 144;
+
+ input.data('colorPosition', position);
+ colorPicker.css('left', position.x).css('top', position.y).show();
+
+ // Calculate saturation
+ var s = Math.round((position.x + 5) * 0.67);
+ if( s < 0 ) s = 0;
+ if( s > 100 ) s = 100;
+
+ // Calculate brightness
+ var b = 100 - Math.round((position.y + 5) * 0.67);
+ if( b < 0 ) b = 0;
+ if( b > 100 ) b = 100;
+
+ // Update HSB values
+ var hsb = input.data('hsb');
+ hsb.s = s;
+ hsb.b = b;
+
+ // Set color
+ setColor(input, hsb, true);
+ };
+
+ var moveHue = function(input, event) {
+
+ var huePicker = input.data('huePicker');
+
+ huePicker.hide();
+
+ var position = {
+ y: event.pageY
+ };
+
+ // Touch support
+ if( event.originalEvent.changedTouches ) {
+ position.y = event.originalEvent.changedTouches[0].pageY;
+ }
+
+ position.y = position.y - input.data('selector').find('.miniColors-colors').offset().top - 1;
+ if( position.y <= -1 ) position.y = -1;
+ if( position.y >= 149 ) position.y = 149;
+ input.data('huePosition', position);
+ huePicker.css('top', position.y).show();
+
+ // Calculate hue
+ var h = Math.round((150 - position.y - 1) * 2.4);
+ if( h < 0 ) h = 0;
+ if( h > 360 ) h = 360;
+
+ // Update HSB values
+ var hsb = input.data('hsb');
+ hsb.h = h;
+
+ // Set color
+ setColor(input, hsb, true);
+
+ };
+
+ var setColor = function(input, hsb, updateInput) {
+ input.data('hsb', hsb);
+ var hex = hsb2hex(hsb);
+ if( updateInput ) input.val( '#' + convertCase(hex, input.data('letterCase')) );
+ input.data('trigger').css('backgroundColor', '#' + hex);
+ if( input.data('selector') ) input.data('selector').find('.miniColors-colors').css('backgroundColor', '#' + hsb2hex({ h: hsb.h, s: 100, b: 100 }));
+
+ // Fire change callback
+ if( input.data('change') ) {
+ if( hex === input.data('lastChange') ) return;
+ input.data('change').call(input.get(0), '#' + hex, hsb2rgb(hsb));
+ input.data('lastChange', hex);
+ }
+
+ };
+
+ var setColorFromInput = function(input) {
+
+ input.val('#' + cleanHex(input.val()));
+ var hex = expandHex(input.val());
+ if( !hex ) return false;
+
+ // Get HSB equivalent
+ var hsb = hex2hsb(hex);
+
+ // If color is the same, no change required
+ var currentHSB = input.data('hsb');
+ if( hsb.h === currentHSB.h && hsb.s === currentHSB.s && hsb.b === currentHSB.b ) return true;
+
+ // Set colorPicker position
+ var colorPosition = getColorPositionFromHSB(hsb);
+ var colorPicker = $(input.data('colorPicker'));
+ colorPicker.css('top', colorPosition.y + 'px').css('left', colorPosition.x + 'px');
+ input.data('colorPosition', colorPosition);
+
+ // Set huePosition position
+ var huePosition = getHuePositionFromHSB(hsb);
+ var huePicker = $(input.data('huePicker'));
+ huePicker.css('top', huePosition.y + 'px');
+ input.data('huePosition', huePosition);
+
+ setColor(input, hsb);
+
+ return true;
+
+ };
+
+ var convertCase = function(string, letterCase) {
+ if( letterCase === 'lowercase' ) return string.toLowerCase();
+ if( letterCase === 'uppercase' ) return string.toUpperCase();
+ return string;
+ };
+
+ var getColorPositionFromHSB = function(hsb) {
+ var x = Math.ceil(hsb.s / 0.67);
+ if( x < 0 ) x = 0;
+ if( x > 150 ) x = 150;
+ var y = 150 - Math.ceil(hsb.b / 0.67);
+ if( y < 0 ) y = 0;
+ if( y > 150 ) y = 150;
+ return { x: x - 5, y: y - 5 };
+ };
+
+ var getHuePositionFromHSB = function(hsb) {
+ var y = 150 - (hsb.h / 2.4);
+ if( y < 0 ) h = 0;
+ if( y > 150 ) h = 150;
+ return { y: y - 1 };
+ };
+
+ var cleanHex = function(hex) {
+ return hex.replace(/[^A-F0-9]/ig, '');
+ };
+
+ var expandHex = function(hex) {
+ hex = cleanHex(hex);
+ if( !hex ) return null;
+ if( hex.length === 3 ) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
+ return hex.length === 6 ? hex : null;
+ };
+
+ var hsb2rgb = function(hsb) {
+ var rgb = {};
+ var h = Math.round(hsb.h);
+ var s = Math.round(hsb.s*255/100);
+ var v = Math.round(hsb.b*255/100);
+ if(s === 0) {
+ rgb.r = rgb.g = rgb.b = v;
+ } else {
+ var t1 = v;
+ var t2 = (255 - s) * v / 255;
+ var t3 = (t1 - t2) * (h % 60) / 60;
+ if( h === 360 ) h = 0;
+ if( h < 60 ) { rgb.r = t1; rgb.b = t2; rgb.g = t2 + t3; }
+ else if( h < 120 ) {rgb.g = t1; rgb.b = t2; rgb.r = t1 - t3; }
+ else if( h < 180 ) {rgb.g = t1; rgb.r = t2; rgb.b = t2 + t3; }
+ else if( h < 240 ) {rgb.b = t1; rgb.r = t2; rgb.g = t1 - t3; }
+ else if( h < 300 ) {rgb.b = t1; rgb.g = t2; rgb.r = t2 + t3; }
+ else if( h < 360 ) {rgb.r = t1; rgb.g = t2; rgb.b = t1 - t3; }
+ else { rgb.r = 0; rgb.g = 0; rgb.b = 0; }
+ }
+ return {
+ r: Math.round(rgb.r),
+ g: Math.round(rgb.g),
+ b: Math.round(rgb.b)
+ };
+ };
+
+ var rgb2hex = function(rgb) {
+ var hex = [
+ rgb.r.toString(16),
+ rgb.g.toString(16),
+ rgb.b.toString(16)
+ ];
+ $.each(hex, function(nr, val) {
+ if (val.length === 1) hex[nr] = '0' + val;
+ });
+ return hex.join('');
+ };
+
+ var hex2rgb = function(hex) {
+ hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16);
+
+ return {
+ r: hex >> 16,
+ g: (hex & 0x00FF00) >> 8,
+ b: (hex & 0x0000FF)
+ };
+ };
+
+ var rgb2hsb = function(rgb) {
+ var hsb = { h: 0, s: 0, b: 0 };
+ var min = Math.min(rgb.r, rgb.g, rgb.b);
+ var max = Math.max(rgb.r, rgb.g, rgb.b);
+ var delta = max - min;
+ hsb.b = max;
+ hsb.s = max !== 0 ? 255 * delta / max : 0;
+ if( hsb.s !== 0 ) {
+ if( rgb.r === max ) {
+ hsb.h = (rgb.g - rgb.b) / delta;
+ } else if( rgb.g === max ) {
+ hsb.h = 2 + (rgb.b - rgb.r) / delta;
+ } else {
+ hsb.h = 4 + (rgb.r - rgb.g) / delta;
+ }
+ } else {
+ hsb.h = -1;
+ }
+ hsb.h *= 60;
+ if( hsb.h < 0 ) {
+ hsb.h += 360;
+ }
+ hsb.s *= 100/255;
+ hsb.b *= 100/255;
+ return hsb;
+ };
+
+ var hex2hsb = function(hex) {
+ var hsb = rgb2hsb(hex2rgb(hex));
+ // Zero out hue marker for black, white, and grays (saturation === 0)
+ if( hsb.s === 0 ) hsb.h = 360;
+ return hsb;
+ };
+
+ var hsb2hex = function(hsb) {
+ return rgb2hex(hsb2rgb(hsb));
+ };
+
+
+ // Handle calls to $([selector]).miniColors()
+ switch(o) {
+
+ case 'readonly':
+
+ $(this).each( function() {
+ if( !$(this).hasClass('miniColors') ) return;
+ $(this).prop('readonly', data);
+ });
+
+ return $(this);
+
+ case 'disabled':
+
+ $(this).each( function() {
+ if( !$(this).hasClass('miniColors') ) return;
+ if( data ) {
+ disable($(this));
+ } else {
+ enable($(this));
+ }
+ });
+
+ return $(this);
+
+ case 'value':
+
+ // Getter
+ if( data === undefined ) {
+ if( !$(this).hasClass('miniColors') ) return;
+ var input = $(this),
+ hex = expandHex(input.val());
+ return hex ? '#' + convertCase(hex, input.data('letterCase')) : null;
+ }
+
+ // Setter
+ $(this).each( function() {
+ if( !$(this).hasClass('miniColors') ) return;
+ $(this).val(data);
+ setColorFromInput($(this));
+ });
+
+ return $(this);
+
+ case 'destroy':
+
+ $(this).each( function() {
+ if( !$(this).hasClass('miniColors') ) return;
+ destroy($(this));
+ });
+
+ return $(this);
+
+ default:
+
+ if( !o ) o = {};
+
+ $(this).each( function() {
+
+ // Must be called on an input element
+ if( $(this)[0].tagName.toLowerCase() !== 'input' ) return;
+
+ // If a trigger is present, the control was already created
+ if( $(this).data('trigger') ) return;
+
+ // Create the control
+ create($(this), o, data);
+
+ });
+
+ return $(this);
+
+ }
+
+ }
+
+ });
+
+})(jQuery); \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/editor/js/mapeditor.iefixes.js b/www/wiki/extensions/Maps/resources/editor/js/mapeditor.iefixes.js
new file mode 100644
index 00000000..a49e42fb
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/editor/js/mapeditor.iefixes.js
@@ -0,0 +1,28 @@
+if (!Array.prototype.indexOf)
+{
+ Array.prototype.indexOf = function(elt /*, from*/)
+ {
+ var len = this.length >>> 0;
+
+ var from = Number(arguments[1]) || 0;
+ from = (from < 0)
+ ? Math.ceil(from)
+ : Math.floor(from);
+ if (from < 0)
+ from += len;
+
+ for (; from < len; from++)
+ {
+ if (from in this &&
+ this[from] === elt)
+ return from;
+ }
+ return -1;
+ };
+}
+
+if(typeof String.prototype.trim !== 'function') {
+ String.prototype.trim = function() {
+ return this.replace(/^\s+|\s+$/g, '');
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/editor/js/mapeditor.js b/www/wiki/extensions/Maps/resources/editor/js/mapeditor.js
new file mode 100644
index 00000000..795fcc53
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/editor/js/mapeditor.js
@@ -0,0 +1,919 @@
+var mapEditor = {
+ __map:null,
+ __drawingManager:null,
+ __mapObjects:[],
+ __controlsDiv:null,
+ __options:{
+ canvas:'map-canvas',
+ onRightClick:function(){}
+ },
+ __mapObjectOptions: {
+ marker:{
+ draggable:true
+ },
+ polyline:{
+ strokeWeight:2,
+ strokeOpacity:1,
+ strokeColor:'#FF0000',
+ editable:true
+ },
+ circle:{
+ strokeWeight:2,
+ strokeOpacity:1,
+ strokeColor:'#FF0000',
+ fillColor:'#FF0000',
+ fillOpacity:0.5,
+ editable:true
+ },
+ rectangle:{
+ strokeWeight:2,
+ strokeOpacity:1,
+ strokeColor:'#FF0000',
+ fillColor:'#FF0000',
+ fillOpacity:0.5,
+ editable:true
+ },
+ polygon:{
+ strokeWeight:2,
+ strokeOpacity:1,
+ strokeColor:'#FF0000',
+ fillColor:'#FF0000',
+ fillOpacity:0.5,
+ editable:true
+ },
+ imageoverlay:{
+ strokeWeight:1,
+ strokeOpacity:0.5,
+ strokeColor:'#000',
+ fillOpacity:0.0,
+ editable:true
+ }
+ },
+ __mapParameters:{
+ mappingservice: {
+ values:mw.config.get( 'egMapsAvailableServices' )
+ },
+ copycoords: {
+ values:['on','off']
+ },
+ markercluster: {
+ values:['on','off']
+ },
+ searchmarkers:{
+ values:['title','all']
+ },
+ 'static':{
+ values:['on','off']
+ },
+ maxzoom: {
+ values:[]
+ },
+ minzoom: {
+ values:[]
+ },
+ zoom:{
+ values:[]
+ },
+ centre:{
+ values:[]
+ },
+ title:{
+ values:[]
+ },
+ label:{
+ values:[]
+ },
+ icon:{
+ values:[]
+ },
+ type:{
+ values:[]
+ },
+ types:{
+ values:[]
+ },
+ layers:{
+ values:[]
+ },
+ controls:{
+ values:[]
+ },
+ zoomstyle:{
+ values:['default','small','large']
+ },
+ typestyle:{
+ values:[]
+ },
+ autoinfowindows:{
+ values:['on','off']
+ },
+ kml:{
+ values:[]
+ },
+ gkml:{
+ values:[]
+ },
+ resizable:{
+ values:[]
+ },
+ tilt:{
+ values:[]
+ },
+ kmlrezoom:{
+ values:['on','off']
+ },
+ poi:{
+ values:['on','off']
+ },
+ visitedicon:{
+ values:[]
+ }
+ },
+ __addMapObject:function(o){
+ var idx = this.__mapObjects.indexOf(o);
+ if(idx === -1){
+ this.__mapObjects.push(o);
+ o.overlay.setMap(this.__map);
+ }
+ },
+ __removeMapObject:function(o){
+ var idx = this.__mapObjects.indexOf(o);
+ if(idx !== -1){
+ this.__mapObjects[idx].overlay.setMap(null);
+ this.__mapObjects.splice(idx,1);
+ }
+ },
+ __createOrUpdateImageOverlay:function(e,imageUrl){
+ //remove old image overlay if exists
+ this.__removeMapObject(e.imageoverlay);
+
+ //set to new type so it doesn't collide with rectangle
+ e.type = 'imageoverlaybounds';
+
+ //add map objects
+ this.__addMapObject(e);
+ var image = new google.maps.GroundOverlay(imageUrl, e.overlay.getBounds());
+ var imageOverlay = {
+ overlay:image,
+ type:'imageoverlay'
+ };
+ this.__addMapObject(imageOverlay);
+ e.imageoverlay = imageOverlay;
+ imageOverlay.metadata = e.metadata;
+
+ //register right click listener if not already done
+ if(e.rightClickListener !== true){
+ google.maps.event.addListener(e.overlay, 'rightclick', function () {
+ mapEditor.__options.onRightClick(e);
+ });
+ e.rightClickListener = true;
+ }
+
+ //register bounds change listener if not already done
+ if(e.boundsChangedListener !== true){
+ google.maps.event.addListener(e.overlay, 'bounds_changed', function () {
+ //destroy and recreate imageoverlay (due to no e.imageoverlay.setBounds() method)
+ mapEditor.__createOrUpdateImageOverlay(e, e.imageoverlay.overlay.getUrl());
+ });
+ e.boundsChangedListener = true;
+ }
+
+ return imageOverlay;
+ },
+ __initControls:function(){
+ //Create root controls element
+ var controlDiv = this.__controlsDiv = document.createElement('div');
+ controlDiv.setAttribute('class','mapeditor-controls');
+ controlDiv.index = 1;
+
+ this.__map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(controlDiv);
+ },
+ __initMap:function(){
+ var myOptions = {
+ center:new google.maps.LatLng(0, 0),
+ zoom:1,
+ mapTypeId:google.maps.MapTypeId.ROADMAP
+ };
+
+ this.__map = new google.maps.Map(document.getElementById(this.__options.canvas),myOptions);
+
+ //noinspection JSUnresolvedVariable
+ var drawingManager = this.__drawingManager = new google.maps.drawing.DrawingManager({
+ drawingMode:null,
+ drawingControl:true,
+ drawingControlOptions:{
+ position:google.maps.ControlPosition.TOP_CENTER
+ },
+ markerOptions:this.__mapObjectOptions.marker,
+ circleOptions:this.__mapObjectOptions.circle,
+ rectangleOptions:this.__mapObjectOptions.rectangle,
+ polygonOptions:this.__mapObjectOptions.polygon,
+ polylineOptions:this.__mapObjectOptions.polyline
+ });
+ drawingManager.setMap(this.__map);
+
+ google.maps.event.addListener(drawingManager, 'overlaycomplete', function (e) {
+ mapEditor.__addMapObject(e);
+ mapEditor.__registerRightClickListener(e);
+ google.maps.event.trigger(e.overlay, 'rightclick');
+ });
+ },
+ __readMapObjectOptionsFromMetadata:function(e){
+ var options = $.extend({},this.__mapObjectOptions[e.type]);
+ for(var key in e.metadata){
+ var data = e.metadata[key];
+ if(data.value.trim() !== ''){
+ options[data.name] = data.value;
+ }
+ }
+ try{
+ e.overlay.setOptions(options);
+ return true;
+ }catch(e){
+ return false;
+ }
+ },
+ __writeMetaDataToMapObject:function(e,metadata){
+ e.metadata = this.__arrayToObject(metadata);
+ },
+ __convertPositionalParametersToMetaData:function(positionalArray){
+ var positionalNames = [
+ 'title',
+ 'text',
+ 'strokeColor',
+ 'strokeOpacity',
+ 'strokeWeight',
+ 'fillColor',
+ 'fillOpacity',
+ 'showOnHover'
+ ];
+
+ if(positionalArray !== undefined && positionalArray.length > 0){
+ if(positionalArray[0].trim().indexOf('link:') === 0){
+ positionalNames.splice(0,1);
+ positionalNames[0] = 'link';
+ }
+ }
+
+ for(var x = 0; x < positionalArray.length; x++){
+ positionalArray[x] = {
+ name:positionalNames[x],
+ value:positionalArray[x].trim()
+ };
+ }
+ return this.__arrayToObject(positionalArray);
+ },
+ __arrayToObject:function(arr){
+ var o = {};
+ for (var i = 0; i < arr.length; ++i){
+ o[i] = arr[i];
+ }
+ return o;
+ },
+ __registerRightClickListener:function(e){
+ google.maps.event.addListener(e.overlay, 'rightclick', function () {
+ mapEditor.__options.onRightClick(e);
+ });
+ },
+ __generateWikiCode:function( separators ){
+ var code = separators.codeStart;
+
+ var markers = '';
+ var circles = '';
+ var polygons = '';
+ var lines = '';
+ var rectangles = '';
+ var imageoverlays = '';
+
+ for(var x = 0; x < this.__mapObjects.length; x++){
+ var mapObject = this.__mapObjects[x].overlay;
+ var mapObjectType = this.__mapObjects[x].type;
+ var mapObjectMeta = this.__mapObjects[x].metadata;
+
+ var metadata = '';
+ if(mapObjectMeta !== undefined){
+ var delimiterPosition = '';
+ for(var key in mapObjectMeta){
+ var data = mapObjectMeta[key];
+ delimiterPosition += delimiterPosition.length > 0 ? ' ~' : '~';
+ if(data.value !== ''){
+ if(data.name === 'link' && data.value.indexOf('link:') === -1){
+ data.value = 'link:'+data.value;
+ }
+ if(!(mapObjectType === 'imageoverlay' && data.name === 'image')){
+ metadata += delimiterPosition+data.value;
+ delimiterPosition = '';
+ }
+ }
+ }
+ }
+
+ var serializedData = mapObject+metadata;
+ if (mapObjectType === 'marker') {
+ markers += markers === '' ? serializedData : '; '+serializedData;
+ } else if (mapObjectType === 'circle') {
+ circles += circles === '' ? serializedData : '; '+serializedData;
+ } else if (mapObjectType === 'polygon') {
+ polygons += polygons === '' ? serializedData : '; '+serializedData;
+ } else if (mapObjectType === 'polyline') {
+ lines += lines === '' ? serializedData : '; '+serializedData;
+ } else if (mapObjectType === 'rectangle') {
+ rectangles += rectangles === '' ? serializedData : '; '+serializedData;
+ }else if(mapObjectType === 'imageoverlay'){
+ imageoverlays += imageoverlays === '' ? serializedData : '; '+serializedData;
+ }
+ }
+
+
+ code += markers !== '' ? markers : '';
+ code += circles !== '' ? separators.separator+'circles='+circles : '';
+ code += polygons !== '' ? separators.separator+'polygons='+polygons : '';
+ code += lines !== '' ? separators.separator+'lines='+lines : '';
+ code += rectangles !== '' ? separators.separator+'rectangles='+rectangles : '';
+ code += imageoverlays !== '' ? separators.separator+'imageoverlays='+imageoverlays : '';
+
+ //add map parameters
+ for(var param in this.__mapParameters){
+ var value = this.__mapParameters[param].value;
+ if(value === undefined || value === ''){
+ continue;
+ }
+ code += '\n|'+param+'='+value;
+ }
+
+ code += separators.codeEnd;
+ return code;
+ },
+ __importWikiCode:function(rawData){
+ var syntaxPattern = /^\{\{#display_map:[\s\S]*\}\}[\s\n]*$/i;
+ if(rawData.match(syntaxPattern) === null){ //NO MATCH
+ return false;
+ }else{
+ try{
+ var patterns = {
+ marker: /^\{\{#display_map:\s*(.*)/i,
+ polyline: /\|\s*lines=(.*)/i,
+ circle:/\|\s*circles=(.*)/i,
+ polygon:/\|\s*polygons=(.*)/i,
+ rectangle:/\|\s*rectangles=(.*)/i,
+ imageoverlay:/\|\s*imageoverlays=(.*)/i,
+ parameter:/\|\s*(.*)=(.*)/i
+ };
+ var mapObjects = [];
+ rawData = rawData.split('\n');
+ for(var j = 0; j < rawData.length; j++){
+ for (var key in patterns){
+ var match = rawData[j].match(patterns[key]);
+ if(match !== null && match[1].trim().length !== 0){
+ var isMapObject = false;
+ if(key !== 'parameter'){
+ var data = match[1].split(';');
+ for(var i = 0; i < data.length; i++){
+
+ var metadata = data[i].split('~');
+ metadata.splice(0,1);
+ if(metadata.length > 0){
+ data[i] = data[i].substring(0,data[i].indexOf('~'));
+ }
+ metadata = this.__convertPositionalParametersToMetaData(metadata);
+
+ var options = this.__mapObjectOptions[key];
+ var mapObject = null;
+ if (key === 'marker') {
+ var position = data[i].split(',');
+ //noinspection JSValidateTypes
+ options = $.extend({
+ position: new google.maps.LatLng(position[0],position[1])
+ },options);
+ mapObject = new google.maps.Marker(options);
+ } else if (key === 'circle') {
+ var parts = data[i].split(':');
+ var radius = parts[1];
+ var position = parts[0].split(',');
+ //noinspection JSValidateTypes
+ options = $.extend({
+ center: new google.maps.LatLng(position[0],position[1]),
+ radius: parseFloat(radius)
+ },options);
+ mapObject = new google.maps.Circle(options);
+ } else if (key === 'polygon') {
+ var paths = data[i].split(':');
+ for(var x = 0; x < paths.length; x++){
+ var position = paths[x].split(',');
+ paths[x] = new google.maps.LatLng(position[0],position[1]);
+ }
+ paths = new google.maps.MVCArray(paths);
+ //noinspection JSValidateTypes
+ options = $.extend({
+ paths: paths
+ },options);
+ mapObject = new google.maps.Polygon(options);
+ } else if (key === 'polyline') {
+ var paths = data[i].split(':');
+ for(var x = 0; x < paths.length; x++){
+ var position = paths[x].split(',');
+ paths[x] = new google.maps.LatLng(position[0],position[1]);
+ }
+ paths = new google.maps.MVCArray(paths);
+ //noinspection JSValidateTypes
+ options = $.extend({
+ path: paths
+ },options);
+ mapObject = new google.maps.Polyline(options);
+ } else if (key === 'rectangle') {
+ var parts = data[i].split(':');
+ var ne = parts[0].split(',');
+ var sw = parts[1].split(',');
+ sw = new google.maps.LatLng(sw[0],sw[1]);
+ ne = new google.maps.LatLng(ne[0],ne[1]);
+ //noinspection JSValidateTypes
+ options = $.extend({
+ bounds: new google.maps.LatLngBounds(sw,ne)
+ },options);
+ mapObject = new google.maps.Rectangle(options);
+ }else if (key === 'imageoverlay'){
+ var parts = data[i].split(':');
+ var ne = parts[0].split(',');
+ var sw = parts[1].split(',');
+ var imageUrl = parts[2];
+ sw = new google.maps.LatLng(sw[0],sw[1]);
+ ne = new google.maps.LatLng(ne[0],ne[1]);
+
+ options = $.extend({
+ bounds: new google.maps.LatLngBounds(sw,ne)
+ },options);
+ var rectangle = new google.maps.Rectangle(options);
+
+ //add image url as metadata entry
+ metadata.image = {
+ name:'image',value:imageUrl
+ };
+
+ mapObject = {
+ type:'imageoverlaybounds',
+ overlay:rectangle,
+ metadata:metadata
+ };
+
+ this.__createOrUpdateImageOverlay(mapObject,imageUrl);
+ this.__readMapObjectOptionsFromMetadata(mapObject);
+
+ }
+ if(mapObject !== null){
+ //imageoverlay needs special handling
+ if(key !== 'imageoverlay' ){
+ mapObject = {
+ type:key,
+ overlay:mapObject,
+ metadata:metadata
+ };
+
+ this.__registerRightClickListener(mapObject);
+ this.__addMapObject(mapObject);
+ this.__readMapObjectOptionsFromMetadata(mapObject);
+ }
+
+ isMapObject = true;
+
+ }
+ }
+ }else if(!isMapObject){
+ //handle global map parameters
+ if(this.__mapParameters[match[1]] === undefined){
+ this.__mapParameters[match[1]] = {};
+ }
+ this.__mapParameters[match[1]].value = match[2];
+ }
+ }
+ }
+ }
+ }catch(e){
+ console.log('An error occurred when parsing data');
+ return false;
+ }
+ return true;
+ }
+ },
+ addControlButton:function (text, onclick){
+ // Set CSS for the control border
+ var controlUI = $('<div class="mapeditor-control-element"></div>');
+ $(controlUI).click(function(){
+ onclick.call(this);
+ }).appendTo(this.__controlsDiv);
+
+ // Set CSS for the control interior
+ var controlText = $('<span class="mapeditor-control-text"></span>')
+ .text(text).appendTo(controlUI);
+ },
+ setup:function(o){
+ //extend options
+ $.extend(this.__options,o);
+
+ //Override tostring methods for wiki code generation
+ google.maps.LatLng.prototype.toString = function(){
+ return this.lat()+','+this.lng();
+ };
+
+ google.maps.Rectangle.prototype.toString = function(){
+ var bounds = this.getBounds();
+ var ne = bounds.getNorthEast();
+ var sw = bounds.getSouthWest();
+ return ne+':'+sw;
+ };
+
+ google.maps.Marker.prototype.toString = function(){
+ var position = this.getPosition();
+ return position.lat()+','+position.lng();
+ };
+
+ google.maps.Circle.prototype.toString = function(){
+ var center = this.getCenter();
+ var radius = this.getRadius();
+ return center.lat()+','+center.lng()+':'+radius;
+ };
+
+ google.maps.Polygon.prototype.toString = function(){
+ var polygons = '';
+ this.getPath().forEach(function(e){
+ polygons += ':'+e;
+ });
+ return polygons.substr(1);
+ };
+
+ google.maps.Polyline.prototype.toString = function(){
+ var lines = '';
+ this.getPath().forEach(function(e){
+ lines += ':'+e;
+ });
+ return lines.substr(1);
+ };
+
+ google.maps.GroundOverlay.prototype.toString = function(){
+ var bounds = this.getBounds();
+ var sw = bounds.getSouthWest();
+ var ne = bounds.getNorthEast();
+ return [ne,sw,this.getUrl()].join(':');
+ };
+
+ //initialize rest
+ this.__initMap();
+ this.__initControls();
+ }
+};
+
+$(document).ready(function(){
+
+
+ function openDialog(e){
+ if(e.metadata !== undefined){
+ for(var key in e.metadata){
+ var data = e.metadata[key];
+ $(this).find('form input[name="'+data.name+'"]').val(data.value);
+ }
+ }
+ var i18nButtons = {};
+ i18nButtons[mw.msg('mapeditor-done-button')] = function () {
+ var form = $(this).find('form');
+ form.find('.miniColors').each(function(){
+ if($(this).val() === '#'){
+ $(this).val('');
+ }
+ });
+ mapEditor.__writeMetaDataToMapObject(e,form.serializeArray());
+ $(this).dialog("close");
+ if(!mapEditor.__readMapObjectOptionsFromMetadata(e)){
+ alert(mw.msg('mapeditor-parser-error'));
+ }
+ };
+ i18nButtons[mw.msg('mapeditor-remove-button')] = function () {
+ mapEditor.__removeMapObject(e);
+ $(this).dialog("close");
+ };
+
+ this.dialog({
+ modal:true,
+ buttons:i18nButtons,
+ open:function(){
+ if(e.metadata !== undefined){
+ var isText = true;
+ for(var key in e.metadata){
+ var data = e.metadata[key];
+ if(data.name === 'link' && data.value.length > 0){
+ isText = false;
+ break;
+ }
+ }
+ //depending on existing metadata,
+ //show either form with title/text fields or just link field
+ if(isText){
+ $(this).find('input[value="text"]').trigger('click');
+ }else{
+ $(this).find('input[value="link"]').trigger('click');
+ }
+ }else{
+ //default trigger click on text radio button
+ $(this).find('input[value="text"]').trigger('click');
+ }
+
+
+ },
+ beforeClose:function(){
+ //reset the form
+ var form = $(this).find('form');
+ form[0].reset();
+ form.find('.opacity-data-holder').text(mw.msg('mapeditor-none-text'));
+ form.find('.ui-slider').slider('value',-1);
+ form.find('.miniColors').miniColors('value','#fff').val('');
+ }
+ });
+ }
+
+ function openImageOverlayDialog(e,callback){
+
+ var form = $('#imageoverlay-form');
+
+ var i18nButtons = {};
+ i18nButtons[mw.msg('mapeditor-done-button')] = function(){
+ var imageUrl = $(this).find('input[name="image"]').val();
+ mapEditor.__createOrUpdateImageOverlay(e,imageUrl);
+
+ var metadata = form.find('form').serializeArray();
+ mapEditor.__writeMetaDataToMapObject(e,metadata);
+ mapEditor.__writeMetaDataToMapObject(e.imageoverlay,metadata);
+
+ form.dialog("close");
+ };
+
+ i18nButtons[mw.msg('mapeditor-remove-button')] = function () {
+ mapEditor.__removeMapObject(e.imageoverlay);
+ e.imageoverlay = undefined;
+ form.dialog("close");
+ };
+
+ form.dialog({
+ modal:true,
+ buttons:i18nButtons,
+ open:function(){
+ //restore data from previous edits
+ if(e.metadata !== undefined){
+ var isText = true;
+ for(var key in e.metadata){
+ var data = e.metadata[key];
+ if(data.name === 'link' && data.value.length > 0){
+ isText = false;
+ }
+ form.find('form input[name="'+data.name+'"]').val(data.value);
+ }
+ //depending on existing metadata,
+ //show either form with title/text fields or just link field
+ if(isText){
+ form.find('input[value="text"]').trigger('click');
+ }else{
+ form.find('input[value="link"]').trigger('click');
+ }
+ }else{
+ //default trigger click on text radio button
+ form.find('input[value="text"]').trigger('click');
+ }
+ },
+ beforeClose:function(){
+ mapEditor.__drawingManager.setMap(mapEditor.__map); //re-enable standard drawing manager
+ if(e.imageoverlay === undefined){
+ mapEditor.__removeMapObject(e);
+ }
+
+ //reset the form
+ var formElement = form.find('form');
+ formElement[0].reset();
+
+ if(callback !== undefined && typeof(callback) === 'function'){
+ callback();
+ }
+
+ }
+ });
+ }
+
+ mapEditor.setup({
+ onRightClick:function(e){
+ if (e.type === 'marker') {
+ openDialog.call($('#marker-form'),e);
+ } else if (e.type === 'circle') {
+ openDialog.call($('#fillable-form'),e);
+ } else if (e.type === 'polygon') {
+ openDialog.call($('#polygon-form'),e);
+ } else if (e.type === 'polyline') {
+ openDialog.call($('#strokable-form'),e);
+ } else if (e.type === 'rectangle') {
+ openDialog.call($('#fillable-form'),e);
+ } else if (e.type === 'imageoverlaybounds') {
+ openImageOverlayDialog(e);
+ }
+ }
+ });
+
+ //add custom controls
+ if( $('#map-canvas').attr('context') != 'forminput' ) { //for Special:MapEditor
+ var editorMarkers = {
+ 'codeStart' : '{{#display_map: ',
+ 'separator' : '\n|',
+ 'codeEnd' : '\n}}\n'
+ };
+ mapEditor.addControlButton(mw.msg('mapeditor-export-button'),function(){
+ var code = mapEditor.__generateWikiCode( editorMarkers );
+ if(navigator.appName == 'Microsoft Internet Explorer'){
+ //if IE replace /n with /r/n so it is displayed properly
+ code = code.split('\n').join('\r\n');
+ }
+ $('#code-output').text(code);
+ $('#code-output-container').dialog({
+ modal:true,
+ width:'80%',
+ open:function(){
+ $('#code-output').focus();
+ }
+ });
+ });
+
+ mapEditor.addControlButton(mw.msg('mapeditor-import-button'), function(){
+ var i18nButtons = {};
+ i18nButtons[mw.msg('mapeditor-import-button2')] = function () {
+ var data = $('#code-input').val();
+ if(mapEditor.__importWikiCode(data)){
+ $(this).dialog('close');
+ }else{
+ alert('Could not parse input! make sure the input has the same structure as exported wiki code');
+ }
+ };
+ $('#code-input-container').dialog({
+ modal:true,
+ width:'80%',
+ buttons: i18nButtons
+ });
+ });
+ } else { //for form input
+ var forminputMarkers = {
+ 'codeStart' : '',
+ 'separator' : '',
+ 'codeEnd' : ''
+ };
+ mapEditor.addControlButton(mw.msg('mapeditor-export-button'),function(){
+ var code = mapEditor.__generateWikiCode( forminputMarkers );
+ if(navigator.appName == 'Microsoft Internet Explorer'){
+ //if IE replace /n with /r/n so it is displayed properly
+ code = code.split('\n').join('\r\n');
+ }
+ $('#map-polygon').text(code);
+ });
+ }
+
+ mapEditor.addControlButton(mw.msg('mapeditor-mapparam-button'), function(){
+ var i18nButtons = {};
+ i18nButtons[mw.msg('mapeditor-done-button')] = function(){
+ var data = $(this).find('form input').not('select').serializeArray();
+ for(var x = 0; x < data.length; x++){
+ mapEditor.__mapParameters[data[x].name].value = data[x].value;
+ }
+ $(this).dialog('close');
+ };
+ $('#map-parameter-form').dialog({
+ modal:true,
+ width: 500,
+ buttons:i18nButtons
+ });
+ });
+
+ mapEditor.addControlButton(mw.msg('mapeditor-clear-button'), function(){
+ while(mapEditor.__mapObjects.length > 0){
+ var mapObj = mapEditor.__mapObjects.pop();
+ mapObj.overlay.setMap(null);
+ }
+ for(var param in mapEditor.__mapParameters){
+ mapEditor.__mapParameters[param].value = undefined;
+ }
+ });
+
+ mapEditor.addControlButton(mw.msg('mapeditor-imageoverlay-button'), function(){
+ var button = $(this);
+ if(button.data('clicked') === true){
+ return; //already clicked, disregard this click
+ }else{
+ button.data('clicked',true);
+ }
+
+ mapEditor.__drawingManager.setMap(null); //disable current drawing manager
+
+ var drawingManager = new google.maps.drawing.DrawingManager({
+ drawingMode:google.maps.drawing.OverlayType.RECTANGLE,
+ drawingControl:false,
+ rectangleOptions:mapEditor.__mapObjectOptions.imageoverlay
+ });
+ drawingManager.setMap(mapEditor.__map);
+
+ google.maps.event.addListener(drawingManager, 'overlaycomplete', function (e) {
+ mapEditor.__addMapObject(e); //add if it doesn't already exist.
+
+ drawingManager.setMap(null);
+
+ openImageOverlayDialog(e,function(){
+ //re-enable button
+ button.data('clicked',false);
+ });
+
+ });
+
+ });
+
+
+
+ //init map parameters
+ var formselect = $('#map-parameter-form select[name="key"]');
+ formselect.on('change',function(){
+ var option = $(this);
+ var key = option.val();
+ var value = mapEditor.__mapParameters[key].value;
+ if(value === undefined){
+ value = '';
+ }
+
+ var parent = option.parent();
+ var input = $('<input type="text" name="'+key+'" value="'+value+'"></input>');
+ var removeButton = $('<a href="#">[x]</a>');
+ var option2 = option.clone(true);
+ var container = $('<div></div>');
+
+ option2.find('option[value="'+key+'"]').remove();
+ option.attr('disabled',true);
+ removeButton.on('click',function(){
+ var removedKey = $(this).prevAll('select').val();
+ var activeSelect = $(this).parent().parent().find('select').not('select[disabled="disabled"]');
+ var option = $('<option value="'+removedKey+'">'+removedKey+'</option>');
+ activeSelect.children().first().after(option);
+ $(this).parent().remove();
+ mapEditor.__mapParameters[key].value = undefined;
+ return false;
+ });
+
+ parent.append(input);
+ parent.append(removeButton);
+ parent.parent().append(container);
+ container.append(option2);
+
+ input.autocomplete({
+ source: mapEditor.__mapParameters[key].values,
+ minLength: 0,
+ autoFocus: true
+ });
+ input.autocomplete('search','');
+ });
+
+ for(var parameter in mapEditor.__mapParameters){
+ var option = $('<option value="'+parameter+'">'+parameter+'</option>');
+ formselect.append(option);
+ }
+
+ //hide link input initially
+ $('input[name="link"]').attr('disabled',true).hide().prev().hide();
+
+ //init text/link switcher
+ $('input[name="switch"]').on('click',function(){
+ if($(this).val() === 'link'){
+ $(this).parent().next().find('input[name="title"],input[name="text"]').attr('disabled',true).hide().prev().hide();
+ $(this).parent().next().find('input[name="link"]').attr('disabled',false).show().prev().show();
+ }else{
+ $(this).parent().next().find('input[name="title"],input[name="text"]').attr('disabled',false).show().prev().show();
+ $(this).parent().next().find('input[name="link"]').attr('disabled',true).hide().prev().hide();
+ }
+ });
+
+ //init enter keypress to press done on dialog.
+ $('.mapeditor-dialog').on('keypress',function(e){
+ if(e.keyCode === 13){
+ $(this).dialog('option','buttons').Done.call(this);
+ }
+ });
+
+ //init sliders
+ $('input[name="strokeOpacity"],input[name="fillOpacity"]').each(function(){
+ var input = $(this);
+ var dataHolder = $('<span class="opacity-data-holder">'+mw.msg('mapeditor-none-text')+'</span>');
+ dataHolder.css({fontSize: 12});
+ var slider = $('<div></div>').slider({
+ min: -1,
+ slide: function(event, ui){
+ if(ui.value === -1){
+ input.val('');
+ dataHolder.text(mw.msg('mapeditor-none-text'));
+ }else{
+ input.val( ui.value/100 );
+ dataHolder.text(ui.value+'%');
+ }
+ }
+ });
+ input.before(dataHolder);
+ input.after(slider);
+ });
+
+ //init color pickers
+ $('input[name="strokeColor"],input[name="fillColor"]').miniColors().miniColors('value','').val('');
+});
diff --git a/www/wiki/extensions/Maps/resources/leaflet/cluster/m1.png b/www/wiki/extensions/Maps/resources/leaflet/cluster/m1.png
new file mode 100644
index 00000000..3346508f
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/cluster/m1.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/cluster/m2.png b/www/wiki/extensions/Maps/resources/leaflet/cluster/m2.png
new file mode 100644
index 00000000..64786829
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/cluster/m2.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/cluster/m3.png b/www/wiki/extensions/Maps/resources/leaflet/cluster/m3.png
new file mode 100644
index 00000000..772ed91a
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/cluster/m3.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/cluster/m4.png b/www/wiki/extensions/Maps/resources/leaflet/cluster/m4.png
new file mode 100644
index 00000000..eb33557a
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/cluster/m4.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/cluster/m5.png b/www/wiki/extensions/Maps/resources/leaflet/cluster/m5.png
new file mode 100644
index 00000000..6ca9c490
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/cluster/m5.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/ext.maps.leaflet.js b/www/wiki/extensions/Maps/resources/leaflet/ext.maps.leaflet.js
new file mode 100644
index 00000000..087d4acf
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/ext.maps.leaflet.js
@@ -0,0 +1,4 @@
+
+mediaWiki.loader.using( [ 'ext.maps.leaflet' ] ).done( function () {
+ ( new maps.services( jQuery( document ) ) ).leaflet();
+} ); \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/leaflet/ext.sm.leafletajax.js b/www/wiki/extensions/Maps/resources/leaflet/ext.sm.leafletajax.js
new file mode 100644
index 00000000..fb9f7341
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/ext.sm.leafletajax.js
@@ -0,0 +1,44 @@
+/**
+ * JavaScript for Leaflet in the Semantic Maps extension.
+ * @see https://www.mediawiki.org/wiki/Extension:Semantic_Maps
+ *
+ * @licence GNU GPL v2+
+ * @author Peter Grassberger < petertheone@gmail.com >
+ */
+
+
+(function( $, sm ) {
+ var ajaxRequest = null;
+
+ var mapEvents = ['dragend', 'zoomend'];
+
+ $( document ).ready( function() {
+ // todo: find a way to remove setTimeout.
+ setTimeout( function() {
+ $( window.maps.leafletList ).each( function( index, map ) {
+ if( !map.options.ajaxquery || !map.options.ajaxcoordproperty ) {
+ return;
+ }
+ map.map.on( mapEvents.join( ' ' ), function() {
+ var bounds = map.map.getBounds();
+ var query = sm.buildQueryString(
+ decodeURIComponent( map.options.ajaxquery.replace( /\+/g, ' ' ) ),
+ map.options.ajaxcoordproperty,
+ bounds.getNorthEast().lat,
+ bounds.getNorthEast().lng,
+ bounds.getSouthWest().lat,
+ bounds.getSouthWest().lng
+ );
+
+ if( ajaxRequest !== null ) {
+ ajaxRequest.abort();
+ }
+ ajaxRequest = sm.ajaxUpdateMarker( map, query, map.options.icon ).done( function() {
+ map.createMarkerCluster();
+ ajaxRequest = null;
+ } );
+ } );
+ } );
+ }, 1000 );
+ } );
+})( window.jQuery, window.sm );
diff --git a/www/wiki/extensions/Maps/resources/leaflet/jquery.leaflet.js b/www/wiki/extensions/Maps/resources/leaflet/jquery.leaflet.js
new file mode 100644
index 00000000..3483ec3a
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/jquery.leaflet.js
@@ -0,0 +1,434 @@
+/**
+ * JavaScript for Leaflet in the Maps extension.
+ * @see https://www.mediawiki.org/wiki/Extension:Maps
+ *
+ * @author Pavel Astakhov < pastakhov@yandex.ru >
+ * @author Peter Grassberger < petertheone@gmail.com >
+ */
+(function ($, mw, L, MQ) {
+ $.fn.leafletmaps = function ( options ) {
+ var _this = this;
+ this.map = null;
+ this.options = options;
+ this.markers = [];
+ this.markercluster = null;
+ var apikeys = mw.config.get('egMapsLeafletLayersApiKeys') ;
+
+ /**
+ * array point of all map elements (markers, lines, polygons, etc.)
+ * for map fit
+ */
+ this.points = [];
+
+ /**
+ * Creates a new marker with the provided data and returns it.
+ * @param {Object} properties Contains the fields lat, lon, title, text and icon
+ * @return {L.Marker}
+ */
+ this.createMarker = function (properties) {
+ this.points.push( new L.LatLng(properties.lat, properties.lon) );
+
+ var markerOptions = {
+ title:properties.title
+ };
+
+ var marker = L.marker( [properties.lat, properties.lon], markerOptions );
+
+ if (properties.hasOwnProperty('icon') && properties.icon !== '') {
+ marker.setOpacity(0);
+
+ var img = new Image();
+ img.onload = function() {
+ var icon = new L.Icon({
+ iconUrl: properties.icon,
+ iconSize: [ img.width, img.height ],
+ iconAnchor: [ img.width / 2, img.height ],
+ popupAnchor: [ -img.width % 2, -img.height*2/3 ]
+ });
+
+ marker.setIcon(icon);
+ marker.setOpacity(1);
+ };
+ img.src = properties.icon;
+ }
+
+ if( properties.hasOwnProperty('text') && properties.text.length > 0 ) {
+ marker.bindPopup( properties.text );
+ }
+
+ if ( options.copycoords ) {
+ marker.on(
+ 'contextmenu',
+ function( e ) {
+ prompt(mw.msg('maps-copycoords-prompt'), e.latlng.lat + ',' + e.latlng.lng);
+ }
+ );
+ }
+
+ return marker;
+ };
+
+ /**
+ * Creates a new marker with the provided data, adds it to the map
+ * and returns it.
+ * @param {Object} properties Contains the fields lat, lon, title, text and icon
+ * @return {L.Marker}
+ */
+ this.addMarker = function (properties) {
+ var marker = this.createMarker(properties);
+ if (!this.options.markercluster) {
+ marker.addTo( this.map );
+ }
+ this.markers.push( marker );
+ return marker;
+ };
+
+ this.removeMarker = function (marker) {
+ this.map.removeLayer(marker);
+ this.points = [];
+ this.markers = this.markers.filter(function(object) {
+ return object !== marker;
+ });
+ };
+
+ this.removeMarkers = function () {
+ if (this.markercluster) {
+ this.map.removeLayer(this.markercluster);
+ this.markercluster = null;
+ }
+ var map = this.map;
+ $.each(this.markers, function(index, marker) {
+ map.removeLayer(marker);
+ });
+
+ this.points = [];
+ this.markers = [];
+ };
+
+ this.addLine = function (properties) {
+ var options = {
+ color: properties.strokeColor,
+ weight:properties.strokeWeight,
+ opacity:properties.strokeOpacity
+ };
+
+ var latlngs = [];
+ for (var x = 0; x < properties.pos.length; x++) {
+ latlngs.push([properties.pos[x].lat, properties.pos[x].lon]);
+ this.points.push( new L.LatLng(properties.pos[x].lat, properties.pos[x].lon) );
+ }
+
+ var line = L.polyline(latlngs, options).addTo(this.map);
+
+ if( properties.hasOwnProperty('text') && properties.text.trim().length > 0 ) {
+ line.bindPopup( properties.text );
+ }
+ };
+
+ this.addPolygon = function (properties) {
+ properties.pos.forEach(function(position) {
+ _this.points.push( new L.LatLng(position.lat, position.lon) );
+ });
+
+ var polygon = L.polygon(
+ properties.pos.map(function(position) {
+ return [position.lat, position.lon];
+ }),
+ {
+ color: properties.strokeColor,
+ weight:properties.strokeWeight,
+ opacity:properties.strokeOpacity,
+ fillColor:properties.fillColor,
+ fillOpacity:properties.fillOpacity
+ }
+ );
+
+ polygon.addTo(this.map);
+
+ if( properties.hasOwnProperty('text') && properties.text.trim().length > 0 ) {
+ console.log(properties.text);
+ polygon.bindPopup( properties.text );
+ }
+ };
+
+ this.addCircle = function (properties) {
+ var circle = L.circle(
+ [properties.centre.lat, properties.centre.lon],
+ {
+ radius: properties.radius,
+ color: properties.strokeColor,
+ weight:properties.strokeWeight,
+ opacity:properties.strokeOpacity,
+ fillColor:properties.fillColor,
+ fillOpacity:properties.fillOpacity,
+ }
+ ).addTo(this.map);
+
+ this.points.push( new L.LatLng(properties.centre.lat, properties.centre.lon) );
+
+ if( properties.hasOwnProperty('text') && properties.text.trim().length > 0 ) {
+ circle.bindPopup( properties.text );
+ }
+ };
+
+ this.addRectangle = function (properties) {
+ this.points.push( new L.LatLng(properties.sw.lat, properties.sw.lon) );
+ this.points.push( new L.LatLng(properties.ne.lat, properties.ne.lon) );
+
+ var options = {
+ color: properties.strokeColor,
+ weight:properties.strokeWeight,
+ opacity:properties.strokeOpacity,
+ fillColor:properties.fillColor,
+ fillOpacity:properties.fillOpacity
+ };
+
+ var bounds = [[properties.sw.lat, properties.sw.lon], [properties.ne.lat, properties.ne.lon]];
+
+ var rectangle = L.rectangle( bounds, options ).addTo(this.map);
+
+ if( properties.hasOwnProperty('text') && properties.text.trim().length > 0 ) {
+ rectangle.bindPopup( properties.text );
+ }
+ };
+
+ this.createMarkerCluster = function () {
+ if ( !options.markercluster ) {
+ return;
+ }
+ var markers = this.markers;
+
+ var markercluster = new L.MarkerClusterGroup({
+ maxClusterRadius: options.clustermaxradius,
+ disableClusteringAtZoom: options.clustermaxzoom + 1,
+ zoomToBoundsOnClick: options.clusterzoomonclick,
+ spiderfyOnMaxZoom: options.clusterspiderfy,
+ iconCreateFunction: function(cluster) {
+ var childCount = cluster.getChildCount();
+
+ var imagePath = mw.config.get( 'egMapsScriptPath' ) + '/resources/leaflet/cluster/';
+
+ var styles = [
+ {
+ iconUrl: imagePath + 'm1.png',
+ iconSize: [53, 52]
+ },
+ {
+ iconUrl: imagePath + 'm2.png',
+ iconSize: [56, 55]
+ },
+ {
+ iconUrl: imagePath + 'm3.png',
+ iconSize: [66, 65]
+ },
+ {
+ iconUrl: imagePath + 'm4.png',
+ iconSize: [78, 77]
+ },
+ {
+ iconUrl: imagePath + 'm5.png',
+ iconSize: [90, 89]
+ }
+ ];
+
+ var index = 0;
+ var dv = childCount;
+ while (dv !== 0) {
+ dv = parseInt(dv / 10, 10);
+ index++;
+ }
+ var index = Math.min(index, styles.length);
+ index = Math.max(0, index - 1);
+ index = Math.min(styles.length - 1, index);
+ var style = styles[index];
+
+ return new L.divIcon({
+ iconSize: style.iconSize,
+ className: '',
+ html: '<img style="' +
+ '" src="' + style.iconUrl + '" />' +
+ '<span style="' +
+ 'position: absolute; font-size: 11px; font-weight: bold; text-align: center; ' +
+ 'top: 0; left: 0; ' +
+ 'line-height: ' + style.iconSize[1] + 'px;' +
+ 'width: ' + style.iconSize[0] + 'px;' +
+ '">' + childCount + '</span>'
+ });
+ }
+ });
+ $.each(this.markers, function(index, marker) {
+ markercluster.addLayer(marker);
+ });
+ if (this.markercluster) {
+ this.map.removeLayer(this.markercluster);
+ this.markercluster = null;
+ }
+ this.map.addLayer(markercluster);
+ this.markercluster = markercluster;
+ };
+
+ this.addGeoJson = function(options) {
+ if (options.geojson !== '') {
+ var geoJson = options.geojson;
+ var geoJsonLayer = L.geoJSON( geoJson ).addTo( this.map );
+
+ this.points.push( geoJsonLayer.getBounds().getNorthEast() );
+ this.points.push( geoJsonLayer.getBounds().getSouthWest() );
+ }
+ };
+
+ this.setup = function () {
+
+ var mapOptions = {};
+ if (options.minzoom !== false ) mapOptions.minZoom = options.minzoom;
+ if (options.maxzoom !== false ) mapOptions.maxZoom = options.maxzoom;
+
+ if (options.enablefullscreen) {
+ mapOptions.fullscreenControl = true;
+ mapOptions.fullscreenControlOptions= {
+ position: 'topleft'
+ };
+ }
+
+ mapOptions.scrollWheelZoom = options.scrollwheelzoom;
+
+ if (options.static) {
+ mapOptions.scrollWheelZoom = false;
+ mapOptions.doubleClickZoom = false;
+ mapOptions.touchZoom = false;
+ mapOptions.boxZoom = false;
+ mapOptions.tap = false;
+ mapOptions.keyboard = false;
+ mapOptions.zoomControl = false;
+ mapOptions.dragging = false;
+ }
+
+ var map = L.map( this.get(0), mapOptions ).fitWorld();
+ this.map = map;
+
+ var layers = {};
+ $.each(options.layers.reverse(), function(index, layerName) {
+ var options = {} ;
+ var providerName = layerName.split('.')[0] ;
+ if (apikeys.hasOwnProperty(providerName) && apikeys[providerName] !== '') {
+ options.apikey = apikeys[providerName] ;
+ }
+ if (layerName === 'MapQuestOpen') {
+ layers[layerName] = new MQ.TileLayer().addTo(map);
+ } else {
+ layers[layerName] = new L.tileLayer.provider(layerName,options).addTo(map);
+ }
+ });
+
+ var overlaylayers = {};
+ $.each(options.overlaylayers, function(index, overlaylayerName) {
+ overlaylayers[overlaylayerName] = new L.tileLayer.provider(overlaylayerName).addTo(_this.map);
+ });
+
+ if (options.layers.length > 1) {
+ L.control.layers(layers, overlaylayers).addTo(map);
+ }
+
+ if (options.resizable) {
+ //TODO: Fix moving map when resized
+ _this.resizable();
+ }
+
+ if (!options.locations) {
+ options.locations = [];
+ }
+
+ // Add the markers.
+ for (var i = options.locations.length - 1; i >= 0; i--) {
+ this.addMarker(options.locations[i]);
+ }
+
+ // Add markercluster
+ if (options.markercluster) {
+ this.createMarkerCluster();
+ }
+
+ // Add lines
+ if (options.lines) {
+ for (var i = 0; i < options.lines.length; i++) {
+ this.addLine(options.lines[i]);
+ }
+ }
+
+ // Add polygons
+ if (options.polygons) {
+ for (var i = 0; i < options.polygons.length; i++) {
+ this.addPolygon(options.polygons[i]);
+ }
+ }
+
+ // Add circles
+ if (options.circles) {
+ for (var i = 0; i < options.circles.length; i++) {
+ this.addCircle(options.circles[i]);
+ }
+ }
+
+ // Add rectangles
+ if (options.rectangles) {
+ for (var i = 0; i < options.rectangles.length; i++) {
+ this.addRectangle(options.rectangles[i]);
+ }
+ }
+
+ this.addGeoJson(options);
+
+ // Set map position (centre and zoom)
+ var centre;
+ if (options.centre === false) {
+ switch ( this.points.length ) {
+ case 0:
+ centre = new L.LatLng(0, 0);
+ break;
+ case 1:
+ centre = this.points[0];
+ break;
+ default:
+ var bounds = new L.LatLngBounds( this.points );
+ if (options.zoom === false) {
+ map.fitBounds( bounds );
+ centre = false;
+ } else {
+ centre = bounds.getCenter();
+ }
+ break;
+ }
+ this.points = [];
+ } else {
+ centre = new L.LatLng(options.centre.lat, options.centre.lon);
+ }
+ if(centre) {
+ map.setView( centre, options.zoom !== false ? options.zoom : options.defzoom );
+ }
+ };
+
+ this.getDependencies = function ( options ) {
+ var dependencies = [];
+ if (options.layers !== ['MapQuestOpen'] || options.overlaylayers.length > 0) {
+ dependencies.push( 'ext.maps.leaflet.providers' );
+ }
+ if (options.enablefullscreen) {
+ dependencies.push( 'ext.maps.leaflet.fullscreen' );
+ }
+ if (options.resizable) {
+ dependencies.push( 'ext.maps.resizable' );
+ }
+ if (options.markercluster) {
+ dependencies.push( 'ext.maps.leaflet.markercluster' );
+ }
+ return dependencies;
+ };
+
+ mw.loader.using( this.getDependencies( options ) ).then( function() {
+ _this.setup();
+ } );
+
+ return this;
+
+ };
+})(jQuery, window.mediaWiki, L, window.MQ);
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet-providers/leaflet-providers.js b/www/wiki/extensions/Maps/resources/leaflet/leaflet-providers/leaflet-providers.js
new file mode 100644
index 00000000..5437f1f1
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet-providers/leaflet-providers.js
@@ -0,0 +1,774 @@
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['leaflet'], factory);
+ } else if (typeof modules === 'object' && module.exports) {
+ // define a Common JS module that relies on 'leaflet'
+ module.exports = factory(require('leaflet'));
+ } else {
+ // Assume Leaflet is loaded into global object L already
+ factory(L);
+ }
+}(this, function (L) {
+ 'use strict';
+
+ L.TileLayer.Provider = L.TileLayer.extend({
+ initialize: function (arg, options) {
+ var providers = L.TileLayer.Provider.providers;
+
+ var parts = arg.split('.');
+
+ var providerName = parts[0];
+ var variantName = parts[1];
+
+ if (!providers[providerName]) {
+ throw 'No such provider (' + providerName + ')';
+ }
+
+ var provider = {
+ url: providers[providerName].url,
+ options: providers[providerName].options
+ };
+
+ // overwrite values in provider from variant.
+ if (variantName && 'variants' in providers[providerName]) {
+ if (!(variantName in providers[providerName].variants)) {
+ throw 'No such variant of ' + providerName + ' (' + variantName + ')';
+ }
+ var variant = providers[providerName].variants[variantName];
+ var variantOptions;
+ if (typeof variant === 'string') {
+ variantOptions = {
+ variant: variant
+ };
+ } else {
+ variantOptions = variant.options;
+ }
+ provider = {
+ url: variant.url || provider.url,
+ options: L.Util.extend({}, provider.options, variantOptions)
+ };
+ }
+
+ // replace attribution placeholders with their values from toplevel provider attribution,
+ // recursively
+ var attributionReplacer = function (attr) {
+ if (attr.indexOf('{attribution.') === -1) {
+ return attr;
+ }
+ return attr.replace(/\{attribution.(\w*)\}/,
+ function (match, attributionName) {
+ return attributionReplacer(providers[attributionName].options.attribution);
+ }
+ );
+ };
+ provider.options.attribution = attributionReplacer(provider.options.attribution);
+
+ // Compute final options combining provider options with any user overrides
+ var layerOpts = L.Util.extend({}, provider.options, options);
+ L.TileLayer.prototype.initialize.call(this, provider.url, layerOpts);
+ }
+ });
+
+ /**
+ * Definition of providers.
+ * see http://leafletjs.com/reference.html#tilelayer for options in the options map.
+ */
+
+ L.TileLayer.Provider.providers = {
+ OpenStreetMap: {
+ url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 19,
+ attribution:
+ '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
+ },
+ variants: {
+ Mapnik: {},
+ BlackAndWhite: {
+ url: 'http://{s}.tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 18
+ }
+ },
+ DE: {
+ url: 'https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 18
+ }
+ },
+ CH: {
+ url: 'https://tile.osm.ch/switzerland/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 18,
+ bounds: [[45, 5], [48, 11]]
+ }
+ },
+ France: {
+ url: 'https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 20,
+ attribution: '&copy; Openstreetmap France | {attribution.OpenStreetMap}'
+ }
+ },
+ HOT: {
+ url: 'https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png',
+ options: {
+ attribution: '{attribution.OpenStreetMap}, Tiles courtesy of <a href="http://hot.openstreetmap.org/" target="_blank">Humanitarian OpenStreetMap Team</a>'
+ }
+ },
+ BZH: {
+ url: 'https://tile.openstreetmap.bzh/br/{z}/{x}/{y}.png',
+ options: {
+ attribution: '{attribution.OpenStreetMap}, Tiles courtesy of <a href="http://www.openstreetmap.bzh/" target="_blank">Breton OpenStreetMap Team</a>',
+ bounds: [[46.2, -5.5], [50, 0.7]]
+ }
+ }
+ }
+ },
+ OpenInfraMap: {
+ url: 'https://tiles-{s}.openinframap.org/{variant}/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 18,
+ attribution:
+ '{attribution.OpenStreetMap}, <a href="http://www.openinframap.org/about.html">About OpenInfraMap</a>'
+ },
+ variants: {
+ Power: 'power',
+ Telecom: 'telecoms',
+ Petroleum: 'petroleum',
+ Water: 'water'
+ }
+ },
+ OpenSeaMap: {
+ url: 'https://tiles.openseamap.org/seamark/{z}/{x}/{y}.png',
+ options: {
+ attribution: 'Map data: &copy; <a href="http://www.openseamap.org">OpenSeaMap</a> contributors'
+ }
+ },
+ OpenPtMap: {
+ url: 'http://openptmap.org/tiles/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 17,
+ attribution: 'Map data: &copy; <a href="http://www.openptmap.org">OpenPtMap</a> contributors'
+ }
+ },
+ OpenTopoMap: {
+ url: 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 17,
+ attribution: 'Map data: {attribution.OpenStreetMap}, <a href="http://viewfinderpanoramas.org">SRTM</a> | Map style: &copy; <a href="https://opentopomap.org">OpenTopoMap</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)'
+ }
+ },
+ OpenRailwayMap: {
+ url: 'https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 19,
+ attribution: 'Map data: {attribution.OpenStreetMap} | Map style: &copy; <a href="https://www.OpenRailwayMap.org">OpenRailwayMap</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)'
+ }
+ },
+ OpenFireMap: {
+ url: 'http://openfiremap.org/hytiles/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 19,
+ attribution: 'Map data: {attribution.OpenStreetMap} | Map style: &copy; <a href="http://www.openfiremap.org">OpenFireMap</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)'
+ }
+ },
+ SafeCast: {
+ url: 'https://s3.amazonaws.com/te512.safecast.org/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 16,
+ attribution: 'Map data: {attribution.OpenStreetMap} | Map style: &copy; <a href="https://blog.safecast.org/about/">SafeCast</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)'
+ }
+ },
+ Thunderforest: {
+ url: 'https://{s}.tile.thunderforest.com/{variant}/{z}/{x}/{y}.png?apikey={apikey}',
+ options: {
+ attribution:
+ '&copy; <a href="http://www.thunderforest.com/">Thunderforest</a>, {attribution.OpenStreetMap}',
+ variant: 'cycle',
+ apikey: '<insert your api key here>',
+ maxZoom: 22
+ },
+ variants: {
+ OpenCycleMap: 'cycle',
+ Transport: {
+ options: {
+ variant: 'transport'
+ }
+ },
+ TransportDark: {
+ options: {
+ variant: 'transport-dark'
+ }
+ },
+ SpinalMap: {
+ options: {
+ variant: 'spinal-map'
+ }
+ },
+ Landscape: 'landscape',
+ Outdoors: 'outdoors',
+ Pioneer: 'pioneer'
+ }
+ },
+ OpenMapSurfer: {
+ url: 'https://korona.geog.uni-heidelberg.de/tiles/{variant}/x={x}&y={y}&z={z}',
+ options: {
+ maxZoom: 20,
+ variant: 'roads',
+ attribution: 'Imagery from <a href="http://giscience.uni-hd.de/">GIScience Research Group @ University of Heidelberg</a> &mdash; Map data {attribution.OpenStreetMap}'
+ },
+ variants: {
+ Roads: 'roads',
+ AdminBounds: {
+ options: {
+ variant: 'adminb',
+ maxZoom: 19
+ }
+ },
+ Grayscale: {
+ options: {
+ variant: 'roadsg',
+ maxZoom: 19
+ }
+ }
+ }
+ },
+ Hydda: {
+ url: 'https://{s}.tile.openstreetmap.se/hydda/{variant}/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 18,
+ variant: 'full',
+ attribution: 'Tiles courtesy of <a href="http://openstreetmap.se/" target="_blank">OpenStreetMap Sweden</a> &mdash; Map data {attribution.OpenStreetMap}'
+ },
+ variants: {
+ Full: 'full',
+ Base: 'base',
+ RoadsAndLabels: 'roads_and_labels'
+ }
+ },
+ MapBox: {
+ url: 'https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}{r}.png?access_token={accessToken}',
+ options: {
+ attribution:
+ 'Imagery from <a href="http://mapbox.com/about/maps/">MapBox</a> &mdash; ' +
+ 'Map data {attribution.OpenStreetMap}',
+ subdomains: 'abcd',
+ id: 'streets',
+ accessToken: '<insert your access token here>',
+ }
+ },
+ Stamen: {
+ url: 'https://stamen-tiles-{s}.a.ssl.fastly.net/{variant}/{z}/{x}/{y}{r}.{ext}',
+ options: {
+ attribution:
+ 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, ' +
+ '<a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a> &mdash; ' +
+ 'Map data {attribution.OpenStreetMap}',
+ subdomains: 'abcd',
+ minZoom: 0,
+ maxZoom: 20,
+ variant: 'toner',
+ ext: 'png'
+ },
+ variants: {
+ Toner: 'toner',
+ TonerBackground: 'toner-background',
+ TonerHybrid: 'toner-hybrid',
+ TonerLines: 'toner-lines',
+ TonerLabels: 'toner-labels',
+ TonerLite: 'toner-lite',
+ Watercolor: {
+ url: 'https://stamen-tiles-{s}.a.ssl.fastly.net/{variant}/{z}/{x}/{y}.{ext}',
+ options: {
+ variant: 'watercolor',
+ minZoom: 1,
+ maxZoom: 16
+ }
+ },
+ Terrain: {
+ options: {
+ variant: 'terrain',
+ minZoom: 0,
+ maxZoom: 18
+ }
+ },
+ TerrainBackground: {
+ options: {
+ variant: 'terrain-background',
+ minZoom: 0,
+ maxZoom: 18
+ }
+ },
+ TopOSMRelief: {
+ url: 'https://stamen-tiles-{s}.a.ssl.fastly.net/{variant}/{z}/{x}/{y}.{ext}',
+ options: {
+ variant: 'toposm-color-relief',
+ ext: 'jpg',
+ bounds: [[22, -132], [51, -56]]
+ }
+ },
+ TopOSMFeatures: {
+ options: {
+ variant: 'toposm-features',
+ bounds: [[22, -132], [51, -56]],
+ opacity: 0.9
+ }
+ }
+ }
+ },
+ Esri: {
+ url: 'https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}',
+ options: {
+ variant: 'World_Street_Map',
+ attribution: 'Tiles &copy; Esri'
+ },
+ variants: {
+ WorldStreetMap: {
+ options: {
+ attribution:
+ '{attribution.Esri} &mdash; ' +
+ 'Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012'
+ }
+ },
+ DeLorme: {
+ options: {
+ variant: 'Specialty/DeLorme_World_Base_Map',
+ minZoom: 1,
+ maxZoom: 11,
+ attribution: '{attribution.Esri} &mdash; Copyright: &copy;2012 DeLorme'
+ }
+ },
+ WorldTopoMap: {
+ options: {
+ variant: 'World_Topo_Map',
+ attribution:
+ '{attribution.Esri} &mdash; ' +
+ 'Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community'
+ }
+ },
+ WorldImagery: {
+ options: {
+ variant: 'World_Imagery',
+ attribution:
+ '{attribution.Esri} &mdash; ' +
+ 'Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
+ }
+ },
+ WorldTerrain: {
+ options: {
+ variant: 'World_Terrain_Base',
+ maxZoom: 13,
+ attribution:
+ '{attribution.Esri} &mdash; ' +
+ 'Source: USGS, Esri, TANA, DeLorme, and NPS'
+ }
+ },
+ WorldShadedRelief: {
+ options: {
+ variant: 'World_Shaded_Relief',
+ maxZoom: 13,
+ attribution: '{attribution.Esri} &mdash; Source: Esri'
+ }
+ },
+ WorldPhysical: {
+ options: {
+ variant: 'World_Physical_Map',
+ maxZoom: 8,
+ attribution: '{attribution.Esri} &mdash; Source: US National Park Service'
+ }
+ },
+ OceanBasemap: {
+ options: {
+ variant: 'Ocean_Basemap',
+ maxZoom: 13,
+ attribution: '{attribution.Esri} &mdash; Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri'
+ }
+ },
+ NatGeoWorldMap: {
+ options: {
+ variant: 'NatGeo_World_Map',
+ maxZoom: 16,
+ attribution: '{attribution.Esri} &mdash; National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC'
+ }
+ },
+ WorldGrayCanvas: {
+ options: {
+ variant: 'Canvas/World_Light_Gray_Base',
+ maxZoom: 16,
+ attribution: '{attribution.Esri} &mdash; Esri, DeLorme, NAVTEQ'
+ }
+ }
+ }
+ },
+ OpenWeatherMap: {
+ url: 'http://{s}.tile.openweathermap.org/map/{variant}/{z}/{x}/{y}.png?appid={apiKey}',
+ options: {
+ maxZoom: 19,
+ attribution: 'Map data &copy; <a href="http://openweathermap.org">OpenWeatherMap</a>',
+ apiKey:'<insert your api key here>',
+ opacity: 0.5
+ },
+ variants: {
+ Clouds: 'clouds',
+ CloudsClassic: 'clouds_cls',
+ Precipitation: 'precipitation',
+ PrecipitationClassic: 'precipitation_cls',
+ Rain: 'rain',
+ RainClassic: 'rain_cls',
+ Pressure: 'pressure',
+ PressureContour: 'pressure_cntr',
+ Wind: 'wind',
+ Temperature: 'temp',
+ Snow: 'snow'
+ }
+ },
+ HERE: {
+ /*
+ * HERE maps, formerly Nokia maps.
+ * These basemaps are free, but you need an API key. Please sign up at
+ * http://developer.here.com/getting-started
+ *
+ * Note that the base urls contain '.cit' whichs is HERE's
+ * 'Customer Integration Testing' environment. Please remove for production
+ * envirionments.
+ */
+ url:
+ 'https://{s}.{base}.maps.cit.api.here.com/maptile/2.1/' +
+ '{type}/{mapID}/{variant}/{z}/{x}/{y}/{size}/{format}?' +
+ 'app_id={app_id}&app_code={app_code}&lg={language}',
+ options: {
+ attribution:
+ 'Map &copy; 1987-2014 <a href="http://developer.here.com">HERE</a>',
+ subdomains: '1234',
+ mapID: 'newest',
+ 'app_id': '<insert your app_id here>',
+ 'app_code': '<insert your app_code here>',
+ base: 'base',
+ variant: 'normal.day',
+ maxZoom: 20,
+ type: 'maptile',
+ language: 'eng',
+ format: 'png8',
+ size: '256'
+ },
+ variants: {
+ normalDay: 'normal.day',
+ normalDayCustom: 'normal.day.custom',
+ normalDayGrey: 'normal.day.grey',
+ normalDayMobile: 'normal.day.mobile',
+ normalDayGreyMobile: 'normal.day.grey.mobile',
+ normalDayTransit: 'normal.day.transit',
+ normalDayTransitMobile: 'normal.day.transit.mobile',
+ normalNight: 'normal.night',
+ normalNightMobile: 'normal.night.mobile',
+ normalNightGrey: 'normal.night.grey',
+ normalNightGreyMobile: 'normal.night.grey.mobile',
+
+ basicMap: {
+ options: {
+ type: 'basetile'
+ }
+ },
+ mapLabels: {
+ options: {
+ type: 'labeltile',
+ format: 'png'
+ }
+ },
+ trafficFlow: {
+ options: {
+ base: 'traffic',
+ type: 'flowtile'
+ }
+ },
+ carnavDayGrey: 'carnav.day.grey',
+ hybridDay: {
+ options: {
+ base: 'aerial',
+ variant: 'hybrid.day'
+ }
+ },
+ hybridDayMobile: {
+ options: {
+ base: 'aerial',
+ variant: 'hybrid.day.mobile'
+ }
+ },
+ pedestrianDay: 'pedestrian.day',
+ pedestrianNight: 'pedestrian.night',
+ satelliteDay: {
+ options: {
+ base: 'aerial',
+ variant: 'satellite.day'
+ }
+ },
+ terrainDay: {
+ options: {
+ base: 'aerial',
+ variant: 'terrain.day'
+ }
+ },
+ terrainDayMobile: {
+ options: {
+ base: 'aerial',
+ variant: 'terrain.day.mobile'
+ }
+ }
+ }
+ },
+ FreeMapSK: {
+ url: 'http://t{s}.freemap.sk/T/{z}/{x}/{y}.jpeg',
+ options: {
+ minZoom: 8,
+ maxZoom: 16,
+ subdomains: '1234',
+ bounds: [[47.204642, 15.996093], [49.830896, 22.576904]],
+ attribution:
+ '{attribution.OpenStreetMap}, vizualization CC-By-SA 2.0 <a href="http://freemap.sk">Freemap.sk</a>'
+ }
+ },
+ MtbMap: {
+ url: 'http://tile.mtbmap.cz/mtbmap_tiles/{z}/{x}/{y}.png',
+ options: {
+ attribution:
+ '{attribution.OpenStreetMap} &amp; USGS'
+ }
+ },
+ CartoDB: {
+ url: 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/{variant}/{z}/{x}/{y}{r}.png',
+ options: {
+ attribution: '{attribution.OpenStreetMap} &copy; <a href="http://cartodb.com/attributions">CartoDB</a>',
+ subdomains: 'abcd',
+ maxZoom: 19,
+ variant: 'light_all'
+ },
+ variants: {
+ Positron: 'light_all',
+ PositronNoLabels: 'light_nolabels',
+ PositronOnlyLabels: 'light_only_labels',
+ DarkMatter: 'dark_all',
+ DarkMatterNoLabels: 'dark_nolabels',
+ DarkMatterOnlyLabels: 'dark_only_labels',
+ Voyager: 'rastertiles/voyager',
+ VoyagerNoLabels: 'rastertiles/voyager_nolabels',
+ VoyagerOnlyLabels: 'rastertiles/voyager_only_labels',
+ VoyagerLabelsUnder: 'rastertiles/voyager_labels_under'
+ }
+ },
+ HikeBike: {
+ url: 'http://{s}.tiles.wmflabs.org/{variant}/{z}/{x}/{y}.png',
+ options: {
+ maxZoom: 19,
+ attribution: '{attribution.OpenStreetMap}',
+ variant: 'hikebike'
+ },
+ variants: {
+ HikeBike: {},
+ HillShading: {
+ options: {
+ maxZoom: 15,
+ variant: 'hillshading'
+ }
+ }
+ }
+ },
+ BasemapAT: {
+ url: 'https://maps{s}.wien.gv.at/basemap/{variant}/normal/google3857/{z}/{y}/{x}.{format}',
+ options: {
+ maxZoom: 19,
+ attribution: 'Datenquelle: <a href="https://www.basemap.at">basemap.at</a>',
+ subdomains: ['', '1', '2', '3', '4'],
+ format: 'png',
+ bounds: [[46.358770, 8.782379], [49.037872, 17.189532]],
+ variant: 'geolandbasemap'
+ },
+ variants: {
+ basemap: {
+ options: {
+ maxZoom: 20, // currently only in Vienna
+ variant: 'geolandbasemap'
+ }
+ },
+ grau: 'bmapgrau',
+ overlay: 'bmapoverlay',
+ highdpi: {
+ options: {
+ variant: 'bmaphidpi',
+ format: 'jpeg'
+ }
+ },
+ orthofoto: {
+ options: {
+ maxZoom: 20, // currently only in Vienna
+ variant: 'bmaporthofoto30cm',
+ format: 'jpeg'
+ }
+ }
+ }
+ },
+ nlmaps: {
+ url: 'https://geodata.nationaalgeoregister.nl/tiles/service/wmts/{variant}/EPSG:3857/{z}/{x}/{y}.png',
+ options: {
+ minZoom: 6,
+ maxZoom: 19,
+ bounds: [[50.5, 3.25], [54, 7.6]],
+ attribution: 'Kaartgegevens &copy; <a href="kadaster.nl">Kadaster</a>'
+ },
+ variants: {
+ 'standaard': 'brtachtergrondkaart',
+ 'pastel': 'brtachtergrondkaartpastel',
+ 'grijs': 'brtachtergrondkaartgrijs',
+ 'luchtfoto': {
+ 'url': 'https://geodata.nationaalgeoregister.nl/luchtfoto/rgb/wmts/1.0.0/2016_ortho25/EPSG:3857/{z}/{x}/{y}.png',
+ }
+ }
+ },
+ NASAGIBS: {
+ url: 'https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{maxZoom}/{z}/{y}/{x}.{format}',
+ options: {
+ attribution:
+ 'Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System ' +
+ '(<a href="https://earthdata.nasa.gov">ESDIS</a>) with funding provided by NASA/HQ.',
+ bounds: [[-85.0511287776, -179.999999975], [85.0511287776, 179.999999975]],
+ minZoom: 1,
+ maxZoom: 9,
+ format: 'jpg',
+ time: '',
+ tilematrixset: 'GoogleMapsCompatible_Level'
+ },
+ variants: {
+ ModisTerraTrueColorCR: 'MODIS_Terra_CorrectedReflectance_TrueColor',
+ ModisTerraBands367CR: 'MODIS_Terra_CorrectedReflectance_Bands367',
+ ViirsEarthAtNight2012: {
+ options: {
+ variant: 'VIIRS_CityLights_2012',
+ maxZoom: 8
+ }
+ },
+ ModisTerraLSTDay: {
+ options: {
+ variant: 'MODIS_Terra_Land_Surface_Temp_Day',
+ format: 'png',
+ maxZoom: 7,
+ opacity: 0.75
+ }
+ },
+ ModisTerraSnowCover: {
+ options: {
+ variant: 'MODIS_Terra_Snow_Cover',
+ format: 'png',
+ maxZoom: 8,
+ opacity: 0.75
+ }
+ },
+ ModisTerraAOD: {
+ options: {
+ variant: 'MODIS_Terra_Aerosol',
+ format: 'png',
+ maxZoom: 6,
+ opacity: 0.75
+ }
+ },
+ ModisTerraChlorophyll: {
+ options: {
+ variant: 'MODIS_Terra_Chlorophyll_A',
+ format: 'png',
+ maxZoom: 7,
+ opacity: 0.75
+ }
+ }
+ }
+ },
+ NLS: {
+ // NLS maps are copyright National library of Scotland.
+ // http://maps.nls.uk/projects/api/index.html
+ // Please contact NLS for anything other than non-commercial low volume usage
+ //
+ // Map sources: Ordnance Survey 1:1m to 1:63K, 1920s-1940s
+ // z0-9 - 1:1m
+ // z10-11 - quarter inch (1:253440)
+ // z12-18 - one inch (1:63360)
+ url: 'https://nls-{s}.tileserver.com/nls/{z}/{x}/{y}.jpg',
+ options: {
+ attribution: '<a href="http://geo.nls.uk/maps/">National Library of Scotland Historic Maps</a>',
+ bounds: [[49.6, -12], [61.7, 3]],
+ minZoom: 1,
+ maxZoom: 18,
+ subdomains: '0123',
+ }
+ },
+ JusticeMap: {
+ // Justice Map (http://www.justicemap.org/)
+ // Visualize race and income data for your community, county and country.
+ // Includes tools for data journalists, bloggers and community activists.
+ url: 'http://www.justicemap.org/tile/{size}/{variant}/{z}/{x}/{y}.png',
+ options: {
+ attribution: '<a href="http://www.justicemap.org/terms.php">Justice Map</a>',
+ // one of 'county', 'tract', 'block'
+ size: 'county',
+ // Bounds for USA, including Alaska and Hawaii
+ bounds: [[14, -180], [72, -56]]
+ },
+ variants: {
+ income: 'income',
+ americanIndian: 'indian',
+ asian: 'asian',
+ black: 'black',
+ hispanic: 'hispanic',
+ multi: 'multi',
+ nonWhite: 'nonwhite',
+ white: 'white',
+ plurality: 'plural'
+ }
+ },
+ Wikimedia: {
+ url: 'https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}{r}.png',
+ options: {
+ attribution: '<a href="https://wikimediafoundation.org/wiki/Maps_Terms_of_Use">Wikimedia</a>',
+ minZoom: 1,
+ maxZoom: 19
+ }
+ },
+ GeoportailFrance: {
+ url: 'https://wxs.ign.fr/{apikey}/geoportail/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&STYLE={style}&TILEMATRIXSET=PM&FORMAT={format}&LAYER={variant}&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}',
+ options: {
+ attribution: '<a target="_blank" href="https://www.geoportail.gouv.fr/">Geoportail France</a>',
+ bounds: [[-75, -180], [81, 180]],
+ minZoom: 2,
+ maxZoom: 18,
+ // Get your own geoportail apikey here : http://professionnels.ign.fr/ign/contrats/
+ // NB : 'choisirgeoportail' is a demonstration key that comes with no guarantee
+ apikey: 'choisirgeoportail',
+ format: 'image/jpeg',
+ style : 'normal',
+ variant: 'GEOGRAPHICALGRIDSYSTEMS.MAPS.SCAN-EXPRESS.STANDARD'
+ },
+ variants: {
+ parcels: {
+ options : {
+ variant: 'CADASTRALPARCELS.PARCELS',
+ maxZoom: 20,
+ style : 'bdparcellaire',
+ format: 'image/png'
+ }
+ },
+ ignMaps: 'GEOGRAPHICALGRIDSYSTEMS.MAPS',
+ maps: 'GEOGRAPHICALGRIDSYSTEMS.MAPS.SCAN-EXPRESS.STANDARD',
+ orthos: {
+ options: {
+ maxZoom: 19,
+ variant: 'ORTHOIMAGERY.ORTHOPHOTOS'
+ }
+ }
+ }
+ }
+ };
+
+ L.tileLayer.provider = function (provider, options) {
+ return new L.TileLayer.Provider(provider, options);
+ };
+
+ return L;
+}));
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.editable/Leaflet.Editable.js b/www/wiki/extensions/Maps/resources/leaflet/leaflet.editable/Leaflet.Editable.js
new file mode 100644
index 00000000..e259a14c
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet.editable/Leaflet.Editable.js
@@ -0,0 +1,1945 @@
+'use strict';
+(function (factory, window) {// 1.2 alpha
+ /*globals define, module, require*/
+
+ // define an AMD module that relies on 'leaflet'
+ if (typeof define === 'function' && define.amd) {
+ define(['leaflet'], factory);
+
+
+ // define a Common JS module that relies on 'leaflet'
+ } else if (typeof exports === 'object') {
+ module.exports = factory(require('leaflet'));
+ }
+
+ // attach your plugin to the global 'L' variable
+ if(typeof window !== 'undefined' && window.L){
+ factory(window.L);
+ }
+
+}(function (L) {
+ // 🍂miniclass CancelableEvent (Event objects)
+ // 🍂method cancel()
+ // Cancel any subsequent action.
+
+ // 🍂miniclass VertexEvent (Event objects)
+ // 🍂property vertex: VertexMarker
+ // The vertex that fires the event.
+
+ // 🍂miniclass ShapeEvent (Event objects)
+ // 🍂property shape: Array
+ // The shape (LatLngs array) subject of the action.
+
+ // 🍂miniclass CancelableVertexEvent (Event objects)
+ // 🍂inherits VertexEvent
+ // 🍂inherits CancelableEvent
+
+ // 🍂miniclass CancelableShapeEvent (Event objects)
+ // 🍂inherits ShapeEvent
+ // 🍂inherits CancelableEvent
+
+ // 🍂miniclass LayerEvent (Event objects)
+ // 🍂property layer: object
+ // The Layer (Marker, Polyline…) subject of the action.
+
+ // 🍂namespace Editable; 🍂class Editable; 🍂aka L.Editable
+ // Main edition handler. By default, it is attached to the map
+ // as `map.editTools` property.
+ // Leaflet.Editable is made to be fully extendable. You have three ways to customize
+ // the behaviour: using options, listening to events, or extending.
+ L.Editable = L.Evented.extend({
+
+ statics: {
+ FORWARD: 1,
+ BACKWARD: -1
+ },
+
+ options: {
+
+ // You can pass them when creating a map using the `editOptions` key.
+ // 🍂option zIndex: int = 1000
+ // The default zIndex of the editing tools.
+ zIndex: 1000,
+
+ // 🍂option polygonClass: class = L.Polygon
+ // Class to be used when creating a new Polygon.
+ polygonClass: L.Polygon,
+
+ // 🍂option polylineClass: class = L.Polyline
+ // Class to be used when creating a new Polyline.
+ polylineClass: L.Polyline,
+
+ // 🍂option markerClass: class = L.Marker
+ // Class to be used when creating a new Marker.
+ markerClass: L.Marker,
+
+ // 🍂option rectangleClass: class = L.Rectangle
+ // Class to be used when creating a new Rectangle.
+ rectangleClass: L.Rectangle,
+
+ // 🍂option circleClass: class = L.Circle
+ // Class to be used when creating a new Circle.
+ circleClass: L.Circle,
+
+ // 🍂option drawingCSSClass: string = 'leaflet-editable-drawing'
+ // CSS class to be added to the map container while drawing.
+ drawingCSSClass: 'leaflet-editable-drawing',
+
+ // 🍂option drawingCursor: const = 'crosshair'
+ // Cursor mode set to the map while drawing.
+ drawingCursor: 'crosshair',
+
+ // 🍂option editLayer: Layer = new L.LayerGroup()
+ // Layer used to store edit tools (vertex, line guide…).
+ editLayer: undefined,
+
+ // 🍂option featuresLayer: Layer = new L.LayerGroup()
+ // Default layer used to store drawn features (Marker, Polyline…).
+ featuresLayer: undefined,
+
+ // 🍂option polylineEditorClass: class = PolylineEditor
+ // Class to be used as Polyline editor.
+ polylineEditorClass: undefined,
+
+ // 🍂option polygonEditorClass: class = PolygonEditor
+ // Class to be used as Polygon editor.
+ polygonEditorClass: undefined,
+
+ // 🍂option markerEditorClass: class = MarkerEditor
+ // Class to be used as Marker editor.
+ markerEditorClass: undefined,
+
+ // 🍂option rectangleEditorClass: class = RectangleEditor
+ // Class to be used as Rectangle editor.
+ rectangleEditorClass: undefined,
+
+ // 🍂option circleEditorClass: class = CircleEditor
+ // Class to be used as Circle editor.
+ circleEditorClass: undefined,
+
+ // 🍂option lineGuideOptions: hash = {}
+ // Options to be passed to the line guides.
+ lineGuideOptions: {},
+
+ // 🍂option skipMiddleMarkers: boolean = false
+ // Set this to true if you don't want middle markers.
+ skipMiddleMarkers: false
+
+ },
+
+ initialize: function (map, options) {
+ L.setOptions(this, options);
+ this._lastZIndex = this.options.zIndex;
+ this.map = map;
+ this.editLayer = this.createEditLayer();
+ this.featuresLayer = this.createFeaturesLayer();
+ this.forwardLineGuide = this.createLineGuide();
+ this.backwardLineGuide = this.createLineGuide();
+ },
+
+ fireAndForward: function (type, e) {
+ e = e || {};
+ e.editTools = this;
+ this.fire(type, e);
+ this.map.fire(type, e);
+ },
+
+ createLineGuide: function () {
+ var options = L.extend({dashArray: '5,10', weight: 1, interactive: false}, this.options.lineGuideOptions);
+ return L.polyline([], options);
+ },
+
+ createVertexIcon: function (options) {
+ return L.Browser.mobile && L.Browser.touch ? new L.Editable.TouchVertexIcon(options) : new L.Editable.VertexIcon(options);
+ },
+
+ createEditLayer: function () {
+ return this.options.editLayer || new L.LayerGroup().addTo(this.map);
+ },
+
+ createFeaturesLayer: function () {
+ return this.options.featuresLayer || new L.LayerGroup().addTo(this.map);
+ },
+
+ moveForwardLineGuide: function (latlng) {
+ if (this.forwardLineGuide._latlngs.length) {
+ this.forwardLineGuide._latlngs[1] = latlng;
+ this.forwardLineGuide._bounds.extend(latlng);
+ this.forwardLineGuide.redraw();
+ }
+ },
+
+ moveBackwardLineGuide: function (latlng) {
+ if (this.backwardLineGuide._latlngs.length) {
+ this.backwardLineGuide._latlngs[1] = latlng;
+ this.backwardLineGuide._bounds.extend(latlng);
+ this.backwardLineGuide.redraw();
+ }
+ },
+
+ anchorForwardLineGuide: function (latlng) {
+ this.forwardLineGuide._latlngs[0] = latlng;
+ this.forwardLineGuide._bounds.extend(latlng);
+ this.forwardLineGuide.redraw();
+ },
+
+ anchorBackwardLineGuide: function (latlng) {
+ this.backwardLineGuide._latlngs[0] = latlng;
+ this.backwardLineGuide._bounds.extend(latlng);
+ this.backwardLineGuide.redraw();
+ },
+
+ attachForwardLineGuide: function () {
+ this.editLayer.addLayer(this.forwardLineGuide);
+ },
+
+ attachBackwardLineGuide: function () {
+ this.editLayer.addLayer(this.backwardLineGuide);
+ },
+
+ detachForwardLineGuide: function () {
+ this.forwardLineGuide.setLatLngs([]);
+ this.editLayer.removeLayer(this.forwardLineGuide);
+ },
+
+ detachBackwardLineGuide: function () {
+ this.backwardLineGuide.setLatLngs([]);
+ this.editLayer.removeLayer(this.backwardLineGuide);
+ },
+
+ blockEvents: function () {
+ // Hack: force map not to listen to other layers events while drawing.
+ if (!this._oldTargets) {
+ this._oldTargets = this.map._targets;
+ this.map._targets = {};
+ }
+ },
+
+ unblockEvents: function () {
+ if (this._oldTargets) {
+ // Reset, but keep targets created while drawing.
+ this.map._targets = L.extend(this.map._targets, this._oldTargets);
+ delete this._oldTargets;
+ }
+ },
+
+ registerForDrawing: function (editor) {
+ if (this._drawingEditor) this.unregisterForDrawing(this._drawingEditor);
+ this.blockEvents();
+ editor.reset(); // Make sure editor tools still receive events.
+ this._drawingEditor = editor;
+ this.map.on('mousemove touchmove', editor.onDrawingMouseMove, editor);
+ this.map.on('mousedown', this.onMousedown, this);
+ this.map.on('mouseup', this.onMouseup, this);
+ L.DomUtil.addClass(this.map._container, this.options.drawingCSSClass);
+ this.defaultMapCursor = this.map._container.style.cursor;
+ this.map._container.style.cursor = this.options.drawingCursor;
+ },
+
+ unregisterForDrawing: function (editor) {
+ this.unblockEvents();
+ L.DomUtil.removeClass(this.map._container, this.options.drawingCSSClass);
+ this.map._container.style.cursor = this.defaultMapCursor;
+ editor = editor || this._drawingEditor;
+ if (!editor) return;
+ this.map.off('mousemove touchmove', editor.onDrawingMouseMove, editor);
+ this.map.off('mousedown', this.onMousedown, this);
+ this.map.off('mouseup', this.onMouseup, this);
+ if (editor !== this._drawingEditor) return;
+ delete this._drawingEditor;
+ if (editor._drawing) editor.cancelDrawing();
+ },
+
+ onMousedown: function (e) {
+ this._mouseDown = e;
+ this._drawingEditor.onDrawingMouseDown(e);
+ },
+
+ onMouseup: function (e) {
+ if (this._mouseDown) {
+ var editor = this._drawingEditor,
+ mouseDown = this._mouseDown;
+ this._mouseDown = null;
+ editor.onDrawingMouseUp(e);
+ if (this._drawingEditor !== editor) return; // onDrawingMouseUp may call unregisterFromDrawing.
+ var origin = L.point(mouseDown.originalEvent.clientX, mouseDown.originalEvent.clientY);
+ var distance = L.point(e.originalEvent.clientX, e.originalEvent.clientY).distanceTo(origin);
+ if (Math.abs(distance) < 9 * (window.devicePixelRatio || 1)) this._drawingEditor.onDrawingClick(e);
+ }
+ },
+
+ // 🍂section Public methods
+ // You will generally access them by the `map.editTools`
+ // instance:
+ //
+ // `map.editTools.startPolyline();`
+
+ // 🍂method drawing(): boolean
+ // Return true if any drawing action is ongoing.
+ drawing: function () {
+ return this._drawingEditor && this._drawingEditor.drawing();
+ },
+
+ // 🍂method stopDrawing()
+ // When you need to stop any ongoing drawing, without needing to know which editor is active.
+ stopDrawing: function () {
+ this.unregisterForDrawing();
+ },
+
+ // 🍂method commitDrawing()
+ // When you need to commit any ongoing drawing, without needing to know which editor is active.
+ commitDrawing: function (e) {
+ if (!this._drawingEditor) return;
+ this._drawingEditor.commitDrawing(e);
+ },
+
+ connectCreatedToMap: function (layer) {
+ return this.featuresLayer.addLayer(layer);
+ },
+
+ // 🍂method startPolyline(latlng: L.LatLng, options: hash): L.Polyline
+ // Start drawing a Polyline. If `latlng` is given, a first point will be added. In any case, continuing on user click.
+ // If `options` is given, it will be passed to the Polyline class constructor.
+ startPolyline: function (latlng, options) {
+ var line = this.createPolyline([], options);
+ line.enableEdit(this.map).newShape(latlng);
+ return line;
+ },
+
+ // 🍂method startPolygon(latlng: L.LatLng, options: hash): L.Polygon
+ // Start drawing a Polygon. If `latlng` is given, a first point will be added. In any case, continuing on user click.
+ // If `options` is given, it will be passed to the Polygon class constructor.
+ startPolygon: function (latlng, options) {
+ var polygon = this.createPolygon([], options);
+ polygon.enableEdit(this.map).newShape(latlng);
+ return polygon;
+ },
+
+ // 🍂method startMarker(latlng: L.LatLng, options: hash): L.Marker
+ // Start adding a Marker. If `latlng` is given, the Marker will be shown first at this point.
+ // In any case, it will follow the user mouse, and will have a final `latlng` on next click (or touch).
+ // If `options` is given, it will be passed to the Marker class constructor.
+ startMarker: function (latlng, options) {
+ latlng = latlng || this.map.getCenter().clone();
+ var marker = this.createMarker(latlng, options);
+ marker.enableEdit(this.map).startDrawing();
+ return marker;
+ },
+
+ // 🍂method startRectangle(latlng: L.LatLng, options: hash): L.Rectangle
+ // Start drawing a Rectangle. If `latlng` is given, the Rectangle anchor will be added. In any case, continuing on user drag.
+ // If `options` is given, it will be passed to the Rectangle class constructor.
+ startRectangle: function(latlng, options) {
+ var corner = latlng || L.latLng([0, 0]);
+ var bounds = new L.LatLngBounds(corner, corner);
+ var rectangle = this.createRectangle(bounds, options);
+ rectangle.enableEdit(this.map).startDrawing();
+ return rectangle;
+ },
+
+ // 🍂method startCircle(latlng: L.LatLng, options: hash): L.Circle
+ // Start drawing a Circle. If `latlng` is given, the Circle anchor will be added. In any case, continuing on user drag.
+ // If `options` is given, it will be passed to the Circle class constructor.
+ startCircle: function (latlng, options) {
+ latlng = latlng || this.map.getCenter().clone();
+ var circle = this.createCircle(latlng, options);
+ circle.enableEdit(this.map).startDrawing();
+ return circle;
+ },
+
+ startHole: function (editor, latlng) {
+ editor.newHole(latlng);
+ },
+
+ createLayer: function (klass, latlngs, options) {
+ options = L.Util.extend({editOptions: {editTools: this}}, options);
+ var layer = new klass(latlngs, options);
+ // 🍂namespace Editable
+ // 🍂event editable:created: LayerEvent
+ // Fired when a new feature (Marker, Polyline…) is created.
+ this.fireAndForward('editable:created', {layer: layer});
+ return layer;
+ },
+
+ createPolyline: function (latlngs, options) {
+ return this.createLayer(options && options.polylineClass || this.options.polylineClass, latlngs, options);
+ },
+
+ createPolygon: function (latlngs, options) {
+ return this.createLayer(options && options.polygonClass || this.options.polygonClass, latlngs, options);
+ },
+
+ createMarker: function (latlng, options) {
+ return this.createLayer(options && options.markerClass || this.options.markerClass, latlng, options);
+ },
+
+ createRectangle: function (bounds, options) {
+ return this.createLayer(options && options.rectangleClass || this.options.rectangleClass, bounds, options);
+ },
+
+ createCircle: function (latlng, options) {
+ return this.createLayer(options && options.circleClass || this.options.circleClass, latlng, options);
+ }
+
+ });
+
+ L.extend(L.Editable, {
+
+ makeCancellable: function (e) {
+ e.cancel = function () {
+ e._cancelled = true;
+ };
+ }
+
+ });
+
+ // 🍂namespace Map; 🍂class Map
+ // Leaflet.Editable add options and events to the `L.Map` object.
+ // See `Editable` events for the list of events fired on the Map.
+ // 🍂example
+ //
+ // ```js
+ // var map = L.map('map', {
+ // editable: true,
+ // editOptions: {
+ // …
+ // }
+ // });
+ // ```
+ // 🍂section Editable Map Options
+ L.Map.mergeOptions({
+
+ // 🍂namespace Map
+ // 🍂section Map Options
+ // 🍂option editToolsClass: class = L.Editable
+ // Class to be used as vertex, for path editing.
+ editToolsClass: L.Editable,
+
+ // 🍂option editable: boolean = false
+ // Whether to create a L.Editable instance at map init.
+ editable: false,
+
+ // 🍂option editOptions: hash = {}
+ // Options to pass to L.Editable when instantiating.
+ editOptions: {}
+
+ });
+
+ L.Map.addInitHook(function () {
+
+ this.whenReady(function () {
+ if (this.options.editable) {
+ this.editTools = new this.options.editToolsClass(this, this.options.editOptions);
+ }
+ });
+
+ });
+
+ L.Editable.VertexIcon = L.DivIcon.extend({
+
+ options: {
+ iconSize: new L.Point(8, 8)
+ }
+
+ });
+
+ L.Editable.TouchVertexIcon = L.Editable.VertexIcon.extend({
+
+ options: {
+ iconSize: new L.Point(20, 20)
+ }
+
+ });
+
+
+ // 🍂namespace Editable; 🍂class VertexMarker; Handler for dragging path vertices.
+ L.Editable.VertexMarker = L.Marker.extend({
+
+ options: {
+ draggable: true,
+ className: 'leaflet-div-icon leaflet-vertex-icon'
+ },
+
+
+ // 🍂section Public methods
+ // The marker used to handle path vertex. You will usually interact with a `VertexMarker`
+ // instance when listening for events like `editable:vertex:ctrlclick`.
+
+ initialize: function (latlng, latlngs, editor, options) {
+ // We don't use this._latlng, because on drag Leaflet replace it while
+ // we want to keep reference.
+ this.latlng = latlng;
+ this.latlngs = latlngs;
+ this.editor = editor;
+ L.Marker.prototype.initialize.call(this, latlng, options);
+ this.options.icon = this.editor.tools.createVertexIcon({className: this.options.className});
+ this.latlng.__vertex = this;
+ this.editor.editLayer.addLayer(this);
+ this.setZIndexOffset(editor.tools._lastZIndex + 1);
+ },
+
+ onAdd: function (map) {
+ L.Marker.prototype.onAdd.call(this, map);
+ this.on('drag', this.onDrag);
+ this.on('dragstart', this.onDragStart);
+ this.on('dragend', this.onDragEnd);
+ this.on('mouseup', this.onMouseup);
+ this.on('click', this.onClick);
+ this.on('contextmenu', this.onContextMenu);
+ this.on('mousedown touchstart', this.onMouseDown);
+ this.on('mouseover', this.onMouseOver);
+ this.on('mouseout', this.onMouseOut);
+ this.addMiddleMarkers();
+ },
+
+ onRemove: function (map) {
+ if (this.middleMarker) this.middleMarker.delete();
+ delete this.latlng.__vertex;
+ this.off('drag', this.onDrag);
+ this.off('dragstart', this.onDragStart);
+ this.off('dragend', this.onDragEnd);
+ this.off('mouseup', this.onMouseup);
+ this.off('click', this.onClick);
+ this.off('contextmenu', this.onContextMenu);
+ this.off('mousedown touchstart', this.onMouseDown);
+ this.off('mouseover', this.onMouseOver);
+ this.off('mouseout', this.onMouseOut);
+ L.Marker.prototype.onRemove.call(this, map);
+ },
+
+ onDrag: function (e) {
+ e.vertex = this;
+ this.editor.onVertexMarkerDrag(e);
+ var iconPos = L.DomUtil.getPosition(this._icon),
+ latlng = this._map.layerPointToLatLng(iconPos);
+ this.latlng.update(latlng);
+ this._latlng = this.latlng; // Push back to Leaflet our reference.
+ this.editor.refresh();
+ if (this.middleMarker) this.middleMarker.updateLatLng();
+ var next = this.getNext();
+ if (next && next.middleMarker) next.middleMarker.updateLatLng();
+ },
+
+ onDragStart: function (e) {
+ e.vertex = this;
+ this.editor.onVertexMarkerDragStart(e);
+ },
+
+ onDragEnd: function (e) {
+ e.vertex = this;
+ this.editor.onVertexMarkerDragEnd(e);
+ },
+
+ onClick: function (e) {
+ e.vertex = this;
+ this.editor.onVertexMarkerClick(e);
+ },
+
+ onMouseup: function (e) {
+ L.DomEvent.stop(e);
+ e.vertex = this;
+ this.editor.map.fire('mouseup', e);
+ },
+
+ onContextMenu: function (e) {
+ e.vertex = this;
+ this.editor.onVertexMarkerContextMenu(e);
+ },
+
+ onMouseDown: function (e) {
+ e.vertex = this;
+ this.editor.onVertexMarkerMouseDown(e);
+ },
+
+ onMouseOver: function (e) {
+ e.vertex = this;
+ this.editor.onVertexMarkerMouseOver(e);
+ },
+
+ onMouseOut: function (e) {
+ e.vertex = this;
+ this.editor.onVertexMarkerMouseOut(e);
+ },
+
+ // 🍂method delete()
+ // Delete a vertex and the related LatLng.
+ delete: function () {
+ var next = this.getNext(); // Compute before changing latlng
+ this.latlngs.splice(this.getIndex(), 1);
+ this.editor.editLayer.removeLayer(this);
+ this.editor.onVertexDeleted({latlng: this.latlng, vertex: this});
+ if (!this.latlngs.length) this.editor.deleteShape(this.latlngs);
+ if (next) next.resetMiddleMarker();
+ this.editor.refresh();
+ },
+
+ // 🍂method getIndex(): int
+ // Get the index of the current vertex among others of the same LatLngs group.
+ getIndex: function () {
+ return this.latlngs.indexOf(this.latlng);
+ },
+
+ // 🍂method getLastIndex(): int
+ // Get last vertex index of the LatLngs group of the current vertex.
+ getLastIndex: function () {
+ return this.latlngs.length - 1;
+ },
+
+ // 🍂method getPrevious(): VertexMarker
+ // Get the previous VertexMarker in the same LatLngs group.
+ getPrevious: function () {
+ if (this.latlngs.length < 2) return;
+ var index = this.getIndex(),
+ previousIndex = index - 1;
+ if (index === 0 && this.editor.CLOSED) previousIndex = this.getLastIndex();
+ var previous = this.latlngs[previousIndex];
+ if (previous) return previous.__vertex;
+ },
+
+ // 🍂method getNext(): VertexMarker
+ // Get the next VertexMarker in the same LatLngs group.
+ getNext: function () {
+ if (this.latlngs.length < 2) return;
+ var index = this.getIndex(),
+ nextIndex = index + 1;
+ if (index === this.getLastIndex() && this.editor.CLOSED) nextIndex = 0;
+ var next = this.latlngs[nextIndex];
+ if (next) return next.__vertex;
+ },
+
+ addMiddleMarker: function (previous) {
+ if (!this.editor.hasMiddleMarkers()) return;
+ previous = previous || this.getPrevious();
+ if (previous && !this.middleMarker) this.middleMarker = this.editor.addMiddleMarker(previous, this, this.latlngs, this.editor);
+ },
+
+ addMiddleMarkers: function () {
+ if (!this.editor.hasMiddleMarkers()) return;
+ var previous = this.getPrevious();
+ if (previous) this.addMiddleMarker(previous);
+ var next = this.getNext();
+ if (next) next.resetMiddleMarker();
+ },
+
+ resetMiddleMarker: function () {
+ if (this.middleMarker) this.middleMarker.delete();
+ this.addMiddleMarker();
+ },
+
+ // 🍂method split()
+ // Split the vertex LatLngs group at its index, if possible.
+ split: function () {
+ if (!this.editor.splitShape) return; // Only for PolylineEditor
+ this.editor.splitShape(this.latlngs, this.getIndex());
+ },
+
+ // 🍂method continue()
+ // Continue the vertex LatLngs from this vertex. Only active for first and last vertices of a Polyline.
+ continue: function () {
+ if (!this.editor.continueBackward) return; // Only for PolylineEditor
+ var index = this.getIndex();
+ if (index === 0) this.editor.continueBackward(this.latlngs);
+ else if (index === this.getLastIndex()) this.editor.continueForward(this.latlngs);
+ }
+
+ });
+
+ L.Editable.mergeOptions({
+
+ // 🍂namespace Editable
+ // 🍂option vertexMarkerClass: class = VertexMarker
+ // Class to be used as vertex, for path editing.
+ vertexMarkerClass: L.Editable.VertexMarker
+
+ });
+
+ L.Editable.MiddleMarker = L.Marker.extend({
+
+ options: {
+ opacity: 0.5,
+ className: 'leaflet-div-icon leaflet-middle-icon',
+ draggable: true
+ },
+
+ initialize: function (left, right, latlngs, editor, options) {
+ this.left = left;
+ this.right = right;
+ this.editor = editor;
+ this.latlngs = latlngs;
+ L.Marker.prototype.initialize.call(this, this.computeLatLng(), options);
+ this._opacity = this.options.opacity;
+ this.options.icon = this.editor.tools.createVertexIcon({className: this.options.className});
+ this.editor.editLayer.addLayer(this);
+ this.setVisibility();
+ },
+
+ setVisibility: function () {
+ var leftPoint = this._map.latLngToContainerPoint(this.left.latlng),
+ rightPoint = this._map.latLngToContainerPoint(this.right.latlng),
+ size = L.point(this.options.icon.options.iconSize);
+ if (leftPoint.distanceTo(rightPoint) < size.x * 3) this.hide();
+ else this.show();
+ },
+
+ show: function () {
+ this.setOpacity(this._opacity);
+ },
+
+ hide: function () {
+ this.setOpacity(0);
+ },
+
+ updateLatLng: function () {
+ this.setLatLng(this.computeLatLng());
+ this.setVisibility();
+ },
+
+ computeLatLng: function () {
+ var leftPoint = this.editor.map.latLngToContainerPoint(this.left.latlng),
+ rightPoint = this.editor.map.latLngToContainerPoint(this.right.latlng),
+ y = (leftPoint.y + rightPoint.y) / 2,
+ x = (leftPoint.x + rightPoint.x) / 2;
+ return this.editor.map.containerPointToLatLng([x, y]);
+ },
+
+ onAdd: function (map) {
+ L.Marker.prototype.onAdd.call(this, map);
+ L.DomEvent.on(this._icon, 'mousedown touchstart', this.onMouseDown, this);
+ map.on('zoomend', this.setVisibility, this);
+ },
+
+ onRemove: function (map) {
+ delete this.right.middleMarker;
+ L.DomEvent.off(this._icon, 'mousedown touchstart', this.onMouseDown, this);
+ map.off('zoomend', this.setVisibility, this);
+ L.Marker.prototype.onRemove.call(this, map);
+ },
+
+ onMouseDown: function (e) {
+ var iconPos = L.DomUtil.getPosition(this._icon),
+ latlng = this.editor.map.layerPointToLatLng(iconPos);
+ e = {
+ originalEvent: e,
+ latlng: latlng
+ };
+ if (this.options.opacity === 0) return;
+ L.Editable.makeCancellable(e);
+ this.editor.onMiddleMarkerMouseDown(e);
+ if (e._cancelled) return;
+ this.latlngs.splice(this.index(), 0, e.latlng);
+ this.editor.refresh();
+ var icon = this._icon;
+ var marker = this.editor.addVertexMarker(e.latlng, this.latlngs);
+ this.editor.onNewVertex(marker);
+ /* Hack to workaround browser not firing touchend when element is no more on DOM */
+ var parent = marker._icon.parentNode;
+ parent.removeChild(marker._icon);
+ marker._icon = icon;
+ parent.appendChild(marker._icon);
+ marker._initIcon();
+ marker._initInteraction();
+ marker.setOpacity(1);
+ /* End hack */
+ // Transfer ongoing dragging to real marker
+ L.Draggable._dragging = false;
+ marker.dragging._draggable._onDown(e.originalEvent);
+ this.delete();
+ },
+
+ delete: function () {
+ this.editor.editLayer.removeLayer(this);
+ },
+
+ index: function () {
+ return this.latlngs.indexOf(this.right.latlng);
+ }
+
+ });
+
+ L.Editable.mergeOptions({
+
+ // 🍂namespace Editable
+ // 🍂option middleMarkerClass: class = VertexMarker
+ // Class to be used as middle vertex, pulled by the user to create a new point in the middle of a path.
+ middleMarkerClass: L.Editable.MiddleMarker
+
+ });
+
+ // 🍂namespace Editable; 🍂class BaseEditor; 🍂aka L.Editable.BaseEditor
+ // When editing a feature (Marker, Polyline…), an editor is attached to it. This
+ // editor basically knows how to handle the edition.
+ L.Editable.BaseEditor = L.Handler.extend({
+
+ initialize: function (map, feature, options) {
+ L.setOptions(this, options);
+ this.map = map;
+ this.feature = feature;
+ this.feature.editor = this;
+ this.editLayer = new L.LayerGroup();
+ this.tools = this.options.editTools || map.editTools;
+ },
+
+ // 🍂method enable(): this
+ // Set up the drawing tools for the feature to be editable.
+ addHooks: function () {
+ if (this.isConnected()) this.onFeatureAdd();
+ else this.feature.once('add', this.onFeatureAdd, this);
+ this.onEnable();
+ this.feature.on(this._getEvents(), this);
+ },
+
+ // 🍂method disable(): this
+ // Remove the drawing tools for the feature.
+ removeHooks: function () {
+ this.feature.off(this._getEvents(), this);
+ if (this.feature.dragging) this.feature.dragging.disable();
+ this.editLayer.clearLayers();
+ this.tools.editLayer.removeLayer(this.editLayer);
+ this.onDisable();
+ if (this._drawing) this.cancelDrawing();
+ },
+
+ // 🍂method drawing(): boolean
+ // Return true if any drawing action is ongoing with this editor.
+ drawing: function () {
+ return !!this._drawing;
+ },
+
+ reset: function () {},
+
+ onFeatureAdd: function () {
+ this.tools.editLayer.addLayer(this.editLayer);
+ if (this.feature.dragging) this.feature.dragging.enable();
+ },
+
+ hasMiddleMarkers: function () {
+ return !this.options.skipMiddleMarkers && !this.tools.options.skipMiddleMarkers;
+ },
+
+ fireAndForward: function (type, e) {
+ e = e || {};
+ e.layer = this.feature;
+ this.feature.fire(type, e);
+ this.tools.fireAndForward(type, e);
+ },
+
+ onEnable: function () {
+ // 🍂namespace Editable
+ // 🍂event editable:enable: Event
+ // Fired when an existing feature is ready to be edited.
+ this.fireAndForward('editable:enable');
+ },
+
+ onDisable: function () {
+ // 🍂namespace Editable
+ // 🍂event editable:disable: Event
+ // Fired when an existing feature is not ready anymore to be edited.
+ this.fireAndForward('editable:disable');
+ },
+
+ onEditing: function () {
+ // 🍂namespace Editable
+ // 🍂event editable:editing: Event
+ // Fired as soon as any change is made to the feature geometry.
+ this.fireAndForward('editable:editing');
+ },
+
+ onStartDrawing: function () {
+ // 🍂namespace Editable
+ // 🍂section Drawing events
+ // 🍂event editable:drawing:start: Event
+ // Fired when a feature is to be drawn.
+ this.fireAndForward('editable:drawing:start');
+ },
+
+ onEndDrawing: function () {
+ // 🍂namespace Editable
+ // 🍂section Drawing events
+ // 🍂event editable:drawing:end: Event
+ // Fired when a feature is not drawn anymore.
+ this.fireAndForward('editable:drawing:end');
+ },
+
+ onCancelDrawing: function () {
+ // 🍂namespace Editable
+ // 🍂section Drawing events
+ // 🍂event editable:drawing:cancel: Event
+ // Fired when user cancel drawing while a feature is being drawn.
+ this.fireAndForward('editable:drawing:cancel');
+ },
+
+ onCommitDrawing: function (e) {
+ // 🍂namespace Editable
+ // 🍂section Drawing events
+ // 🍂event editable:drawing:commit: Event
+ // Fired when user finish drawing a feature.
+ this.fireAndForward('editable:drawing:commit', e);
+ },
+
+ onDrawingMouseDown: function (e) {
+ // 🍂namespace Editable
+ // 🍂section Drawing events
+ // 🍂event editable:drawing:mousedown: Event
+ // Fired when user `mousedown` while drawing.
+ this.fireAndForward('editable:drawing:mousedown', e);
+ },
+
+ onDrawingMouseUp: function (e) {
+ // 🍂namespace Editable
+ // 🍂section Drawing events
+ // 🍂event editable:drawing:mouseup: Event
+ // Fired when user `mouseup` while drawing.
+ this.fireAndForward('editable:drawing:mouseup', e);
+ },
+
+ startDrawing: function () {
+ if (!this._drawing) this._drawing = L.Editable.FORWARD;
+ this.tools.registerForDrawing(this);
+ this.onStartDrawing();
+ },
+
+ commitDrawing: function (e) {
+ this.onCommitDrawing(e);
+ this.endDrawing();
+ },
+
+ cancelDrawing: function () {
+ // If called during a vertex drag, the vertex will be removed before
+ // the mouseup fires on it. This is a workaround. Maybe better fix is
+ // To have L.Draggable reset it's status on disable (Leaflet side).
+ L.Draggable._dragging = false;
+ this.onCancelDrawing();
+ this.endDrawing();
+ },
+
+ endDrawing: function () {
+ this._drawing = false;
+ this.tools.unregisterForDrawing(this);
+ this.onEndDrawing();
+ },
+
+ onDrawingClick: function (e) {
+ if (!this.drawing()) return;
+ L.Editable.makeCancellable(e);
+ // 🍂namespace Editable
+ // 🍂section Drawing events
+ // 🍂event editable:drawing:click: CancelableEvent
+ // Fired when user `click` while drawing, before any internal action is being processed.
+ this.fireAndForward('editable:drawing:click', e);
+ if (e._cancelled) return;
+ if (!this.isConnected()) this.connect(e);
+ this.processDrawingClick(e);
+ },
+
+ isConnected: function () {
+ return this.map.hasLayer(this.feature);
+ },
+
+ connect: function () {
+ this.tools.connectCreatedToMap(this.feature);
+ this.tools.editLayer.addLayer(this.editLayer);
+ },
+
+ onMove: function (e) {
+ // 🍂namespace Editable
+ // 🍂section Drawing events
+ // 🍂event editable:drawing:move: Event
+ // Fired when `move` mouse while drawing, while dragging a marker, and while dragging a vertex.
+ this.fireAndForward('editable:drawing:move', e);
+ },
+
+ onDrawingMouseMove: function (e) {
+ this.onMove(e);
+ },
+
+ _getEvents: function () {
+ return {
+ dragstart: this.onDragStart,
+ drag: this.onDrag,
+ dragend: this.onDragEnd,
+ remove: this.disable
+ };
+ },
+
+ onDragStart: function (e) {
+ this.onEditing();
+ // 🍂namespace Editable
+ // 🍂event editable:dragstart: Event
+ // Fired before a path feature is dragged.
+ this.fireAndForward('editable:dragstart', e);
+ },
+
+ onDrag: function (e) {
+ this.onMove(e);
+ // 🍂namespace Editable
+ // 🍂event editable:drag: Event
+ // Fired when a path feature is being dragged.
+ this.fireAndForward('editable:drag', e);
+ },
+
+ onDragEnd: function (e) {
+ // 🍂namespace Editable
+ // 🍂event editable:dragend: Event
+ // Fired after a path feature has been dragged.
+ this.fireAndForward('editable:dragend', e);
+ }
+
+ });
+
+ // 🍂namespace Editable; 🍂class MarkerEditor; 🍂aka L.Editable.MarkerEditor
+ // 🍂inherits BaseEditor
+ // Editor for Marker.
+ L.Editable.MarkerEditor = L.Editable.BaseEditor.extend({
+
+ onDrawingMouseMove: function (e) {
+ L.Editable.BaseEditor.prototype.onDrawingMouseMove.call(this, e);
+ if (this._drawing) this.feature.setLatLng(e.latlng);
+ },
+
+ processDrawingClick: function (e) {
+ // 🍂namespace Editable
+ // 🍂section Drawing events
+ // 🍂event editable:drawing:clicked: Event
+ // Fired when user `click` while drawing, after all internal actions.
+ this.fireAndForward('editable:drawing:clicked', e);
+ this.commitDrawing(e);
+ },
+
+ connect: function (e) {
+ // On touch, the latlng has not been updated because there is
+ // no mousemove.
+ if (e) this.feature._latlng = e.latlng;
+ L.Editable.BaseEditor.prototype.connect.call(this, e);
+ }
+
+ });
+
+ // 🍂namespace Editable; 🍂class PathEditor; 🍂aka L.Editable.PathEditor
+ // 🍂inherits BaseEditor
+ // Base class for all path editors.
+ L.Editable.PathEditor = L.Editable.BaseEditor.extend({
+
+ CLOSED: false,
+ MIN_VERTEX: 2,
+
+ addHooks: function () {
+ L.Editable.BaseEditor.prototype.addHooks.call(this);
+ if (this.feature) this.initVertexMarkers();
+ return this;
+ },
+
+ initVertexMarkers: function (latlngs) {
+ if (!this.enabled()) return;
+ latlngs = latlngs || this.getLatLngs();
+ if (isFlat(latlngs)) this.addVertexMarkers(latlngs);
+ else for (var i = 0; i < latlngs.length; i++) this.initVertexMarkers(latlngs[i]);
+ },
+
+ getLatLngs: function () {
+ return this.feature.getLatLngs();
+ },
+
+ // 🍂method reset()
+ // Rebuild edit elements (Vertex, MiddleMarker, etc.).
+ reset: function () {
+ this.editLayer.clearLayers();
+ this.initVertexMarkers();
+ },
+
+ addVertexMarker: function (latlng, latlngs) {
+ return new this.tools.options.vertexMarkerClass(latlng, latlngs, this);
+ },
+
+ onNewVertex: function (vertex) {
+ // 🍂namespace Editable
+ // 🍂section Vertex events
+ // 🍂event editable:vertex:new: VertexEvent
+ // Fired when a new vertex is created.
+ this.fireAndForward('editable:vertex:new', {latlng: vertex.latlng, vertex: vertex});
+ },
+
+ addVertexMarkers: function (latlngs) {
+ for (var i = 0; i < latlngs.length; i++) {
+ this.addVertexMarker(latlngs[i], latlngs);
+ }
+ },
+
+ refreshVertexMarkers: function (latlngs) {
+ latlngs = latlngs || this.getDefaultLatLngs();
+ for (var i = 0; i < latlngs.length; i++) {
+ latlngs[i].__vertex.update();
+ }
+ },
+
+ addMiddleMarker: function (left, right, latlngs) {
+ return new this.tools.options.middleMarkerClass(left, right, latlngs, this);
+ },
+
+ onVertexMarkerClick: function (e) {
+ L.Editable.makeCancellable(e);
+ // 🍂namespace Editable
+ // 🍂section Vertex events
+ // 🍂event editable:vertex:click: CancelableVertexEvent
+ // Fired when a `click` is issued on a vertex, before any internal action is being processed.
+ this.fireAndForward('editable:vertex:click', e);
+ if (e._cancelled) return;
+ if (this.tools.drawing() && this.tools._drawingEditor !== this) return;
+ var index = e.vertex.getIndex(), commit;
+ if (e.originalEvent.ctrlKey) {
+ this.onVertexMarkerCtrlClick(e);
+ } else if (e.originalEvent.altKey) {
+ this.onVertexMarkerAltClick(e);
+ } else if (e.originalEvent.shiftKey) {
+ this.onVertexMarkerShiftClick(e);
+ } else if (e.originalEvent.metaKey) {
+ this.onVertexMarkerMetaKeyClick(e);
+ } else if (index === e.vertex.getLastIndex() && this._drawing === L.Editable.FORWARD) {
+ if (index >= this.MIN_VERTEX - 1) commit = true;
+ } else if (index === 0 && this._drawing === L.Editable.BACKWARD && this._drawnLatLngs.length >= this.MIN_VERTEX) {
+ commit = true;
+ } else if (index === 0 && this._drawing === L.Editable.FORWARD && this._drawnLatLngs.length >= this.MIN_VERTEX && this.CLOSED) {
+ commit = true; // Allow to close on first point also for polygons
+ } else {
+ this.onVertexRawMarkerClick(e);
+ }
+ // 🍂namespace Editable
+ // 🍂section Vertex events
+ // 🍂event editable:vertex:clicked: VertexEvent
+ // Fired when a `click` is issued on a vertex, after all internal actions.
+ this.fireAndForward('editable:vertex:clicked', e);
+ if (commit) this.commitDrawing(e);
+ },
+
+ onVertexRawMarkerClick: function (e) {
+ // 🍂namespace Editable
+ // 🍂section Vertex events
+ // 🍂event editable:vertex:rawclick: CancelableVertexEvent
+ // Fired when a `click` is issued on a vertex without any special key and without being in drawing mode.
+ this.fireAndForward('editable:vertex:rawclick', e);
+ if (e._cancelled) return;
+ if (!this.vertexCanBeDeleted(e.vertex)) return;
+ e.vertex.delete();
+ },
+
+ vertexCanBeDeleted: function (vertex) {
+ return vertex.latlngs.length > this.MIN_VERTEX;
+ },
+
+ onVertexDeleted: function (e) {
+ // 🍂namespace Editable
+ // 🍂section Vertex events
+ // 🍂event editable:vertex:deleted: VertexEvent
+ // Fired after a vertex has been deleted by user.
+ this.fireAndForward('editable:vertex:deleted', e);
+ },
+
+ onVertexMarkerCtrlClick: function (e) {
+ // 🍂namespace Editable
+ // 🍂section Vertex events
+ // 🍂event editable:vertex:ctrlclick: VertexEvent
+ // Fired when a `click` with `ctrlKey` is issued on a vertex.
+ this.fireAndForward('editable:vertex:ctrlclick', e);
+ },
+
+ onVertexMarkerShiftClick: function (e) {
+ // 🍂namespace Editable
+ // 🍂section Vertex events
+ // 🍂event editable:vertex:shiftclick: VertexEvent
+ // Fired when a `click` with `shiftKey` is issued on a vertex.
+ this.fireAndForward('editable:vertex:shiftclick', e);
+ },
+
+ onVertexMarkerMetaKeyClick: function (e) {
+ // 🍂namespace Editable
+ // 🍂section Vertex events
+ // 🍂event editable:vertex:metakeyclick: VertexEvent
+ // Fired when a `click` with `metaKey` is issued on a vertex.
+ this.fireAndForward('editable:vertex:metakeyclick', e);
+ },
+
+ onVertexMarkerAltClick: function (e) {
+ // 🍂namespace Editable
+ // 🍂section Vertex events
+ // 🍂event editable:vertex:altclick: VertexEvent
+ // Fired when a `click` with `altKey` is issued on a vertex.
+ this.fireAndForward('editable:vertex:altclick', e);
+ },
+
+ onVertexMarkerContextMenu: function (e) {
+ // 🍂namespace Editable
+ // 🍂section Vertex events
+ // 🍂event editable:vertex:contextmenu: VertexEvent
+ // Fired when a `contextmenu` is issued on a vertex.
+ this.fireAndForward('editable:vertex:contextmenu', e);
+ },
+
+ onVertexMarkerMouseDown: function (e) {
+ // 🍂namespace Editable
+ // 🍂section Vertex events
+ // 🍂event editable:vertex:mousedown: VertexEvent
+ // Fired when user `mousedown` a vertex.
+ this.fireAndForward('editable:vertex:mousedown', e);
+ },
+
+ onVertexMarkerMouseOver: function (e) {
+ // 🍂namespace Editable
+ // 🍂section Vertex events
+ // 🍂event editable:vertex:mouseover: VertexEvent
+ // Fired when a user's mouse enters the vertex
+ this.fireAndForward('editable:vertex:mouseover', e);
+ },
+
+ onVertexMarkerMouseOut: function (e) {
+ // 🍂namespace Editable
+ // 🍂section Vertex events
+ // 🍂event editable:vertex:mouseout: VertexEvent
+ // Fired when a user's mouse leaves the vertex
+ this.fireAndForward('editable:vertex:mouseout', e);
+ },
+
+ onMiddleMarkerMouseDown: function (e) {
+ // 🍂namespace Editable
+ // 🍂section MiddleMarker events
+ // 🍂event editable:middlemarker:mousedown: VertexEvent
+ // Fired when user `mousedown` a middle marker.
+ this.fireAndForward('editable:middlemarker:mousedown', e);
+ },
+
+ onVertexMarkerDrag: function (e) {
+ this.onMove(e);
+ if (this.feature._bounds) this.extendBounds(e);
+ // 🍂namespace Editable
+ // 🍂section Vertex events
+ // 🍂event editable:vertex:drag: VertexEvent
+ // Fired when a vertex is dragged by user.
+ this.fireAndForward('editable:vertex:drag', e);
+ },
+
+ onVertexMarkerDragStart: function (e) {
+ // 🍂namespace Editable
+ // 🍂section Vertex events
+ // 🍂event editable:vertex:dragstart: VertexEvent
+ // Fired before a vertex is dragged by user.
+ this.fireAndForward('editable:vertex:dragstart', e);
+ },
+
+ onVertexMarkerDragEnd: function (e) {
+ // 🍂namespace Editable
+ // 🍂section Vertex events
+ // 🍂event editable:vertex:dragend: VertexEvent
+ // Fired after a vertex is dragged by user.
+ this.fireAndForward('editable:vertex:dragend', e);
+ },
+
+ setDrawnLatLngs: function (latlngs) {
+ this._drawnLatLngs = latlngs || this.getDefaultLatLngs();
+ },
+
+ startDrawing: function () {
+ if (!this._drawnLatLngs) this.setDrawnLatLngs();
+ L.Editable.BaseEditor.prototype.startDrawing.call(this);
+ },
+
+ startDrawingForward: function () {
+ this.startDrawing();
+ },
+
+ endDrawing: function () {
+ this.tools.detachForwardLineGuide();
+ this.tools.detachBackwardLineGuide();
+ if (this._drawnLatLngs && this._drawnLatLngs.length < this.MIN_VERTEX) this.deleteShape(this._drawnLatLngs);
+ L.Editable.BaseEditor.prototype.endDrawing.call(this);
+ delete this._drawnLatLngs;
+ },
+
+ addLatLng: function (latlng) {
+ if (this._drawing === L.Editable.FORWARD) this._drawnLatLngs.push(latlng);
+ else this._drawnLatLngs.unshift(latlng);
+ this.feature._bounds.extend(latlng);
+ var vertex = this.addVertexMarker(latlng, this._drawnLatLngs);
+ this.onNewVertex(vertex);
+ this.refresh();
+ },
+
+ newPointForward: function (latlng) {
+ this.addLatLng(latlng);
+ this.tools.attachForwardLineGuide();
+ this.tools.anchorForwardLineGuide(latlng);
+ },
+
+ newPointBackward: function (latlng) {
+ this.addLatLng(latlng);
+ this.tools.anchorBackwardLineGuide(latlng);
+ },
+
+ // 🍂namespace PathEditor
+ // 🍂method push()
+ // Programmatically add a point while drawing.
+ push: function (latlng) {
+ if (!latlng) return console.error('L.Editable.PathEditor.push expect a valid latlng as parameter');
+ if (this._drawing === L.Editable.FORWARD) this.newPointForward(latlng);
+ else this.newPointBackward(latlng);
+ },
+
+ removeLatLng: function (latlng) {
+ latlng.__vertex.delete();
+ this.refresh();
+ },
+
+ // 🍂method pop(): L.LatLng or null
+ // Programmatically remove last point (if any) while drawing.
+ pop: function () {
+ if (this._drawnLatLngs.length <= 1) return;
+ var latlng;
+ if (this._drawing === L.Editable.FORWARD) latlng = this._drawnLatLngs[this._drawnLatLngs.length - 1];
+ else latlng = this._drawnLatLngs[0];
+ this.removeLatLng(latlng);
+ if (this._drawing === L.Editable.FORWARD) this.tools.anchorForwardLineGuide(this._drawnLatLngs[this._drawnLatLngs.length - 1]);
+ else this.tools.anchorForwardLineGuide(this._drawnLatLngs[0]);
+ return latlng;
+ },
+
+ processDrawingClick: function (e) {
+ if (e.vertex && e.vertex.editor === this) return;
+ if (this._drawing === L.Editable.FORWARD) this.newPointForward(e.latlng);
+ else this.newPointBackward(e.latlng);
+ this.fireAndForward('editable:drawing:clicked', e);
+ },
+
+ onDrawingMouseMove: function (e) {
+ L.Editable.BaseEditor.prototype.onDrawingMouseMove.call(this, e);
+ if (this._drawing) {
+ this.tools.moveForwardLineGuide(e.latlng);
+ this.tools.moveBackwardLineGuide(e.latlng);
+ }
+ },
+
+ refresh: function () {
+ this.feature.redraw();
+ this.onEditing();
+ },
+
+ // 🍂namespace PathEditor
+ // 🍂method newShape(latlng?: L.LatLng)
+ // Add a new shape (Polyline, Polygon) in a multi, and setup up drawing tools to draw it;
+ // if optional `latlng` is given, start a path at this point.
+ newShape: function (latlng) {
+ var shape = this.addNewEmptyShape();
+ if (!shape) return;
+ this.setDrawnLatLngs(shape[0] || shape); // Polygon or polyline
+ this.startDrawingForward();
+ // 🍂namespace Editable
+ // 🍂section Shape events
+ // 🍂event editable:shape:new: ShapeEvent
+ // Fired when a new shape is created in a multi (Polygon or Polyline).
+ this.fireAndForward('editable:shape:new', {shape: shape});
+ if (latlng) this.newPointForward(latlng);
+ },
+
+ deleteShape: function (shape, latlngs) {
+ var e = {shape: shape};
+ L.Editable.makeCancellable(e);
+ // 🍂namespace Editable
+ // 🍂section Shape events
+ // 🍂event editable:shape:delete: CancelableShapeEvent
+ // Fired before a new shape is deleted in a multi (Polygon or Polyline).
+ this.fireAndForward('editable:shape:delete', e);
+ if (e._cancelled) return;
+ shape = this._deleteShape(shape, latlngs);
+ if (this.ensureNotFlat) this.ensureNotFlat(); // Polygon.
+ this.feature.setLatLngs(this.getLatLngs()); // Force bounds reset.
+ this.refresh();
+ this.reset();
+ // 🍂namespace Editable
+ // 🍂section Shape events
+ // 🍂event editable:shape:deleted: ShapeEvent
+ // Fired after a new shape is deleted in a multi (Polygon or Polyline).
+ this.fireAndForward('editable:shape:deleted', {shape: shape});
+ return shape;
+ },
+
+ _deleteShape: function (shape, latlngs) {
+ latlngs = latlngs || this.getLatLngs();
+ if (!latlngs.length) return;
+ var self = this,
+ inplaceDelete = function (latlngs, shape) {
+ // Called when deleting a flat latlngs
+ shape = latlngs.splice(0, Number.MAX_VALUE);
+ return shape;
+ },
+ spliceDelete = function (latlngs, shape) {
+ // Called when removing a latlngs inside an array
+ latlngs.splice(latlngs.indexOf(shape), 1);
+ if (!latlngs.length) self._deleteShape(latlngs);
+ return shape;
+ };
+ if (latlngs === shape) return inplaceDelete(latlngs, shape);
+ for (var i = 0; i < latlngs.length; i++) {
+ if (latlngs[i] === shape) return spliceDelete(latlngs, shape);
+ else if (latlngs[i].indexOf(shape) !== -1) return spliceDelete(latlngs[i], shape);
+ }
+ },
+
+ // 🍂namespace PathEditor
+ // 🍂method deleteShapeAt(latlng: L.LatLng): Array
+ // Remove a path shape at the given `latlng`.
+ deleteShapeAt: function (latlng) {
+ var shape = this.feature.shapeAt(latlng);
+ if (shape) return this.deleteShape(shape);
+ },
+
+ // 🍂method appendShape(shape: Array)
+ // Append a new shape to the Polygon or Polyline.
+ appendShape: function (shape) {
+ this.insertShape(shape);
+ },
+
+ // 🍂method prependShape(shape: Array)
+ // Prepend a new shape to the Polygon or Polyline.
+ prependShape: function (shape) {
+ this.insertShape(shape, 0);
+ },
+
+ // 🍂method insertShape(shape: Array, index: int)
+ // Insert a new shape to the Polygon or Polyline at given index (default is to append).
+ insertShape: function (shape, index) {
+ this.ensureMulti();
+ shape = this.formatShape(shape);
+ if (typeof index === 'undefined') index = this.feature._latlngs.length;
+ this.feature._latlngs.splice(index, 0, shape);
+ this.feature.redraw();
+ if (this._enabled) this.reset();
+ },
+
+ extendBounds: function (e) {
+ this.feature._bounds.extend(e.vertex.latlng);
+ },
+
+ onDragStart: function (e) {
+ this.editLayer.clearLayers();
+ L.Editable.BaseEditor.prototype.onDragStart.call(this, e);
+ },
+
+ onDragEnd: function (e) {
+ this.initVertexMarkers();
+ L.Editable.BaseEditor.prototype.onDragEnd.call(this, e);
+ }
+
+ });
+
+ // 🍂namespace Editable; 🍂class PolylineEditor; 🍂aka L.Editable.PolylineEditor
+ // 🍂inherits PathEditor
+ L.Editable.PolylineEditor = L.Editable.PathEditor.extend({
+
+ startDrawingBackward: function () {
+ this._drawing = L.Editable.BACKWARD;
+ this.startDrawing();
+ },
+
+ // 🍂method continueBackward(latlngs?: Array)
+ // Set up drawing tools to continue the line backward.
+ continueBackward: function (latlngs) {
+ if (this.drawing()) return;
+ latlngs = latlngs || this.getDefaultLatLngs();
+ this.setDrawnLatLngs(latlngs);
+ if (latlngs.length > 0) {
+ this.tools.attachBackwardLineGuide();
+ this.tools.anchorBackwardLineGuide(latlngs[0]);
+ }
+ this.startDrawingBackward();
+ },
+
+ // 🍂method continueForward(latlngs?: Array)
+ // Set up drawing tools to continue the line forward.
+ continueForward: function (latlngs) {
+ if (this.drawing()) return;
+ latlngs = latlngs || this.getDefaultLatLngs();
+ this.setDrawnLatLngs(latlngs);
+ if (latlngs.length > 0) {
+ this.tools.attachForwardLineGuide();
+ this.tools.anchorForwardLineGuide(latlngs[latlngs.length - 1]);
+ }
+ this.startDrawingForward();
+ },
+
+ getDefaultLatLngs: function (latlngs) {
+ latlngs = latlngs || this.feature._latlngs;
+ if (!latlngs.length || latlngs[0] instanceof L.LatLng) return latlngs;
+ else return this.getDefaultLatLngs(latlngs[0]);
+ },
+
+ ensureMulti: function () {
+ if (this.feature._latlngs.length && isFlat(this.feature._latlngs)) {
+ this.feature._latlngs = [this.feature._latlngs];
+ }
+ },
+
+ addNewEmptyShape: function () {
+ if (this.feature._latlngs.length) {
+ var shape = [];
+ this.appendShape(shape);
+ return shape;
+ } else {
+ return this.feature._latlngs;
+ }
+ },
+
+ formatShape: function (shape) {
+ if (isFlat(shape)) return shape;
+ else if (shape[0]) return this.formatShape(shape[0]);
+ },
+
+ // 🍂method splitShape(latlngs?: Array, index: int)
+ // Split the given `latlngs` shape at index `index` and integrate new shape in instance `latlngs`.
+ splitShape: function (shape, index) {
+ if (!index || index >= shape.length - 1) return;
+ this.ensureMulti();
+ var shapeIndex = this.feature._latlngs.indexOf(shape);
+ if (shapeIndex === -1) return;
+ var first = shape.slice(0, index + 1),
+ second = shape.slice(index);
+ // We deal with reference, we don't want twice the same latlng around.
+ second[0] = L.latLng(second[0].lat, second[0].lng, second[0].alt);
+ this.feature._latlngs.splice(shapeIndex, 1, first, second);
+ this.refresh();
+ this.reset();
+ }
+
+ });
+
+ // 🍂namespace Editable; 🍂class PolygonEditor; 🍂aka L.Editable.PolygonEditor
+ // 🍂inherits PathEditor
+ L.Editable.PolygonEditor = L.Editable.PathEditor.extend({
+
+ CLOSED: true,
+ MIN_VERTEX: 3,
+
+ newPointForward: function (latlng) {
+ L.Editable.PathEditor.prototype.newPointForward.call(this, latlng);
+ if (!this.tools.backwardLineGuide._latlngs.length) this.tools.anchorBackwardLineGuide(latlng);
+ if (this._drawnLatLngs.length === 2) this.tools.attachBackwardLineGuide();
+ },
+
+ addNewEmptyHole: function (latlng) {
+ this.ensureNotFlat();
+ var latlngs = this.feature.shapeAt(latlng);
+ if (!latlngs) return;
+ var holes = [];
+ latlngs.push(holes);
+ return holes;
+ },
+
+ // 🍂method newHole(latlng?: L.LatLng, index: int)
+ // Set up drawing tools for creating a new hole on the Polygon. If the `latlng` param is given, a first point is created.
+ newHole: function (latlng) {
+ var holes = this.addNewEmptyHole(latlng);
+ if (!holes) return;
+ this.setDrawnLatLngs(holes);
+ this.startDrawingForward();
+ if (latlng) this.newPointForward(latlng);
+ },
+
+ addNewEmptyShape: function () {
+ if (this.feature._latlngs.length && this.feature._latlngs[0].length) {
+ var shape = [];
+ this.appendShape(shape);
+ return shape;
+ } else {
+ return this.feature._latlngs;
+ }
+ },
+
+ ensureMulti: function () {
+ if (this.feature._latlngs.length && isFlat(this.feature._latlngs[0])) {
+ this.feature._latlngs = [this.feature._latlngs];
+ }
+ },
+
+ ensureNotFlat: function () {
+ if (!this.feature._latlngs.length || isFlat(this.feature._latlngs)) this.feature._latlngs = [this.feature._latlngs];
+ },
+
+ vertexCanBeDeleted: function (vertex) {
+ var parent = this.feature.parentShape(vertex.latlngs),
+ idx = L.Util.indexOf(parent, vertex.latlngs);
+ if (idx > 0) return true; // Holes can be totally deleted without removing the layer itself.
+ return L.Editable.PathEditor.prototype.vertexCanBeDeleted.call(this, vertex);
+ },
+
+ getDefaultLatLngs: function () {
+ if (!this.feature._latlngs.length) this.feature._latlngs.push([]);
+ return this.feature._latlngs[0];
+ },
+
+ formatShape: function (shape) {
+ // [[1, 2], [3, 4]] => must be nested
+ // [] => must be nested
+ // [[]] => is already nested
+ if (isFlat(shape) && (!shape[0] || shape[0].length !== 0)) return [shape];
+ else return shape;
+ }
+
+ });
+
+ // 🍂namespace Editable; 🍂class RectangleEditor; 🍂aka L.Editable.RectangleEditor
+ // 🍂inherits PathEditor
+ L.Editable.RectangleEditor = L.Editable.PathEditor.extend({
+
+ CLOSED: true,
+ MIN_VERTEX: 4,
+
+ options: {
+ skipMiddleMarkers: true
+ },
+
+ extendBounds: function (e) {
+ var index = e.vertex.getIndex(),
+ next = e.vertex.getNext(),
+ previous = e.vertex.getPrevious(),
+ oppositeIndex = (index + 2) % 4,
+ opposite = e.vertex.latlngs[oppositeIndex],
+ bounds = new L.LatLngBounds(e.latlng, opposite);
+ // Update latlngs by hand to preserve order.
+ previous.latlng.update([e.latlng.lat, opposite.lng]);
+ next.latlng.update([opposite.lat, e.latlng.lng]);
+ this.updateBounds(bounds);
+ this.refreshVertexMarkers();
+ },
+
+ onDrawingMouseDown: function (e) {
+ L.Editable.PathEditor.prototype.onDrawingMouseDown.call(this, e);
+ this.connect();
+ var latlngs = this.getDefaultLatLngs();
+ // L.Polygon._convertLatLngs removes last latlng if it equals first point,
+ // which is the case here as all latlngs are [0, 0]
+ if (latlngs.length === 3) latlngs.push(e.latlng);
+ var bounds = new L.LatLngBounds(e.latlng, e.latlng);
+ this.updateBounds(bounds);
+ this.updateLatLngs(bounds);
+ this.refresh();
+ this.reset();
+ // Stop dragging map.
+ // L.Draggable has two workflows:
+ // - mousedown => mousemove => mouseup
+ // - touchstart => touchmove => touchend
+ // Problem: L.Map.Tap does not allow us to listen to touchstart, so we only
+ // can deal with mousedown, but then when in a touch device, we are dealing with
+ // simulated events (actually simulated by L.Map.Tap), which are no more taken
+ // into account by L.Draggable.
+ // Ref.: https://github.com/Leaflet/Leaflet.Editable/issues/103
+ e.originalEvent._simulated = false;
+ this.map.dragging._draggable._onUp(e.originalEvent);
+ // Now transfer ongoing drag action to the bottom right corner.
+ // Should we refine which corner will handle the drag according to
+ // drag direction?
+ latlngs[3].__vertex.dragging._draggable._onDown(e.originalEvent);
+ },
+
+ onDrawingMouseUp: function (e) {
+ this.commitDrawing(e);
+ e.originalEvent._simulated = false;
+ L.Editable.PathEditor.prototype.onDrawingMouseUp.call(this, e);
+ },
+
+ onDrawingMouseMove: function (e) {
+ e.originalEvent._simulated = false;
+ L.Editable.PathEditor.prototype.onDrawingMouseMove.call(this, e);
+ },
+
+
+ getDefaultLatLngs: function (latlngs) {
+ return latlngs || this.feature._latlngs[0];
+ },
+
+ updateBounds: function (bounds) {
+ this.feature._bounds = bounds;
+ },
+
+ updateLatLngs: function (bounds) {
+ var latlngs = this.getDefaultLatLngs(),
+ newLatlngs = this.feature._boundsToLatLngs(bounds);
+ // Keep references.
+ for (var i = 0; i < latlngs.length; i++) {
+ latlngs[i].update(newLatlngs[i]);
+ }
+ }
+
+ });
+
+ // 🍂namespace Editable; 🍂class CircleEditor; 🍂aka L.Editable.CircleEditor
+ // 🍂inherits PathEditor
+ L.Editable.CircleEditor = L.Editable.PathEditor.extend({
+
+ MIN_VERTEX: 2,
+
+ options: {
+ skipMiddleMarkers: true
+ },
+
+ initialize: function (map, feature, options) {
+ L.Editable.PathEditor.prototype.initialize.call(this, map, feature, options);
+ this._resizeLatLng = this.computeResizeLatLng();
+ },
+
+ computeResizeLatLng: function () {
+ // While circle is not added to the map, _radius is not set.
+ var delta = (this.feature._radius || this.feature._mRadius) * Math.cos(Math.PI / 4),
+ point = this.map.project(this.feature._latlng);
+ return this.map.unproject([point.x + delta, point.y - delta]);
+ },
+
+ updateResizeLatLng: function () {
+ this._resizeLatLng.update(this.computeResizeLatLng());
+ this._resizeLatLng.__vertex.update();
+ },
+
+ getLatLngs: function () {
+ return [this.feature._latlng, this._resizeLatLng];
+ },
+
+ getDefaultLatLngs: function () {
+ return this.getLatLngs();
+ },
+
+ onVertexMarkerDrag: function (e) {
+ if (e.vertex.getIndex() === 1) this.resize(e);
+ else this.updateResizeLatLng(e);
+ L.Editable.PathEditor.prototype.onVertexMarkerDrag.call(this, e);
+ },
+
+ resize: function (e) {
+ var radius = this.feature._latlng.distanceTo(e.latlng);
+ this.feature.setRadius(radius);
+ },
+
+ onDrawingMouseDown: function (e) {
+ L.Editable.PathEditor.prototype.onDrawingMouseDown.call(this, e);
+ this._resizeLatLng.update(e.latlng);
+ this.feature._latlng.update(e.latlng);
+ this.connect();
+ // Stop dragging map.
+ e.originalEvent._simulated = false;
+ this.map.dragging._draggable._onUp(e.originalEvent);
+ // Now transfer ongoing drag action to the radius handler.
+ this._resizeLatLng.__vertex.dragging._draggable._onDown(e.originalEvent);
+ },
+
+ onDrawingMouseUp: function (e) {
+ this.commitDrawing(e);
+ e.originalEvent._simulated = false;
+ L.Editable.PathEditor.prototype.onDrawingMouseUp.call(this, e);
+ },
+
+ onDrawingMouseMove: function (e) {
+ e.originalEvent._simulated = false;
+ L.Editable.PathEditor.prototype.onDrawingMouseMove.call(this, e);
+ },
+
+ onDrag: function (e) {
+ L.Editable.PathEditor.prototype.onDrag.call(this, e);
+ this.feature.dragging.updateLatLng(this._resizeLatLng);
+ }
+
+ });
+
+ // 🍂namespace Editable; 🍂class EditableMixin
+ // `EditableMixin` is included to `L.Polyline`, `L.Polygon`, `L.Rectangle`, `L.Circle`
+ // and `L.Marker`. It adds some methods to them.
+ // *When editing is enabled, the editor is accessible on the instance with the
+ // `editor` property.*
+ var EditableMixin = {
+
+ createEditor: function (map) {
+ map = map || this._map;
+ var tools = (this.options.editOptions || {}).editTools || map.editTools;
+ if (!tools) throw Error('Unable to detect Editable instance.');
+ var Klass = this.options.editorClass || this.getEditorClass(tools);
+ return new Klass(map, this, this.options.editOptions);
+ },
+
+ // 🍂method enableEdit(map?: L.Map): this.editor
+ // Enable editing, by creating an editor if not existing, and then calling `enable` on it.
+ enableEdit: function (map) {
+ if (!this.editor) this.createEditor(map);
+ this.editor.enable();
+ return this.editor;
+ },
+
+ // 🍂method editEnabled(): boolean
+ // Return true if current instance has an editor attached, and this editor is enabled.
+ editEnabled: function () {
+ return this.editor && this.editor.enabled();
+ },
+
+ // 🍂method disableEdit()
+ // Disable editing, also remove the editor property reference.
+ disableEdit: function () {
+ if (this.editor) {
+ this.editor.disable();
+ delete this.editor;
+ }
+ },
+
+ // 🍂method toggleEdit()
+ // Enable or disable editing, according to current status.
+ toggleEdit: function () {
+ if (this.editEnabled()) this.disableEdit();
+ else this.enableEdit();
+ },
+
+ _onEditableAdd: function () {
+ if (this.editor) this.enableEdit();
+ }
+
+ };
+
+ var PolylineMixin = {
+
+ getEditorClass: function (tools) {
+ return (tools && tools.options.polylineEditorClass) ? tools.options.polylineEditorClass : L.Editable.PolylineEditor;
+ },
+
+ shapeAt: function (latlng, latlngs) {
+ // We can have those cases:
+ // - latlngs are just a flat array of latlngs, use this
+ // - latlngs is an array of arrays of latlngs, loop over
+ var shape = null;
+ latlngs = latlngs || this._latlngs;
+ if (!latlngs.length) return shape;
+ else if (isFlat(latlngs) && this.isInLatLngs(latlng, latlngs)) shape = latlngs;
+ else for (var i = 0; i < latlngs.length; i++) if (this.isInLatLngs(latlng, latlngs[i])) return latlngs[i];
+ return shape;
+ },
+
+ isInLatLngs: function (l, latlngs) {
+ if (!latlngs) return false;
+ var i, k, len, part = [], p,
+ w = this._clickTolerance();
+ this._projectLatlngs(latlngs, part, this._pxBounds);
+ part = part[0];
+ p = this._map.latLngToLayerPoint(l);
+
+ if (!this._pxBounds.contains(p)) { return false; }
+ for (i = 1, len = part.length, k = 0; i < len; k = i++) {
+
+ if (L.LineUtil.pointToSegmentDistance(p, part[k], part[i]) <= w) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ };
+
+ var PolygonMixin = {
+
+ getEditorClass: function (tools) {
+ return (tools && tools.options.polygonEditorClass) ? tools.options.polygonEditorClass : L.Editable.PolygonEditor;
+ },
+
+ shapeAt: function (latlng, latlngs) {
+ // We can have those cases:
+ // - latlngs are just a flat array of latlngs, use this
+ // - latlngs is an array of arrays of latlngs, this is a simple polygon (maybe with holes), use the first
+ // - latlngs is an array of arrays of arrays, this is a multi, loop over
+ var shape = null;
+ latlngs = latlngs || this._latlngs;
+ if (!latlngs.length) return shape;
+ else if (isFlat(latlngs) && this.isInLatLngs(latlng, latlngs)) shape = latlngs;
+ else if (isFlat(latlngs[0]) && this.isInLatLngs(latlng, latlngs[0])) shape = latlngs;
+ else for (var i = 0; i < latlngs.length; i++) if (this.isInLatLngs(latlng, latlngs[i][0])) return latlngs[i];
+ return shape;
+ },
+
+ isInLatLngs: function (l, latlngs) {
+ var inside = false, l1, l2, j, k, len2;
+
+ for (j = 0, len2 = latlngs.length, k = len2 - 1; j < len2; k = j++) {
+ l1 = latlngs[j];
+ l2 = latlngs[k];
+
+ if (((l1.lat > l.lat) !== (l2.lat > l.lat)) &&
+ (l.lng < (l2.lng - l1.lng) * (l.lat - l1.lat) / (l2.lat - l1.lat) + l1.lng)) {
+ inside = !inside;
+ }
+ }
+
+ return inside;
+ },
+
+ parentShape: function (shape, latlngs) {
+ latlngs = latlngs || this._latlngs;
+ if (!latlngs) return;
+ var idx = L.Util.indexOf(latlngs, shape);
+ if (idx !== -1) return latlngs;
+ for (var i = 0; i < latlngs.length; i++) {
+ idx = L.Util.indexOf(latlngs[i], shape);
+ if (idx !== -1) return latlngs[i];
+ }
+ }
+
+ };
+
+
+ var MarkerMixin = {
+
+ getEditorClass: function (tools) {
+ return (tools && tools.options.markerEditorClass) ? tools.options.markerEditorClass : L.Editable.MarkerEditor;
+ }
+
+ };
+
+ var RectangleMixin = {
+
+ getEditorClass: function (tools) {
+ return (tools && tools.options.rectangleEditorClass) ? tools.options.rectangleEditorClass : L.Editable.RectangleEditor;
+ }
+
+ };
+
+ var CircleMixin = {
+
+ getEditorClass: function (tools) {
+ return (tools && tools.options.circleEditorClass) ? tools.options.circleEditorClass : L.Editable.CircleEditor;
+ }
+
+ };
+
+ var keepEditable = function () {
+ // Make sure you can remove/readd an editable layer.
+ this.on('add', this._onEditableAdd);
+ };
+
+ var isFlat = L.LineUtil.isFlat || L.LineUtil._flat || L.Polyline._flat; // <=> 1.1 compat.
+
+
+ if (L.Polyline) {
+ L.Polyline.include(EditableMixin);
+ L.Polyline.include(PolylineMixin);
+ L.Polyline.addInitHook(keepEditable);
+ }
+ if (L.Polygon) {
+ L.Polygon.include(EditableMixin);
+ L.Polygon.include(PolygonMixin);
+ }
+ if (L.Marker) {
+ L.Marker.include(EditableMixin);
+ L.Marker.include(MarkerMixin);
+ L.Marker.addInitHook(keepEditable);
+ }
+ if (L.Rectangle) {
+ L.Rectangle.include(EditableMixin);
+ L.Rectangle.include(RectangleMixin);
+ }
+ if (L.Circle) {
+ L.Circle.include(EditableMixin);
+ L.Circle.include(CircleMixin);
+ }
+
+ L.LatLng.prototype.update = function (latlng) {
+ latlng = L.latLng(latlng);
+ this.lat = latlng.lat;
+ this.lng = latlng.lng;
+ }
+
+}, window));
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.editor.js b/www/wiki/extensions/Maps/resources/leaflet/leaflet.editor.js
new file mode 100644
index 00000000..6aadad18
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet.editor.js
@@ -0,0 +1,15 @@
+(function( $, mw ) {
+
+ $( document ).ready( function() {
+ var map = L.map('GeoJsonMap'/*, {editable: true}*/);
+
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+ attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
+ }).addTo(map);
+
+ var geoJsonLayer = L.geoJSON(GeoJson).addTo(map);
+
+ map.fitBounds(geoJsonLayer.getBounds());
+ } );
+
+})( window.jQuery, mediaWiki );
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/.jshintrc b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/.jshintrc
new file mode 100644
index 00000000..b7636119
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/.jshintrc
@@ -0,0 +1,12 @@
+{
+ "browser": true,
+ "curly": true,
+ "eqeqeq": true,
+ "undef": true,
+ "quotmark": "single",
+ "trailing": true,
+ "globals": {
+ "L": true,
+ "jQuery": true
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.css b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.css
new file mode 100644
index 00000000..c93b1bf9
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.css
@@ -0,0 +1,4 @@
+.leaflet-control-zoom-fullscreen { background-image: url(icon-fullscreen.png); }
+.leaflet-retina .leaflet-control-zoom-fullscreen { background-image: url(icon-fullscreen-2x.png); background-size: 26px 26px; }
+.leaflet-container:-webkit-full-screen { width: 100% !important; height: 100% !important; z-index: 99999; }
+.leaflet-pseudo-fullscreen { position: fixed !important; width: 100% !important; height: 100% !important; top: 0px !important; left: 0px !important; z-index: 99999; } \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.js b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.js
new file mode 100644
index 00000000..f1cd7ccc
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/Control.FullScreen.js
@@ -0,0 +1,164 @@
+(function() {
+
+L.Control.FullScreen = L.Control.extend({
+ options: {
+ position: 'topleft',
+ title: 'Full Screen',
+ forceSeparateButton: false,
+ forcePseudoFullscreen: false
+ },
+
+ onAdd: function (map) {
+ var className = 'leaflet-control-zoom-fullscreen', container;
+
+ if (map.zoomControl && !this.options.forceSeparateButton) {
+ container = map.zoomControl._container;
+ } else {
+ container = L.DomUtil.create('div', 'leaflet-bar');
+ }
+
+ this._createButton(this.options.title, className, container, this.toggleFullScreen, this);
+
+ return container;
+ },
+
+ _createButton: function (title, className, container, fn, context) {
+ var link = L.DomUtil.create('a', className, container);
+ link.href = '#';
+ link.title = title;
+
+ L.DomEvent
+ .addListener(link, 'click', L.DomEvent.stopPropagation)
+ .addListener(link, 'click', L.DomEvent.preventDefault)
+ .addListener(link, 'click', fn, context);
+
+ L.DomEvent
+ .addListener(container, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation)
+ .addListener(container, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault)
+ .addListener(container, fullScreenApi.fullScreenEventName, this._handleEscKey, context);
+
+ L.DomEvent
+ .addListener(document, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation)
+ .addListener(document, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault)
+ .addListener(document, fullScreenApi.fullScreenEventName, this._handleEscKey, context);
+
+ return link;
+ },
+
+ toggleFullScreen: function () {
+ var map = this._map;
+ map._exitFired = false;
+ if (map._isFullscreen) {
+ if (fullScreenApi.supportsFullScreen && !this.options.forcePseudoFullscreen) {
+ fullScreenApi.cancelFullScreen(map._container);
+ } else {
+ L.DomUtil.removeClass(map._container, 'leaflet-pseudo-fullscreen');
+ }
+ map.invalidateSize();
+ map.fire('exitFullscreen');
+ map._exitFired = true;
+ map._isFullscreen = false;
+ }
+ else {
+ if (fullScreenApi.supportsFullScreen && !this.options.forcePseudoFullscreen) {
+ fullScreenApi.requestFullScreen(map._container);
+ } else {
+ L.DomUtil.addClass(map._container, 'leaflet-pseudo-fullscreen');
+ }
+ map.invalidateSize();
+ map.fire('enterFullscreen');
+ map._isFullscreen = true;
+ }
+ },
+
+ _handleEscKey: function () {
+ var map = this._map;
+ if (!fullScreenApi.isFullScreen(map) && !map._exitFired) {
+ map.fire('exitFullscreen');
+ map._exitFired = true;
+ map._isFullscreen = false;
+ }
+ }
+});
+
+L.Map.addInitHook(function () {
+ if (this.options.fullscreenControl) {
+ this.fullscreenControl = L.control.fullscreen(this.options.fullscreenControlOptions);
+ this.addControl(this.fullscreenControl);
+ }
+});
+
+L.control.fullscreen = function (options) {
+ return new L.Control.FullScreen(options);
+};
+
+/*
+Native FullScreen JavaScript API
+-------------
+Assumes Mozilla naming conventions instead of W3C for now
+
+source : http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/
+
+*/
+
+ var
+ fullScreenApi = {
+ supportsFullScreen: false,
+ isFullScreen: function() { return false; },
+ requestFullScreen: function() {},
+ cancelFullScreen: function() {},
+ fullScreenEventName: '',
+ prefix: ''
+ },
+ browserPrefixes = 'webkit moz o ms khtml'.split(' ');
+
+ // check for native support
+ if (typeof document.exitFullscreen !== 'undefined') {
+ fullScreenApi.supportsFullScreen = true;
+ } else {
+ // check for fullscreen support by vendor prefix
+ for (var i = 0, il = browserPrefixes.length; i < il; i++ ) {
+ fullScreenApi.prefix = browserPrefixes[i];
+ if (typeof document[fullScreenApi.prefix + 'CancelFullScreen' ] !== 'undefined' ) {
+ fullScreenApi.supportsFullScreen = true;
+ break;
+ }
+ }
+ }
+
+ // update methods to do something useful
+ if (fullScreenApi.supportsFullScreen) {
+ fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange';
+ fullScreenApi.isFullScreen = function() {
+ switch (this.prefix) {
+ case '':
+ return document.fullScreen;
+ case 'webkit':
+ return document.webkitIsFullScreen;
+ default:
+ return document[this.prefix + 'FullScreen'];
+ }
+ };
+ fullScreenApi.requestFullScreen = function(el) {
+ return (this.prefix === '') ? el.requestFullscreen() : el[this.prefix + 'RequestFullScreen']();
+ };
+ fullScreenApi.cancelFullScreen = function(el) {
+ return (this.prefix === '') ? document.exitFullscreen() : document[this.prefix + 'CancelFullScreen']();
+ };
+ }
+
+ // jQuery plugin
+ if (typeof jQuery !== 'undefined') {
+ jQuery.fn.requestFullScreen = function() {
+ return this.each(function() {
+ var el = jQuery(this);
+ if (fullScreenApi.supportsFullScreen) {
+ fullScreenApi.requestFullScreen(el);
+ }
+ });
+ };
+ }
+
+ // export api
+ window.fullScreenApi = fullScreenApi;
+})();
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/LICENSE b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/LICENSE
new file mode 100644
index 00000000..07ddddcc
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2013, Bruno Bergot
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/README.md b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/README.md
new file mode 100644
index 00000000..a111801f
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/README.md
@@ -0,0 +1,68 @@
+Leaflet.Control.FullScreen
+============
+
+What ?
+------
+
+Simple plugin for Leaflet that adds fullscreen button to your maps.
+
+Inspired by http://elidupuis.github.com/leaflet.zoomfs/
+
+Use the native javascript fullscreen API http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/
+
+Released under the MIT License http://opensource.org/licenses/mit-license.php
+
+How ?
+------
+
+Include Control.FullScreen.js and Control.FullScreen.css in your page:
+
+``` html
+ <link rel="stylesheet" href="Control.FullScreen.css" />
+ <script src="Control.FullScreen.js"></script>
+```
+
+Add the fullscreen control to the map:
+
+``` js
+var map = new L.Map('map', {
+ fullscreenControl: true,
+ fullscreenControlOptions: {
+ position: 'topleft'
+ }
+});
+```
+
+If your map have a zoomControl the fullscreen button will be added at the bottom of this one.
+
+If your map doesn't have a zoomContron the fullscreen button will be added to topleft corner of the map (same as the zoomcontrol).
+
+__Events and options__:
+
+``` js
+// create a fullscreen button and add it to the map
+L.control.fullscreen({
+ position: 'topleft', // change the position of the button can be topleft, topright, bottomright or bottomleft, defaut topleft
+ title: 'Show me the fullscreen !', // change the title of the button, default Full Screen
+ forceSeparateButton: true, // force seperate button to detach from zoom buttons, default false
+ forcePseudoFullscreen: true // force use of pseudo full screen even if full screen API is available, default false
+}).addTo(map);
+
+// events are fired when entering or exiting fullscreen.
+map.on('enterFullscreen', function(){
+ console.log('entered fullscreen');
+});
+
+map.on('exitFullscreen', function(){
+ console.log('exited fullscreen');
+});
+```
+
+Where ?
+------
+
+Source code : https://github.com/brunob/leaflet.fullscreen
+
+Downloads : https://github.com/brunob/leaflet.fullscreen/releases
+
+Demo : http://brunob.github.com/leaflet.fullscreen/
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/bower.json b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/bower.json
new file mode 100644
index 00000000..6de9eee8
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/bower.json
@@ -0,0 +1,30 @@
+{
+ "name": "leaflet.fullscreen",
+ "version": "1.1.4",
+ "homepage": "https://github.com/brunob/leaflet.fullscreen",
+ "authors": [
+ "brunob <brunobergot@gmail.com>"
+ ],
+ "description": "Leaflet.Control.FullScreen for Leaflet",
+ "main": [
+ "Control.FullScreen.js",
+ "Control.FullScreen.css",
+ "icon-fullscreen.png",
+ "icon-fullscreen-2x.png"
+ ],
+ "keywords": [
+ "leaflet",
+ "plugins",
+ "maps",
+ "fullscreen"
+ ],
+ "license": "MIT",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests",
+ "index.html"
+ ]
+}
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen-2x.png b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen-2x.png
new file mode 100644
index 00000000..7320d953
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen-2x.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen.png b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen.png
new file mode 100644
index 00000000..17478145
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/icon-fullscreen.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/index.html b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/index.html
new file mode 100644
index 00000000..87b345c4
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/index.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset='utf-8'>
+ <title>Leaflet.Control.FullScreen Demo</title>
+ <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
+ <style type="text/css">
+ #map { width: 700px; height: 433px; }
+ .leaflet-control-zoom-fullscreen { background-image: url(icon-fullscreen.png); }
+ /* on selector per rule as explained here : http://www.sitepoint.com/html5-full-screen-api/ */
+ #map:-webkit-full-screen { width: 100% !important; height: 100% !important; z-index: 99999; }
+ #map:-moz-full-screen { width: 100% !important; height: 100% !important; z-index: 99999; }
+ #map:full-screen { width: 100% !important; height: 100% !important; z-index: 99999; }
+ .leaflet-pseudo-fullscreen { position: fixed !important; width: 100% !important; height: 100% !important; top: 0px !important; left: 0px !important; z-index: 99999; }
+ </style>
+ <script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
+ <script src="Control.FullScreen.js"></script>
+</head>
+<body>
+
+ <div id="map"></div>
+
+ <script>
+ var base = new L.TileLayer('http://{s}.www.toolserver.org/tiles/bw-mapnik/{z}/{x}/{y}.png', {
+ maxZoom: 18,
+ attribution: '&copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>'
+ });
+
+ var map = new L.Map('map', {
+ layers: [base],
+ center: new L.LatLng(48.5, -4.5),
+ zoom: 5,
+ fullscreenControl: true,
+ fullscreenControlOptions: { // optional
+ title:"Show me the fullscreen !"
+ }
+ });
+
+ // detect fullscreen toggling
+ map.on('enterFullscreen', function(){
+ if(window.console) window.console.log('enterFullscreen');
+ });
+ map.on('exitFullscreen', function(){
+ if(window.console) window.console.log('exitFullscreen');
+ });
+ </script>
+</body>
+</html>
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/package.json b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/package.json
new file mode 100644
index 00000000..e0c9aa7b
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet.fullscreen/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "leaflet.fullscreen",
+ "version": "1.1.4",
+ "description": "Simple plugin for Leaflet that adds fullscreen button to your maps.",
+ "main": "Control.FullScreen.js",
+ "scripts": {
+ "test": "jshint Control.FullScreen.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/brunob/leaflet.fullscreen.git"
+ },
+ "keywords": [
+ "leaflet",
+ "plugins",
+ "maps",
+ "fullscreen"
+ ],
+ "devDependencies": {
+ "jshint": "2.5.0"
+ },
+ "author": "b_b",
+ "license": "MIT License",
+ "readmeFilename": "README.md"
+}
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.markercluster/MarkerCluster.css b/www/wiki/extensions/Maps/resources/leaflet/leaflet.markercluster/MarkerCluster.css
new file mode 100644
index 00000000..c60d71b7
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet.markercluster/MarkerCluster.css
@@ -0,0 +1,14 @@
+.leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow {
+ -webkit-transition: -webkit-transform 0.3s ease-out, opacity 0.3s ease-in;
+ -moz-transition: -moz-transform 0.3s ease-out, opacity 0.3s ease-in;
+ -o-transition: -o-transform 0.3s ease-out, opacity 0.3s ease-in;
+ transition: transform 0.3s ease-out, opacity 0.3s ease-in;
+}
+
+.leaflet-cluster-spider-leg {
+ /* stroke-dashoffset (duration and function) should match with leaflet-marker-icon transform in order to track it exactly */
+ -webkit-transition: -webkit-stroke-dashoffset 0.3s ease-out, -webkit-stroke-opacity 0.3s ease-in;
+ -moz-transition: -moz-stroke-dashoffset 0.3s ease-out, -moz-stroke-opacity 0.3s ease-in;
+ -o-transition: -o-stroke-dashoffset 0.3s ease-out, -o-stroke-opacity 0.3s ease-in;
+ transition: stroke-dashoffset 0.3s ease-out, stroke-opacity 0.3s ease-in;
+}
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet.markercluster/leaflet.markercluster.js b/www/wiki/extensions/Maps/resources/leaflet/leaflet.markercluster/leaflet.markercluster.js
new file mode 100644
index 00000000..ed22dc07
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet.markercluster/leaflet.markercluster.js
@@ -0,0 +1,3 @@
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e.Leaflet=e.Leaflet||{},e.Leaflet.markercluster=e.Leaflet.markercluster||{}))}(this,function(e){"use strict";var t=L.MarkerClusterGroup=L.FeatureGroup.extend({options:{maxClusterRadius:80,iconCreateFunction:null,clusterPane:L.Marker.prototype.options.pane,spiderfyOnMaxZoom:!0,showCoverageOnHover:!0,zoomToBoundsOnClick:!0,singleMarkerMode:!1,disableClusteringAtZoom:null,removeOutsideVisibleBounds:!0,animate:!0,animateAddingMarkers:!1,spiderfyDistanceMultiplier:1,spiderLegPolylineOptions:{weight:1.5,color:"#222",opacity:.5},chunkedLoading:!1,chunkInterval:200,chunkDelay:50,chunkProgress:null,polygonOptions:{}},initialize:function(e){L.Util.setOptions(this,e),this.options.iconCreateFunction||(this.options.iconCreateFunction=this._defaultIconCreateFunction),this._featureGroup=L.featureGroup(),this._featureGroup.addEventParent(this),this._nonPointGroup=L.featureGroup(),this._nonPointGroup.addEventParent(this),this._inZoomAnimation=0,this._needsClustering=[],this._needsRemoving=[],this._currentShownBounds=null,this._queue=[],this._childMarkerEventHandlers={dragstart:this._childMarkerDragStart,move:this._childMarkerMoved,dragend:this._childMarkerDragEnd};var t=L.DomUtil.TRANSITION&&this.options.animate;L.extend(this,t?this._withAnimation:this._noAnimation),this._markerCluster=t?L.MarkerCluster:L.MarkerClusterNonAnimated},addLayer:function(e){if(e instanceof L.LayerGroup)return this.addLayers([e]);if(!e.getLatLng)return this._nonPointGroup.addLayer(e),this.fire("layeradd",{layer:e}),this;if(!this._map)return this._needsClustering.push(e),this.fire("layeradd",{layer:e}),this;if(this.hasLayer(e))return this;this._unspiderfy&&this._unspiderfy(),this._addLayer(e,this._maxZoom),this.fire("layeradd",{layer:e}),this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons();var t=e,i=this._zoom;if(e.__parent)for(;t.__parent._zoom>=i;)t=t.__parent;return this._currentShownBounds.contains(t.getLatLng())&&(this.options.animateAddingMarkers?this._animationAddLayer(e,t):this._animationAddLayerNonAnimated(e,t)),this},removeLayer:function(e){return e instanceof L.LayerGroup?this.removeLayers([e]):e.getLatLng?this._map?e.__parent?(this._unspiderfy&&(this._unspiderfy(),this._unspiderfyLayer(e)),this._removeLayer(e,!0),this.fire("layerremove",{layer:e}),this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons(),e.off(this._childMarkerEventHandlers,this),this._featureGroup.hasLayer(e)&&(this._featureGroup.removeLayer(e),e.clusterShow&&e.clusterShow()),this):this:(!this._arraySplice(this._needsClustering,e)&&this.hasLayer(e)&&this._needsRemoving.push({layer:e,latlng:e._latlng}),this.fire("layerremove",{layer:e}),this):(this._nonPointGroup.removeLayer(e),this.fire("layerremove",{layer:e}),this)},addLayers:function(e,t){if(!L.Util.isArray(e))return this.addLayer(e);var i,n=this._featureGroup,r=this._nonPointGroup,s=this.options.chunkedLoading,o=this.options.chunkInterval,a=this.options.chunkProgress,h=e.length,l=0,u=!0;if(this._map){var _=(new Date).getTime(),d=L.bind(function(){for(var c=(new Date).getTime();h>l;l++){if(s&&0===l%200){var p=(new Date).getTime()-c;if(p>o)break}if(i=e[l],i instanceof L.LayerGroup)u&&(e=e.slice(),u=!1),this._extractNonGroupLayers(i,e),h=e.length;else if(i.getLatLng){if(!this.hasLayer(i)&&(this._addLayer(i,this._maxZoom),t||this.fire("layeradd",{layer:i}),i.__parent&&2===i.__parent.getChildCount())){var f=i.__parent.getAllChildMarkers(),m=f[0]===i?f[1]:f[0];n.removeLayer(m)}}else r.addLayer(i),t||this.fire("layeradd",{layer:i})}a&&a(l,h,(new Date).getTime()-_),l===h?(this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons(),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,this._currentShownBounds)):setTimeout(d,this.options.chunkDelay)},this);d()}else for(var c=this._needsClustering;h>l;l++)i=e[l],i instanceof L.LayerGroup?(u&&(e=e.slice(),u=!1),this._extractNonGroupLayers(i,e),h=e.length):i.getLatLng?this.hasLayer(i)||c.push(i):r.addLayer(i);return this},removeLayers:function(e){var t,i,n=e.length,r=this._featureGroup,s=this._nonPointGroup,o=!0;if(!this._map){for(t=0;n>t;t++)i=e[t],i instanceof L.LayerGroup?(o&&(e=e.slice(),o=!1),this._extractNonGroupLayers(i,e),n=e.length):(this._arraySplice(this._needsClustering,i),s.removeLayer(i),this.hasLayer(i)&&this._needsRemoving.push({layer:i,latlng:i._latlng}),this.fire("layerremove",{layer:i}));return this}if(this._unspiderfy){this._unspiderfy();var a=e.slice(),h=n;for(t=0;h>t;t++)i=a[t],i instanceof L.LayerGroup?(this._extractNonGroupLayers(i,a),h=a.length):this._unspiderfyLayer(i)}for(t=0;n>t;t++)i=e[t],i instanceof L.LayerGroup?(o&&(e=e.slice(),o=!1),this._extractNonGroupLayers(i,e),n=e.length):i.__parent?(this._removeLayer(i,!0,!0),this.fire("layerremove",{layer:i}),r.hasLayer(i)&&(r.removeLayer(i),i.clusterShow&&i.clusterShow())):(s.removeLayer(i),this.fire("layerremove",{layer:i}));return this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons(),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,this._currentShownBounds),this},clearLayers:function(){return this._map||(this._needsClustering=[],delete this._gridClusters,delete this._gridUnclustered),this._noanimationUnspiderfy&&this._noanimationUnspiderfy(),this._featureGroup.clearLayers(),this._nonPointGroup.clearLayers(),this.eachLayer(function(e){e.off(this._childMarkerEventHandlers,this),delete e.__parent},this),this._map&&this._generateInitialClusters(),this},getBounds:function(){var e=new L.LatLngBounds;this._topClusterLevel&&e.extend(this._topClusterLevel._bounds);for(var t=this._needsClustering.length-1;t>=0;t--)e.extend(this._needsClustering[t].getLatLng());return e.extend(this._nonPointGroup.getBounds()),e},eachLayer:function(e,t){var i,n,r,s=this._needsClustering.slice(),o=this._needsRemoving;for(this._topClusterLevel&&this._topClusterLevel.getAllChildMarkers(s),n=s.length-1;n>=0;n--){for(i=!0,r=o.length-1;r>=0;r--)if(o[r].layer===s[n]){i=!1;break}i&&e.call(t,s[n])}this._nonPointGroup.eachLayer(e,t)},getLayers:function(){var e=[];return this.eachLayer(function(t){e.push(t)}),e},getLayer:function(e){var t=null;return e=parseInt(e,10),this.eachLayer(function(i){L.stamp(i)===e&&(t=i)}),t},hasLayer:function(e){if(!e)return!1;var t,i=this._needsClustering;for(t=i.length-1;t>=0;t--)if(i[t]===e)return!0;for(i=this._needsRemoving,t=i.length-1;t>=0;t--)if(i[t].layer===e)return!1;return!(!e.__parent||e.__parent._group!==this)||this._nonPointGroup.hasLayer(e)},zoomToShowLayer:function(e,t){"function"!=typeof t&&(t=function(){});var i=function(){!e._icon&&!e.__parent._icon||this._inZoomAnimation||(this._map.off("moveend",i,this),this.off("animationend",i,this),e._icon?t():e.__parent._icon&&(this.once("spiderfied",t,this),e.__parent.spiderfy()))};e._icon&&this._map.getBounds().contains(e.getLatLng())?t():e.__parent._zoom<Math.round(this._map._zoom)?(this._map.on("moveend",i,this),this._map.panTo(e.getLatLng())):(this._map.on("moveend",i,this),this.on("animationend",i,this),e.__parent.zoomToBounds())},onAdd:function(e){this._map=e;var t,i,n;if(!isFinite(this._map.getMaxZoom()))throw"Map has no maxZoom specified";for(this._featureGroup.addTo(e),this._nonPointGroup.addTo(e),this._gridClusters||this._generateInitialClusters(),this._maxLat=e.options.crs.projection.MAX_LATITUDE,t=0,i=this._needsRemoving.length;i>t;t++)n=this._needsRemoving[t],n.newlatlng=n.layer._latlng,n.layer._latlng=n.latlng;for(t=0,i=this._needsRemoving.length;i>t;t++)n=this._needsRemoving[t],this._removeLayer(n.layer,!0),n.layer._latlng=n.newlatlng;this._needsRemoving=[],this._zoom=Math.round(this._map._zoom),this._currentShownBounds=this._getExpandedVisibleBounds(),this._map.on("zoomend",this._zoomEnd,this),this._map.on("moveend",this._moveEnd,this),this._spiderfierOnAdd&&this._spiderfierOnAdd(),this._bindEvents(),i=this._needsClustering,this._needsClustering=[],this.addLayers(i,!0)},onRemove:function(e){e.off("zoomend",this._zoomEnd,this),e.off("moveend",this._moveEnd,this),this._unbindEvents(),this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim",""),this._spiderfierOnRemove&&this._spiderfierOnRemove(),delete this._maxLat,this._hideCoverage(),this._featureGroup.remove(),this._nonPointGroup.remove(),this._featureGroup.clearLayers(),this._map=null},getVisibleParent:function(e){for(var t=e;t&&!t._icon;)t=t.__parent;return t||null},_arraySplice:function(e,t){for(var i=e.length-1;i>=0;i--)if(e[i]===t)return e.splice(i,1),!0},_removeFromGridUnclustered:function(e,t){for(var i=this._map,n=this._gridUnclustered,r=Math.floor(this._map.getMinZoom());t>=r&&n[t].removeObject(e,i.project(e.getLatLng(),t));t--);},_childMarkerDragStart:function(e){e.target.__dragStart=e.target._latlng},_childMarkerMoved:function(e){if(!this._ignoreMove&&!e.target.__dragStart){var t=e.target._popup&&e.target._popup.isOpen();this._moveChild(e.target,e.oldLatLng,e.latlng),t&&e.target.openPopup()}},_moveChild:function(e,t,i){e._latlng=t,this.removeLayer(e),e._latlng=i,this.addLayer(e)},_childMarkerDragEnd:function(e){e.target.__dragStart&&this._moveChild(e.target,e.target.__dragStart,e.target._latlng),delete e.target.__dragStart},_removeLayer:function(e,t,i){var n=this._gridClusters,r=this._gridUnclustered,s=this._featureGroup,o=this._map,a=Math.floor(this._map.getMinZoom());t&&this._removeFromGridUnclustered(e,this._maxZoom);var h,l=e.__parent,u=l._markers;for(this._arraySplice(u,e);l&&(l._childCount--,l._boundsNeedUpdate=!0,!(l._zoom<a));)t&&l._childCount<=1?(h=l._markers[0]===e?l._markers[1]:l._markers[0],n[l._zoom].removeObject(l,o.project(l._cLatLng,l._zoom)),r[l._zoom].addObject(h,o.project(h.getLatLng(),l._zoom)),this._arraySplice(l.__parent._childClusters,l),l.__parent._markers.push(h),h.__parent=l.__parent,l._icon&&(s.removeLayer(l),i||s.addLayer(h))):l._iconNeedsUpdate=!0,l=l.__parent;delete e.__parent},_isOrIsParent:function(e,t){for(;t;){if(e===t)return!0;t=t.parentNode}return!1},fire:function(e,t,i){if(t&&t.layer instanceof L.MarkerCluster){if(t.originalEvent&&this._isOrIsParent(t.layer._icon,t.originalEvent.relatedTarget))return;e="cluster"+e}L.FeatureGroup.prototype.fire.call(this,e,t,i)},listens:function(e,t){return L.FeatureGroup.prototype.listens.call(this,e,t)||L.FeatureGroup.prototype.listens.call(this,"cluster"+e,t)},_defaultIconCreateFunction:function(e){var t=e.getChildCount(),i=" marker-cluster-";return i+=10>t?"small":100>t?"medium":"large",new L.DivIcon({html:"<div><span>"+t+"</span></div>",className:"marker-cluster"+i,iconSize:new L.Point(40,40)})},_bindEvents:function(){var e=this._map,t=this.options.spiderfyOnMaxZoom,i=this.options.showCoverageOnHover,n=this.options.zoomToBoundsOnClick;(t||n)&&this.on("clusterclick",this._zoomOrSpiderfy,this),i&&(this.on("clustermouseover",this._showCoverage,this),this.on("clustermouseout",this._hideCoverage,this),e.on("zoomend",this._hideCoverage,this))},_zoomOrSpiderfy:function(e){for(var t=e.layer,i=t;1===i._childClusters.length;)i=i._childClusters[0];i._zoom===this._maxZoom&&i._childCount===t._childCount&&this.options.spiderfyOnMaxZoom?t.spiderfy():this.options.zoomToBoundsOnClick&&t.zoomToBounds(),e.originalEvent&&13===e.originalEvent.keyCode&&this._map._container.focus()},_showCoverage:function(e){var t=this._map;this._inZoomAnimation||(this._shownPolygon&&t.removeLayer(this._shownPolygon),e.layer.getChildCount()>2&&e.layer!==this._spiderfied&&(this._shownPolygon=new L.Polygon(e.layer.getConvexHull(),this.options.polygonOptions),t.addLayer(this._shownPolygon)))},_hideCoverage:function(){this._shownPolygon&&(this._map.removeLayer(this._shownPolygon),this._shownPolygon=null)},_unbindEvents:function(){var e=this.options.spiderfyOnMaxZoom,t=this.options.showCoverageOnHover,i=this.options.zoomToBoundsOnClick,n=this._map;(e||i)&&this.off("clusterclick",this._zoomOrSpiderfy,this),t&&(this.off("clustermouseover",this._showCoverage,this),this.off("clustermouseout",this._hideCoverage,this),n.off("zoomend",this._hideCoverage,this))},_zoomEnd:function(){this._map&&(this._mergeSplitClusters(),this._zoom=Math.round(this._map._zoom),this._currentShownBounds=this._getExpandedVisibleBounds())},_moveEnd:function(){if(!this._inZoomAnimation){var e=this._getExpandedVisibleBounds();this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),this._zoom,e),this._topClusterLevel._recursivelyAddChildrenToMap(null,Math.round(this._map._zoom),e),this._currentShownBounds=e}},_generateInitialClusters:function(){var e=Math.ceil(this._map.getMaxZoom()),t=Math.floor(this._map.getMinZoom()),i=this.options.maxClusterRadius,n=i;"function"!=typeof i&&(n=function(){return i}),null!==this.options.disableClusteringAtZoom&&(e=this.options.disableClusteringAtZoom-1),this._maxZoom=e,this._gridClusters={},this._gridUnclustered={};for(var r=e;r>=t;r--)this._gridClusters[r]=new L.DistanceGrid(n(r)),this._gridUnclustered[r]=new L.DistanceGrid(n(r));this._topClusterLevel=new this._markerCluster(this,t-1)},_addLayer:function(e,t){var i,n,r=this._gridClusters,s=this._gridUnclustered,o=Math.floor(this._map.getMinZoom());for(this.options.singleMarkerMode&&this._overrideMarkerIcon(e),e.on(this._childMarkerEventHandlers,this);t>=o;t--){i=this._map.project(e.getLatLng(),t);var a=r[t].getNearObject(i);if(a)return a._addChild(e),e.__parent=a,void 0;if(a=s[t].getNearObject(i)){var h=a.__parent;h&&this._removeLayer(a,!1);var l=new this._markerCluster(this,t,a,e);r[t].addObject(l,this._map.project(l._cLatLng,t)),a.__parent=l,e.__parent=l;var u=l;for(n=t-1;n>h._zoom;n--)u=new this._markerCluster(this,n,u),r[n].addObject(u,this._map.project(a.getLatLng(),n));return h._addChild(u),this._removeFromGridUnclustered(a,t),void 0}s[t].addObject(e,i)}this._topClusterLevel._addChild(e),e.__parent=this._topClusterLevel},_refreshClustersIcons:function(){this._featureGroup.eachLayer(function(e){e instanceof L.MarkerCluster&&e._iconNeedsUpdate&&e._updateIcon()})},_enqueue:function(e){this._queue.push(e),this._queueTimeout||(this._queueTimeout=setTimeout(L.bind(this._processQueue,this),300))},_processQueue:function(){for(var e=0;e<this._queue.length;e++)this._queue[e].call(this);this._queue.length=0,clearTimeout(this._queueTimeout),this._queueTimeout=null},_mergeSplitClusters:function(){var e=Math.round(this._map._zoom);this._processQueue(),this._zoom<e&&this._currentShownBounds.intersects(this._getExpandedVisibleBounds())?(this._animationStart(),this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),this._zoom,this._getExpandedVisibleBounds()),this._animationZoomIn(this._zoom,e)):this._zoom>e?(this._animationStart(),this._animationZoomOut(this._zoom,e)):this._moveEnd()},_getExpandedVisibleBounds:function(){return this.options.removeOutsideVisibleBounds?L.Browser.mobile?this._checkBoundsMaxLat(this._map.getBounds()):this._checkBoundsMaxLat(this._map.getBounds().pad(1)):this._mapBoundsInfinite},_checkBoundsMaxLat:function(e){var t=this._maxLat;return void 0!==t&&(e.getNorth()>=t&&(e._northEast.lat=1/0),e.getSouth()<=-t&&(e._southWest.lat=-1/0)),e},_animationAddLayerNonAnimated:function(e,t){if(t===e)this._featureGroup.addLayer(e);else if(2===t._childCount){t._addToMap();var i=t.getAllChildMarkers();this._featureGroup.removeLayer(i[0]),this._featureGroup.removeLayer(i[1])}else t._updateIcon()},_extractNonGroupLayers:function(e,t){var i,n=e.getLayers(),r=0;for(t=t||[];r<n.length;r++)i=n[r],i instanceof L.LayerGroup?this._extractNonGroupLayers(i,t):t.push(i);return t},_overrideMarkerIcon:function(e){var t=e.options.icon=this.options.iconCreateFunction({getChildCount:function(){return 1},getAllChildMarkers:function(){return[e]}});return t}});L.MarkerClusterGroup.include({_mapBoundsInfinite:new L.LatLngBounds(new L.LatLng(-1/0,-1/0),new L.LatLng(1/0,1/0))}),L.MarkerClusterGroup.include({_noAnimation:{_animationStart:function(){},_animationZoomIn:function(e,t){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),e),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds()),this.fire("animationend")},_animationZoomOut:function(e,t){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),e),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds()),this.fire("animationend")},_animationAddLayer:function(e,t){this._animationAddLayerNonAnimated(e,t)}},_withAnimation:{_animationStart:function(){this._map._mapPane.className+=" leaflet-cluster-anim",this._inZoomAnimation++},_animationZoomIn:function(e,t){var i,n=this._getExpandedVisibleBounds(),r=this._featureGroup,s=Math.floor(this._map.getMinZoom());this._ignoreMove=!0,this._topClusterLevel._recursively(n,e,s,function(s){var o,a=s._latlng,h=s._markers;for(n.contains(a)||(a=null),s._isSingleParent()&&e+1===t?(r.removeLayer(s),s._recursivelyAddChildrenToMap(null,t,n)):(s.clusterHide(),s._recursivelyAddChildrenToMap(a,t,n)),i=h.length-1;i>=0;i--)o=h[i],n.contains(o._latlng)||r.removeLayer(o)}),this._forceLayout(),this._topClusterLevel._recursivelyBecomeVisible(n,t),r.eachLayer(function(e){e instanceof L.MarkerCluster||!e._icon||e.clusterShow()}),this._topClusterLevel._recursively(n,e,t,function(e){e._recursivelyRestoreChildPositions(t)}),this._ignoreMove=!1,this._enqueue(function(){this._topClusterLevel._recursively(n,e,s,function(e){r.removeLayer(e),e.clusterShow()}),this._animationEnd()})},_animationZoomOut:function(e,t){this._animationZoomOutSingle(this._topClusterLevel,e-1,t),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds()),this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),e,this._getExpandedVisibleBounds())},_animationAddLayer:function(e,t){var i=this,n=this._featureGroup;n.addLayer(e),t!==e&&(t._childCount>2?(t._updateIcon(),this._forceLayout(),this._animationStart(),e._setPos(this._map.latLngToLayerPoint(t.getLatLng())),e.clusterHide(),this._enqueue(function(){n.removeLayer(e),e.clusterShow(),i._animationEnd()})):(this._forceLayout(),i._animationStart(),i._animationZoomOutSingle(t,this._map.getMaxZoom(),this._zoom)))}},_animationZoomOutSingle:function(e,t,i){var n=this._getExpandedVisibleBounds(),r=Math.floor(this._map.getMinZoom());e._recursivelyAnimateChildrenInAndAddSelfToMap(n,r,t+1,i);var s=this;this._forceLayout(),e._recursivelyBecomeVisible(n,i),this._enqueue(function(){if(1===e._childCount){var o=e._markers[0];this._ignoreMove=!0,o.setLatLng(o.getLatLng()),this._ignoreMove=!1,o.clusterShow&&o.clusterShow()}else e._recursively(n,i,r,function(e){e._recursivelyRemoveChildrenFromMap(n,r,t+1)});s._animationEnd()})},_animationEnd:function(){this._map&&(this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim","")),this._inZoomAnimation--,this.fire("animationend")},_forceLayout:function(){L.Util.falseFn(document.body.offsetWidth)}}),L.markerClusterGroup=function(e){return new L.MarkerClusterGroup(e)};var i=L.MarkerCluster=L.Marker.extend({options:L.Icon.prototype.options,initialize:function(e,t,i,n){L.Marker.prototype.initialize.call(this,i?i._cLatLng||i.getLatLng():new L.LatLng(0,0),{icon:this,pane:e.options.clusterPane}),this._group=e,this._zoom=t,this._markers=[],this._childClusters=[],this._childCount=0,this._iconNeedsUpdate=!0,this._boundsNeedUpdate=!0,this._bounds=new L.LatLngBounds,i&&this._addChild(i),n&&this._addChild(n)},getAllChildMarkers:function(e){e=e||[];for(var t=this._childClusters.length-1;t>=0;t--)this._childClusters[t].getAllChildMarkers(e);for(var i=this._markers.length-1;i>=0;i--)e.push(this._markers[i]);return e},getChildCount:function(){return this._childCount},zoomToBounds:function(e){for(var t,i=this._childClusters.slice(),n=this._group._map,r=n.getBoundsZoom(this._bounds),s=this._zoom+1,o=n.getZoom();i.length>0&&r>s;){s++;var a=[];for(t=0;t<i.length;t++)a=a.concat(i[t]._childClusters);i=a}r>s?this._group._map.setView(this._latlng,s):o>=r?this._group._map.setView(this._latlng,o+1):this._group._map.fitBounds(this._bounds,e)},getBounds:function(){var e=new L.LatLngBounds;return e.extend(this._bounds),e},_updateIcon:function(){this._iconNeedsUpdate=!0,this._icon&&this.setIcon(this)},createIcon:function(){return this._iconNeedsUpdate&&(this._iconObj=this._group.options.iconCreateFunction(this),this._iconNeedsUpdate=!1),this._iconObj.createIcon()},createShadow:function(){return this._iconObj.createShadow()},_addChild:function(e,t){this._iconNeedsUpdate=!0,this._boundsNeedUpdate=!0,this._setClusterCenter(e),e instanceof L.MarkerCluster?(t||(this._childClusters.push(e),e.__parent=this),this._childCount+=e._childCount):(t||this._markers.push(e),this._childCount++),this.__parent&&this.__parent._addChild(e,!0)},_setClusterCenter:function(e){this._cLatLng||(this._cLatLng=e._cLatLng||e._latlng)},_resetBounds:function(){var e=this._bounds;e._southWest&&(e._southWest.lat=1/0,e._southWest.lng=1/0),e._northEast&&(e._northEast.lat=-1/0,e._northEast.lng=-1/0)},_recalculateBounds:function(){var e,t,i,n,r=this._markers,s=this._childClusters,o=0,a=0,h=this._childCount;if(0!==h){for(this._resetBounds(),e=0;e<r.length;e++)i=r[e]._latlng,this._bounds.extend(i),o+=i.lat,a+=i.lng;for(e=0;e<s.length;e++)t=s[e],t._boundsNeedUpdate&&t._recalculateBounds(),this._bounds.extend(t._bounds),i=t._wLatLng,n=t._childCount,o+=i.lat*n,a+=i.lng*n;this._latlng=this._wLatLng=new L.LatLng(o/h,a/h),this._boundsNeedUpdate=!1}},_addToMap:function(e){e&&(this._backupLatlng=this._latlng,this.setLatLng(e)),this._group._featureGroup.addLayer(this)},_recursivelyAnimateChildrenIn:function(e,t,i){this._recursively(e,this._group._map.getMinZoom(),i-1,function(e){var i,n,r=e._markers;for(i=r.length-1;i>=0;i--)n=r[i],n._icon&&(n._setPos(t),n.clusterHide())},function(e){var i,n,r=e._childClusters;for(i=r.length-1;i>=0;i--)n=r[i],n._icon&&(n._setPos(t),n.clusterHide())})},_recursivelyAnimateChildrenInAndAddSelfToMap:function(e,t,i,n){this._recursively(e,n,t,function(r){r._recursivelyAnimateChildrenIn(e,r._group._map.latLngToLayerPoint(r.getLatLng()).round(),i),r._isSingleParent()&&i-1===n?(r.clusterShow(),r._recursivelyRemoveChildrenFromMap(e,t,i)):r.clusterHide(),r._addToMap()})},_recursivelyBecomeVisible:function(e,t){this._recursively(e,this._group._map.getMinZoom(),t,null,function(e){e.clusterShow()})},_recursivelyAddChildrenToMap:function(e,t,i){this._recursively(i,this._group._map.getMinZoom()-1,t,function(n){if(t!==n._zoom)for(var r=n._markers.length-1;r>=0;r--){var s=n._markers[r];i.contains(s._latlng)&&(e&&(s._backupLatlng=s.getLatLng(),s.setLatLng(e),s.clusterHide&&s.clusterHide()),n._group._featureGroup.addLayer(s))}},function(t){t._addToMap(e)})},_recursivelyRestoreChildPositions:function(e){for(var t=this._markers.length-1;t>=0;t--){var i=this._markers[t];i._backupLatlng&&(i.setLatLng(i._backupLatlng),delete i._backupLatlng)}if(e-1===this._zoom)for(var n=this._childClusters.length-1;n>=0;n--)this._childClusters[n]._restorePosition();else for(var r=this._childClusters.length-1;r>=0;r--)this._childClusters[r]._recursivelyRestoreChildPositions(e)},_restorePosition:function(){this._backupLatlng&&(this.setLatLng(this._backupLatlng),delete this._backupLatlng)},_recursivelyRemoveChildrenFromMap:function(e,t,i,n){var r,s;this._recursively(e,t-1,i-1,function(e){for(s=e._markers.length-1;s>=0;s--)r=e._markers[s],n&&n.contains(r._latlng)||(e._group._featureGroup.removeLayer(r),r.clusterShow&&r.clusterShow())},function(e){for(s=e._childClusters.length-1;s>=0;s--)r=e._childClusters[s],n&&n.contains(r._latlng)||(e._group._featureGroup.removeLayer(r),r.clusterShow&&r.clusterShow())})},_recursively:function(e,t,i,n,r){var s,o,a=this._childClusters,h=this._zoom;if(h>=t&&(n&&n(this),r&&h===i&&r(this)),t>h||i>h)for(s=a.length-1;s>=0;s--)o=a[s],e.intersects(o._bounds)&&o._recursively(e,t,i,n,r)},_isSingleParent:function(){return this._childClusters.length>0&&this._childClusters[0]._childCount===this._childCount}});L.Marker.include({clusterHide:function(){return this.options.opacityWhenUnclustered=this.options.opacity||1,this.setOpacity(0)},clusterShow:function(){var e=this.setOpacity(this.options.opacity||this.options.opacityWhenUnclustered);return delete this.options.opacityWhenUnclustered,e}}),L.DistanceGrid=function(e){this._cellSize=e,this._sqCellSize=e*e,this._grid={},this._objectPoint={}},L.DistanceGrid.prototype={addObject:function(e,t){var i=this._getCoord(t.x),n=this._getCoord(t.y),r=this._grid,s=r[n]=r[n]||{},o=s[i]=s[i]||[],a=L.Util.stamp(e);this._objectPoint[a]=t,o.push(e)},updateObject:function(e,t){this.removeObject(e),this.addObject(e,t)},removeObject:function(e,t){var i,n,r=this._getCoord(t.x),s=this._getCoord(t.y),o=this._grid,a=o[s]=o[s]||{},h=a[r]=a[r]||[];for(delete this._objectPoint[L.Util.stamp(e)],i=0,n=h.length;n>i;i++)if(h[i]===e)return h.splice(i,1),1===n&&delete a[r],!0},eachObject:function(e,t){var i,n,r,s,o,a,h,l=this._grid;for(i in l){o=l[i];for(n in o)for(a=o[n],r=0,s=a.length;s>r;r++)h=e.call(t,a[r]),h&&(r--,s--)}},getNearObject:function(e){var t,i,n,r,s,o,a,h,l=this._getCoord(e.x),u=this._getCoord(e.y),_=this._objectPoint,d=this._sqCellSize,c=null;for(t=u-1;u+1>=t;t++)if(r=this._grid[t])for(i=l-1;l+1>=i;i++)if(s=r[i])for(n=0,o=s.length;o>n;n++)a=s[n],h=this._sqDist(_[L.Util.stamp(a)],e),(d>h||d>=h&&null===c)&&(d=h,c=a);return c},_getCoord:function(e){var t=Math.floor(e/this._cellSize);return isFinite(t)?t:e},_sqDist:function(e,t){var i=t.x-e.x,n=t.y-e.y;return i*i+n*n}},function(){L.QuickHull={getDistant:function(e,t){var i=t[1].lat-t[0].lat,n=t[0].lng-t[1].lng;return n*(e.lat-t[0].lat)+i*(e.lng-t[0].lng)},findMostDistantPointFromBaseLine:function(e,t){var i,n,r,s=0,o=null,a=[];for(i=t.length-1;i>=0;i--)n=t[i],r=this.getDistant(n,e),r>0&&(a.push(n),r>s&&(s=r,o=n));return{maxPoint:o,newPoints:a}},buildConvexHull:function(e,t){var i=[],n=this.findMostDistantPointFromBaseLine(e,t);return n.maxPoint?(i=i.concat(this.buildConvexHull([e[0],n.maxPoint],n.newPoints)),i=i.concat(this.buildConvexHull([n.maxPoint,e[1]],n.newPoints))):[e[0]]},getConvexHull:function(e){var t,i=!1,n=!1,r=!1,s=!1,o=null,a=null,h=null,l=null,u=null,_=null;for(t=e.length-1;t>=0;t--){var d=e[t];(i===!1||d.lat>i)&&(o=d,i=d.lat),(n===!1||d.lat<n)&&(a=d,n=d.lat),(r===!1||d.lng>r)&&(h=d,r=d.lng),(s===!1||d.lng<s)&&(l=d,s=d.lng)}n!==i?(_=a,u=o):(_=l,u=h);var c=[].concat(this.buildConvexHull([_,u],e),this.buildConvexHull([u,_],e));return c}}}(),L.MarkerCluster.include({getConvexHull:function(){var e,t,i=this.getAllChildMarkers(),n=[];for(t=i.length-1;t>=0;t--)e=i[t].getLatLng(),n.push(e);return L.QuickHull.getConvexHull(n)}}),L.MarkerCluster.include({_2PI:2*Math.PI,_circleFootSeparation:25,_circleStartAngle:0,_spiralFootSeparation:28,_spiralLengthStart:11,_spiralLengthFactor:5,_circleSpiralSwitchover:9,spiderfy:function(){if(this._group._spiderfied!==this&&!this._group._inZoomAnimation){var e,t=this.getAllChildMarkers(),i=this._group,n=i._map,r=n.latLngToLayerPoint(this._latlng);this._group._unspiderfy(),this._group._spiderfied=this,t.length>=this._circleSpiralSwitchover?e=this._generatePointsSpiral(t.length,r):(r.y+=10,e=this._generatePointsCircle(t.length,r)),this._animationSpiderfy(t,e)}},unspiderfy:function(e){this._group._inZoomAnimation||(this._animationUnspiderfy(e),this._group._spiderfied=null)},_generatePointsCircle:function(e,t){var i,n,r=this._group.options.spiderfyDistanceMultiplier*this._circleFootSeparation*(2+e),s=r/this._2PI,o=this._2PI/e,a=[];for(s=Math.max(s,35),a.length=e,i=0;e>i;i++)n=this._circleStartAngle+i*o,a[i]=new L.Point(t.x+s*Math.cos(n),t.y+s*Math.sin(n))._round();return a},_generatePointsSpiral:function(e,t){var i,n=this._group.options.spiderfyDistanceMultiplier,r=n*this._spiralLengthStart,s=n*this._spiralFootSeparation,o=n*this._spiralLengthFactor*this._2PI,a=0,h=[];for(h.length=e,i=e;i>=0;i--)e>i&&(h[i]=new L.Point(t.x+r*Math.cos(a),t.y+r*Math.sin(a))._round()),a+=s/r+5e-4*i,r+=o/a;return h},_noanimationUnspiderfy:function(){var e,t,i=this._group,n=i._map,r=i._featureGroup,s=this.getAllChildMarkers();for(i._ignoreMove=!0,this.setOpacity(1),t=s.length-1;t>=0;t--)e=s[t],r.removeLayer(e),e._preSpiderfyLatlng&&(e.setLatLng(e._preSpiderfyLatlng),delete e._preSpiderfyLatlng),e.setZIndexOffset&&e.setZIndexOffset(0),e._spiderLeg&&(n.removeLayer(e._spiderLeg),delete e._spiderLeg);i.fire("unspiderfied",{cluster:this,markers:s}),i._ignoreMove=!1,i._spiderfied=null}}),L.MarkerClusterNonAnimated=L.MarkerCluster.extend({_animationSpiderfy:function(e,t){var i,n,r,s,o=this._group,a=o._map,h=o._featureGroup,l=this._group.options.spiderLegPolylineOptions;for(o._ignoreMove=!0,i=0;i<e.length;i++)s=a.layerPointToLatLng(t[i]),n=e[i],r=new L.Polyline([this._latlng,s],l),a.addLayer(r),n._spiderLeg=r,n._preSpiderfyLatlng=n._latlng,n.setLatLng(s),n.setZIndexOffset&&n.setZIndexOffset(1e6),h.addLayer(n);this.setOpacity(.3),o._ignoreMove=!1,o.fire("spiderfied",{cluster:this,markers:e})},_animationUnspiderfy:function(){this._noanimationUnspiderfy()}}),L.MarkerCluster.include({_animationSpiderfy:function(e,t){var i,n,r,s,o,a,h=this,l=this._group,u=l._map,_=l._featureGroup,d=this._latlng,c=u.latLngToLayerPoint(d),p=L.Path.SVG,f=L.extend({},this._group.options.spiderLegPolylineOptions),m=f.opacity;for(void 0===m&&(m=L.MarkerClusterGroup.prototype.options.spiderLegPolylineOptions.opacity),p?(f.opacity=0,f.className=(f.className||"")+" leaflet-cluster-spider-leg"):f.opacity=m,l._ignoreMove=!0,i=0;i<e.length;i++)n=e[i],a=u.layerPointToLatLng(t[i]),r=new L.Polyline([d,a],f),u.addLayer(r),n._spiderLeg=r,p&&(s=r._path,o=s.getTotalLength()+.1,s.style.strokeDasharray=o,s.style.strokeDashoffset=o),n.setZIndexOffset&&n.setZIndexOffset(1e6),n.clusterHide&&n.clusterHide(),_.addLayer(n),n._setPos&&n._setPos(c);for(l._forceLayout(),l._animationStart(),i=e.length-1;i>=0;i--)a=u.layerPointToLatLng(t[i]),n=e[i],n._preSpiderfyLatlng=n._latlng,n.setLatLng(a),n.clusterShow&&n.clusterShow(),p&&(r=n._spiderLeg,s=r._path,s.style.strokeDashoffset=0,r.setStyle({opacity:m}));this.setOpacity(.3),l._ignoreMove=!1,setTimeout(function(){l._animationEnd(),l.fire("spiderfied",{cluster:h,markers:e})},200)},_animationUnspiderfy:function(e){var t,i,n,r,s,o,a=this,h=this._group,l=h._map,u=h._featureGroup,_=e?l._latLngToNewLayerPoint(this._latlng,e.zoom,e.center):l.latLngToLayerPoint(this._latlng),d=this.getAllChildMarkers(),c=L.Path.SVG;for(h._ignoreMove=!0,h._animationStart(),this.setOpacity(1),i=d.length-1;i>=0;i--)t=d[i],t._preSpiderfyLatlng&&(t.closePopup(),t.setLatLng(t._preSpiderfyLatlng),delete t._preSpiderfyLatlng,o=!0,t._setPos&&(t._setPos(_),o=!1),t.clusterHide&&(t.clusterHide(),o=!1),o&&u.removeLayer(t),c&&(n=t._spiderLeg,r=n._path,s=r.getTotalLength()+.1,r.style.strokeDashoffset=s,n.setStyle({opacity:0})));h._ignoreMove=!1,setTimeout(function(){var e=0;for(i=d.length-1;i>=0;i--)t=d[i],t._spiderLeg&&e++;for(i=d.length-1;i>=0;i--)t=d[i],t._spiderLeg&&(t.clusterShow&&t.clusterShow(),t.setZIndexOffset&&t.setZIndexOffset(0),e>1&&u.removeLayer(t),l.removeLayer(t._spiderLeg),delete t._spiderLeg);h._animationEnd(),h.fire("unspiderfied",{cluster:a,markers:d})},200)}}),L.MarkerClusterGroup.include({_spiderfied:null,unspiderfy:function(){this._unspiderfy.apply(this,arguments)},_spiderfierOnAdd:function(){this._map.on("click",this._unspiderfyWrapper,this),this._map.options.zoomAnimation&&this._map.on("zoomstart",this._unspiderfyZoomStart,this),this._map.on("zoomend",this._noanimationUnspiderfy,this),L.Browser.touch||this._map.getRenderer(this)},_spiderfierOnRemove:function(){this._map.off("click",this._unspiderfyWrapper,this),this._map.off("zoomstart",this._unspiderfyZoomStart,this),this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._map.off("zoomend",this._noanimationUnspiderfy,this),this._noanimationUnspiderfy()
+},_unspiderfyZoomStart:function(){this._map&&this._map.on("zoomanim",this._unspiderfyZoomAnim,this)},_unspiderfyZoomAnim:function(e){L.DomUtil.hasClass(this._map._mapPane,"leaflet-touching")||(this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._unspiderfy(e))},_unspiderfyWrapper:function(){this._unspiderfy()},_unspiderfy:function(e){this._spiderfied&&this._spiderfied.unspiderfy(e)},_noanimationUnspiderfy:function(){this._spiderfied&&this._spiderfied._noanimationUnspiderfy()},_unspiderfyLayer:function(e){e._spiderLeg&&(this._featureGroup.removeLayer(e),e.clusterShow&&e.clusterShow(),e.setZIndexOffset&&e.setZIndexOffset(0),this._map.removeLayer(e._spiderLeg),delete e._spiderLeg)}}),L.MarkerClusterGroup.include({refreshClusters:function(e){return e?e instanceof L.MarkerClusterGroup?e=e._topClusterLevel.getAllChildMarkers():e instanceof L.LayerGroup?e=e._layers:e instanceof L.MarkerCluster?e=e.getAllChildMarkers():e instanceof L.Marker&&(e=[e]):e=this._topClusterLevel.getAllChildMarkers(),this._flagParentsIconsNeedUpdate(e),this._refreshClustersIcons(),this.options.singleMarkerMode&&this._refreshSingleMarkerModeMarkers(e),this},_flagParentsIconsNeedUpdate:function(e){var t,i;for(t in e)for(i=e[t].__parent;i;)i._iconNeedsUpdate=!0,i=i.__parent},_refreshSingleMarkerModeMarkers:function(e){var t,i;for(t in e)i=e[t],this.hasLayer(i)&&i.setIcon(this._overrideMarkerIcon(i))}}),L.Marker.include({refreshIconOptions:function(e,t){var i=this.options.icon;return L.setOptions(i,e),this.setIcon(i),t&&this.__parent&&this.__parent._group.refreshClusters(this),this}}),e.MarkerClusterGroup=t,e.MarkerCluster=i});
+//# sourceMappingURL=leaflet.markercluster.js.map \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/layers-2x.png b/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/layers-2x.png
new file mode 100644
index 00000000..200c333d
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/layers-2x.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/layers.png b/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/layers.png
new file mode 100644
index 00000000..1a72e578
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/layers.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon-2x.png b/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon-2x.png
new file mode 100644
index 00000000..1c26e9fc
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon-2x.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon.png b/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon.png
new file mode 100644
index 00000000..3e64e06d
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-icon.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-shadow.png b/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-shadow.png
new file mode 100644
index 00000000..9fd29795
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet/images/marker-shadow.png
Binary files differ
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet/leaflet.css b/www/wiki/extensions/Maps/resources/leaflet/leaflet/leaflet.css
new file mode 100644
index 00000000..a0932d57
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet/leaflet.css
@@ -0,0 +1,635 @@
+/* required styles */
+
+.leaflet-pane,
+.leaflet-tile,
+.leaflet-marker-icon,
+.leaflet-marker-shadow,
+.leaflet-tile-container,
+.leaflet-pane > svg,
+.leaflet-pane > canvas,
+.leaflet-zoom-box,
+.leaflet-image-layer,
+.leaflet-layer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ }
+.leaflet-container {
+ overflow: hidden;
+ }
+.leaflet-tile,
+.leaflet-marker-icon,
+.leaflet-marker-shadow {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+ -webkit-user-drag: none;
+ }
+/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
+.leaflet-safari .leaflet-tile {
+ image-rendering: -webkit-optimize-contrast;
+ }
+/* hack that prevents hw layers "stretching" when loading new tiles */
+.leaflet-safari .leaflet-tile-container {
+ width: 1600px;
+ height: 1600px;
+ -webkit-transform-origin: 0 0;
+ }
+.leaflet-marker-icon,
+.leaflet-marker-shadow {
+ display: block;
+ }
+/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
+/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
+.leaflet-container .leaflet-overlay-pane svg,
+.leaflet-container .leaflet-marker-pane img,
+.leaflet-container .leaflet-shadow-pane img,
+.leaflet-container .leaflet-tile-pane img,
+.leaflet-container img.leaflet-image-layer,
+.leaflet-container .leaflet-tile {
+ max-width: none !important;
+ max-height: none !important;
+ }
+
+.leaflet-container.leaflet-touch-zoom {
+ -ms-touch-action: pan-x pan-y;
+ touch-action: pan-x pan-y;
+ }
+.leaflet-container.leaflet-touch-drag {
+ -ms-touch-action: pinch-zoom;
+ /* Fallback for FF which doesn't support pinch-zoom */
+ touch-action: none;
+ touch-action: pinch-zoom;
+}
+.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
+ -ms-touch-action: none;
+ touch-action: none;
+}
+.leaflet-container {
+ -webkit-tap-highlight-color: transparent;
+}
+.leaflet-container a {
+ -webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
+}
+.leaflet-tile {
+ filter: inherit;
+ visibility: hidden;
+ }
+.leaflet-tile-loaded {
+ visibility: inherit;
+ }
+.leaflet-zoom-box {
+ width: 0;
+ height: 0;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ z-index: 800;
+ }
+/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
+.leaflet-overlay-pane svg {
+ -moz-user-select: none;
+ }
+
+.leaflet-pane { z-index: 400; }
+
+.leaflet-tile-pane { z-index: 200; }
+.leaflet-overlay-pane { z-index: 400; }
+.leaflet-shadow-pane { z-index: 500; }
+.leaflet-marker-pane { z-index: 600; }
+.leaflet-tooltip-pane { z-index: 650; }
+.leaflet-popup-pane { z-index: 700; }
+
+.leaflet-map-pane canvas { z-index: 100; }
+.leaflet-map-pane svg { z-index: 200; }
+
+.leaflet-vml-shape {
+ width: 1px;
+ height: 1px;
+ }
+.lvml {
+ behavior: url(#default#VML);
+ display: inline-block;
+ position: absolute;
+ }
+
+
+/* control positioning */
+
+.leaflet-control {
+ position: relative;
+ z-index: 800;
+ pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
+ pointer-events: auto;
+ }
+.leaflet-top,
+.leaflet-bottom {
+ position: absolute;
+ z-index: 1000;
+ pointer-events: none;
+ }
+.leaflet-top {
+ top: 0;
+ }
+.leaflet-right {
+ right: 0;
+ }
+.leaflet-bottom {
+ bottom: 0;
+ }
+.leaflet-left {
+ left: 0;
+ }
+.leaflet-control {
+ float: left;
+ clear: both;
+ }
+.leaflet-right .leaflet-control {
+ float: right;
+ }
+.leaflet-top .leaflet-control {
+ margin-top: 10px;
+ }
+.leaflet-bottom .leaflet-control {
+ margin-bottom: 10px;
+ }
+.leaflet-left .leaflet-control {
+ margin-left: 10px;
+ }
+.leaflet-right .leaflet-control {
+ margin-right: 10px;
+ }
+
+
+/* zoom and fade animations */
+
+.leaflet-fade-anim .leaflet-tile {
+ will-change: opacity;
+ }
+.leaflet-fade-anim .leaflet-popup {
+ opacity: 0;
+ -webkit-transition: opacity 0.2s linear;
+ -moz-transition: opacity 0.2s linear;
+ transition: opacity 0.2s linear;
+ }
+.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
+ opacity: 1;
+ }
+.leaflet-zoom-animated {
+ -webkit-transform-origin: 0 0;
+ -ms-transform-origin: 0 0;
+ transform-origin: 0 0;
+ }
+.leaflet-zoom-anim .leaflet-zoom-animated {
+ will-change: transform;
+ }
+.leaflet-zoom-anim .leaflet-zoom-animated {
+ -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
+ -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
+ transition: transform 0.25s cubic-bezier(0,0,0.25,1);
+ }
+.leaflet-zoom-anim .leaflet-tile,
+.leaflet-pan-anim .leaflet-tile {
+ -webkit-transition: none;
+ -moz-transition: none;
+ transition: none;
+ }
+
+.leaflet-zoom-anim .leaflet-zoom-hide {
+ visibility: hidden;
+ }
+
+
+/* cursors */
+
+.leaflet-interactive {
+ cursor: pointer;
+ }
+.leaflet-grab {
+ cursor: -webkit-grab;
+ cursor: -moz-grab;
+ cursor: grab;
+ }
+.leaflet-crosshair,
+.leaflet-crosshair .leaflet-interactive {
+ cursor: crosshair;
+ }
+.leaflet-popup-pane,
+.leaflet-control {
+ cursor: auto;
+ }
+.leaflet-dragging .leaflet-grab,
+.leaflet-dragging .leaflet-grab .leaflet-interactive,
+.leaflet-dragging .leaflet-marker-draggable {
+ cursor: move;
+ cursor: -webkit-grabbing;
+ cursor: -moz-grabbing;
+ cursor: grabbing;
+ }
+
+/* marker & overlays interactivity */
+.leaflet-marker-icon,
+.leaflet-marker-shadow,
+.leaflet-image-layer,
+.leaflet-pane > svg path,
+.leaflet-tile-container {
+ pointer-events: none;
+ }
+
+.leaflet-marker-icon.leaflet-interactive,
+.leaflet-image-layer.leaflet-interactive,
+.leaflet-pane > svg path.leaflet-interactive {
+ pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
+ pointer-events: auto;
+ }
+
+/* visual tweaks */
+
+.leaflet-container {
+ background: #ddd;
+ outline: 0;
+ }
+.leaflet-container a {
+ color: #0078A8;
+ }
+.leaflet-container a.leaflet-active {
+ outline: 2px solid orange;
+ }
+.leaflet-zoom-box {
+ border: 2px dotted #38f;
+ background: rgba(255,255,255,0.5);
+ }
+
+
+/* general typography */
+.leaflet-container {
+ font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
+ }
+
+
+/* general toolbar styles */
+
+.leaflet-bar {
+ box-shadow: 0 1px 5px rgba(0,0,0,0.65);
+ border-radius: 4px;
+ }
+.leaflet-bar a,
+.leaflet-bar a:hover {
+ background-color: #fff;
+ border-bottom: 1px solid #ccc;
+ width: 26px;
+ height: 26px;
+ line-height: 26px;
+ display: block;
+ text-align: center;
+ text-decoration: none;
+ color: black;
+ }
+.leaflet-bar a,
+.leaflet-control-layers-toggle {
+ background-position: 50% 50%;
+ background-repeat: no-repeat;
+ display: block;
+ }
+.leaflet-bar a:hover {
+ background-color: #f4f4f4;
+ }
+.leaflet-bar a:first-child {
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+ }
+.leaflet-bar a:last-child {
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ border-bottom: none;
+ }
+.leaflet-bar a.leaflet-disabled {
+ cursor: default;
+ background-color: #f4f4f4;
+ color: #bbb;
+ }
+
+.leaflet-touch .leaflet-bar a {
+ width: 30px;
+ height: 30px;
+ line-height: 30px;
+ }
+.leaflet-touch .leaflet-bar a:first-child {
+ border-top-left-radius: 2px;
+ border-top-right-radius: 2px;
+ }
+.leaflet-touch .leaflet-bar a:last-child {
+ border-bottom-left-radius: 2px;
+ border-bottom-right-radius: 2px;
+ }
+
+/* zoom control */
+
+.leaflet-control-zoom-in,
+.leaflet-control-zoom-out {
+ font: bold 18px 'Lucida Console', Monaco, monospace;
+ text-indent: 1px;
+ }
+
+.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
+ font-size: 22px;
+ }
+
+
+/* layers control */
+
+.leaflet-control-layers {
+ box-shadow: 0 1px 5px rgba(0,0,0,0.4);
+ background: #fff;
+ border-radius: 5px;
+ }
+.leaflet-control-layers-toggle {
+ background-image: url(images/layers.png);
+ width: 36px;
+ height: 36px;
+ }
+.leaflet-retina .leaflet-control-layers-toggle {
+ background-image: url(images/layers-2x.png);
+ background-size: 26px 26px;
+ }
+.leaflet-touch .leaflet-control-layers-toggle {
+ width: 44px;
+ height: 44px;
+ }
+.leaflet-control-layers .leaflet-control-layers-list,
+.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
+ display: none;
+ }
+.leaflet-control-layers-expanded .leaflet-control-layers-list {
+ display: block;
+ position: relative;
+ }
+.leaflet-control-layers-expanded {
+ padding: 6px 10px 6px 6px;
+ color: #333;
+ background: #fff;
+ }
+.leaflet-control-layers-scrollbar {
+ overflow-y: scroll;
+ overflow-x: hidden;
+ padding-right: 5px;
+ }
+.leaflet-control-layers-selector {
+ margin-top: 2px;
+ position: relative;
+ top: 1px;
+ }
+.leaflet-control-layers label {
+ display: block;
+ }
+.leaflet-control-layers-separator {
+ height: 0;
+ border-top: 1px solid #ddd;
+ margin: 5px -10px 5px -6px;
+ }
+
+/* Default icon URLs */
+.leaflet-default-icon-path {
+ background-image: url(images/marker-icon.png);
+ }
+
+
+/* attribution and scale controls */
+
+.leaflet-container .leaflet-control-attribution {
+ background: #fff;
+ background: rgba(255, 255, 255, 0.7);
+ margin: 0;
+ }
+.leaflet-control-attribution,
+.leaflet-control-scale-line {
+ padding: 0 5px;
+ color: #333;
+ }
+.leaflet-control-attribution a {
+ text-decoration: none;
+ }
+.leaflet-control-attribution a:hover {
+ text-decoration: underline;
+ }
+.leaflet-container .leaflet-control-attribution,
+.leaflet-container .leaflet-control-scale {
+ font-size: 11px;
+ }
+.leaflet-left .leaflet-control-scale {
+ margin-left: 5px;
+ }
+.leaflet-bottom .leaflet-control-scale {
+ margin-bottom: 5px;
+ }
+.leaflet-control-scale-line {
+ border: 2px solid #777;
+ border-top: none;
+ line-height: 1.1;
+ padding: 2px 5px 1px;
+ font-size: 11px;
+ white-space: nowrap;
+ overflow: hidden;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+
+ background: #fff;
+ background: rgba(255, 255, 255, 0.5);
+ }
+.leaflet-control-scale-line:not(:first-child) {
+ border-top: 2px solid #777;
+ border-bottom: none;
+ margin-top: -2px;
+ }
+.leaflet-control-scale-line:not(:first-child):not(:last-child) {
+ border-bottom: 2px solid #777;
+ }
+
+.leaflet-touch .leaflet-control-attribution,
+.leaflet-touch .leaflet-control-layers,
+.leaflet-touch .leaflet-bar {
+ box-shadow: none;
+ }
+.leaflet-touch .leaflet-control-layers,
+.leaflet-touch .leaflet-bar {
+ border: 2px solid rgba(0,0,0,0.2);
+ background-clip: padding-box;
+ }
+
+
+/* popup */
+
+.leaflet-popup {
+ position: absolute;
+ text-align: center;
+ margin-bottom: 20px;
+ }
+.leaflet-popup-content-wrapper {
+ padding: 1px;
+ text-align: left;
+ border-radius: 12px;
+ }
+.leaflet-popup-content {
+ margin: 13px 19px;
+ line-height: 1.4;
+ }
+.leaflet-popup-content p {
+ margin: 18px 0;
+ }
+.leaflet-popup-tip-container {
+ width: 40px;
+ height: 20px;
+ position: absolute;
+ left: 50%;
+ margin-left: -20px;
+ overflow: hidden;
+ pointer-events: none;
+ }
+.leaflet-popup-tip {
+ width: 17px;
+ height: 17px;
+ padding: 1px;
+
+ margin: -10px auto 0;
+
+ -webkit-transform: rotate(45deg);
+ -moz-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ transform: rotate(45deg);
+ }
+.leaflet-popup-content-wrapper,
+.leaflet-popup-tip {
+ background: white;
+ color: #333;
+ box-shadow: 0 3px 14px rgba(0,0,0,0.4);
+ }
+.leaflet-container a.leaflet-popup-close-button {
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 4px 4px 0 0;
+ border: none;
+ text-align: center;
+ width: 18px;
+ height: 14px;
+ font: 16px/14px Tahoma, Verdana, sans-serif;
+ color: #c3c3c3;
+ text-decoration: none;
+ font-weight: bold;
+ background: transparent;
+ }
+.leaflet-container a.leaflet-popup-close-button:hover {
+ color: #999;
+ }
+.leaflet-popup-scrolled {
+ overflow: auto;
+ border-bottom: 1px solid #ddd;
+ border-top: 1px solid #ddd;
+ }
+
+.leaflet-oldie .leaflet-popup-content-wrapper {
+ zoom: 1;
+ }
+.leaflet-oldie .leaflet-popup-tip {
+ width: 24px;
+ margin: 0 auto;
+
+ -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
+ filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
+ }
+.leaflet-oldie .leaflet-popup-tip-container {
+ margin-top: -1px;
+ }
+
+.leaflet-oldie .leaflet-control-zoom,
+.leaflet-oldie .leaflet-control-layers,
+.leaflet-oldie .leaflet-popup-content-wrapper,
+.leaflet-oldie .leaflet-popup-tip {
+ border: 1px solid #999;
+ }
+
+
+/* div icon */
+
+.leaflet-div-icon {
+ background: #fff;
+ border: 1px solid #666;
+ }
+
+
+/* Tooltip */
+/* Base styles for the element that has a tooltip */
+.leaflet-tooltip {
+ position: absolute;
+ padding: 6px;
+ background-color: #fff;
+ border: 1px solid #fff;
+ border-radius: 3px;
+ color: #222;
+ white-space: nowrap;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ pointer-events: none;
+ box-shadow: 0 1px 3px rgba(0,0,0,0.4);
+ }
+.leaflet-tooltip.leaflet-clickable {
+ cursor: pointer;
+ pointer-events: auto;
+ }
+.leaflet-tooltip-top:before,
+.leaflet-tooltip-bottom:before,
+.leaflet-tooltip-left:before,
+.leaflet-tooltip-right:before {
+ position: absolute;
+ pointer-events: none;
+ border: 6px solid transparent;
+ background: transparent;
+ content: "";
+ }
+
+/* Directions */
+
+.leaflet-tooltip-bottom {
+ margin-top: 6px;
+}
+.leaflet-tooltip-top {
+ margin-top: -6px;
+}
+.leaflet-tooltip-bottom:before,
+.leaflet-tooltip-top:before {
+ left: 50%;
+ margin-left: -6px;
+ }
+.leaflet-tooltip-top:before {
+ bottom: 0;
+ margin-bottom: -12px;
+ border-top-color: #fff;
+ }
+.leaflet-tooltip-bottom:before {
+ top: 0;
+ margin-top: -12px;
+ margin-left: -6px;
+ border-bottom-color: #fff;
+ }
+.leaflet-tooltip-left {
+ margin-left: -6px;
+}
+.leaflet-tooltip-right {
+ margin-left: 6px;
+}
+.leaflet-tooltip-left:before,
+.leaflet-tooltip-right:before {
+ top: 50%;
+ margin-top: -6px;
+ }
+.leaflet-tooltip-left:before {
+ right: 0;
+ margin-right: -12px;
+ border-left-color: #fff;
+ }
+.leaflet-tooltip-right:before {
+ left: 0;
+ margin-left: -12px;
+ border-right-color: #fff;
+ }
diff --git a/www/wiki/extensions/Maps/resources/leaflet/leaflet/leaflet.js b/www/wiki/extensions/Maps/resources/leaflet/leaflet/leaflet.js
new file mode 100644
index 00000000..3b628aba
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/leaflet/leaflet/leaflet.js
@@ -0,0 +1,5 @@
+/* @preserve
+ * Leaflet 1.3.4+Detached: 0e566b2ad5e696ba9f79a9d48a7e51c8f4892441.0e566b2, a JS library for interactive maps. http://leafletjs.com
+ * (c) 2010-2018 Vladimir Agafonkin, (c) 2010-2011 CloudMade
+ */
+!function(t,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i(t.L={})}(this,function(t){"use strict";function i(t){var i,e,n,o;for(e=1,n=arguments.length;e<n;e++){o=arguments[e];for(i in o)t[i]=o[i]}return t}function e(t,i){var e=Array.prototype.slice;if(t.bind)return t.bind.apply(t,e.call(arguments,1));var n=e.call(arguments,2);return function(){return t.apply(i,n.length?n.concat(e.call(arguments)):arguments)}}function n(t){return t._leaflet_id=t._leaflet_id||++ei,t._leaflet_id}function o(t,i,e){var n,o,s,r;return r=function(){n=!1,o&&(s.apply(e,o),o=!1)},s=function(){n?o=arguments:(t.apply(e,arguments),setTimeout(r,i),n=!0)}}function s(t,i,e){var n=i[1],o=i[0],s=n-o;return t===n&&e?t:((t-o)%s+s)%s+o}function r(){return!1}function a(t,i){var e=Math.pow(10,void 0===i?6:i);return Math.round(t*e)/e}function h(t){return t.trim?t.trim():t.replace(/^\s+|\s+$/g,"")}function u(t){return h(t).split(/\s+/)}function l(t,i){t.hasOwnProperty("options")||(t.options=t.options?ii(t.options):{});for(var e in i)t.options[e]=i[e];return t.options}function c(t,i,e){var n=[];for(var o in t)n.push(encodeURIComponent(e?o.toUpperCase():o)+"="+encodeURIComponent(t[o]));return(i&&-1!==i.indexOf("?")?"&":"?")+n.join("&")}function _(t,i){return t.replace(ni,function(t,e){var n=i[e];if(void 0===n)throw new Error("No value provided for variable "+t);return"function"==typeof n&&(n=n(i)),n})}function d(t,i){for(var e=0;e<t.length;e++)if(t[e]===i)return e;return-1}function p(t){return window["webkit"+t]||window["moz"+t]||window["ms"+t]}function m(t){var i=+new Date,e=Math.max(0,16-(i-ri));return ri=i+e,window.setTimeout(t,e)}function f(t,i,n){if(!n||ai!==m)return ai.call(window,e(t,i));t.call(i)}function g(t){t&&hi.call(window,t)}function v(){}function y(t){if("undefined"!=typeof L&&L&&L.Mixin){t=oi(t)?t:[t];for(var i=0;i<t.length;i++)t[i]===L.Mixin.Events&&console.warn("Deprecated include of L.Mixin.Events: this property will be removed in future releases, please inherit from L.Evented instead.",(new Error).stack)}}function x(t,i,e){this.x=e?Math.round(t):t,this.y=e?Math.round(i):i}function w(t,i,e){return t instanceof x?t:oi(t)?new x(t[0],t[1]):void 0===t||null===t?t:"object"==typeof t&&"x"in t&&"y"in t?new x(t.x,t.y):new x(t,i,e)}function P(t,i){if(t)for(var e=i?[t,i]:t,n=0,o=e.length;n<o;n++)this.extend(e[n])}function b(t,i){return!t||t instanceof P?t:new P(t,i)}function T(t,i){if(t)for(var e=i?[t,i]:t,n=0,o=e.length;n<o;n++)this.extend(e[n])}function z(t,i){return t instanceof T?t:new T(t,i)}function M(t,i,e){if(isNaN(t)||isNaN(i))throw new Error("Invalid LatLng object: ("+t+", "+i+")");this.lat=+t,this.lng=+i,void 0!==e&&(this.alt=+e)}function C(t,i,e){return t instanceof M?t:oi(t)&&"object"!=typeof t[0]?3===t.length?new M(t[0],t[1],t[2]):2===t.length?new M(t[0],t[1]):null:void 0===t||null===t?t:"object"==typeof t&&"lat"in t?new M(t.lat,"lng"in t?t.lng:t.lon,t.alt):void 0===i?null:new M(t,i,e)}function S(t,i,e,n){if(oi(t))return this._a=t[0],this._b=t[1],this._c=t[2],void(this._d=t[3]);this._a=t,this._b=i,this._c=e,this._d=n}function Z(t,i,e,n){return new S(t,i,e,n)}function E(t){return document.createElementNS("http://www.w3.org/2000/svg",t)}function k(t,i){var e,n,o,s,r,a,h="";for(e=0,o=t.length;e<o;e++){for(n=0,s=(r=t[e]).length;n<s;n++)a=r[n],h+=(n?"L":"M")+a.x+" "+a.y;h+=i?Ji?"z":"x":""}return h||"M0 0"}function A(t){return navigator.userAgent.toLowerCase().indexOf(t)>=0}function B(t,i,e,n){return"touchstart"===i?O(t,e,n):"touchmove"===i?W(t,e,n):"touchend"===i&&H(t,e,n),this}function I(t,i,e){var n=t["_leaflet_"+i+e];return"touchstart"===i?t.removeEventListener(te,n,!1):"touchmove"===i?t.removeEventListener(ie,n,!1):"touchend"===i&&(t.removeEventListener(ee,n,!1),t.removeEventListener(ne,n,!1)),this}function O(t,i,n){var o=e(function(t){if("mouse"!==t.pointerType&&t.MSPOINTER_TYPE_MOUSE&&t.pointerType!==t.MSPOINTER_TYPE_MOUSE){if(!(oe.indexOf(t.target.tagName)<0))return;Pt(t)}j(t,i)});t["_leaflet_touchstart"+n]=o,t.addEventListener(te,o,!1),re||(document.documentElement.addEventListener(te,R,!0),document.documentElement.addEventListener(ie,N,!0),document.documentElement.addEventListener(ee,D,!0),document.documentElement.addEventListener(ne,D,!0),re=!0)}function R(t){se[t.pointerId]=t,ae++}function N(t){se[t.pointerId]&&(se[t.pointerId]=t)}function D(t){delete se[t.pointerId],ae--}function j(t,i){t.touches=[];for(var e in se)t.touches.push(se[e]);t.changedTouches=[t],i(t)}function W(t,i,e){var n=function(t){(t.pointerType!==t.MSPOINTER_TYPE_MOUSE&&"mouse"!==t.pointerType||0!==t.buttons)&&j(t,i)};t["_leaflet_touchmove"+e]=n,t.addEventListener(ie,n,!1)}function H(t,i,e){var n=function(t){j(t,i)};t["_leaflet_touchend"+e]=n,t.addEventListener(ee,n,!1),t.addEventListener(ne,n,!1)}function F(t,i,e){function n(t){var i;if(Vi){if(!bi||"mouse"===t.pointerType)return;i=ae}else i=t.touches.length;if(!(i>1)){var e=Date.now(),n=e-(s||e);r=t.touches?t.touches[0]:t,a=n>0&&n<=h,s=e}}function o(t){if(a&&!r.cancelBubble){if(Vi){if(!bi||"mouse"===t.pointerType)return;var e,n,o={};for(n in r)e=r[n],o[n]=e&&e.bind?e.bind(r):e;r=o}r.type="dblclick",i(r),s=null}}var s,r,a=!1,h=250;return t[le+he+e]=n,t[le+ue+e]=o,t[le+"dblclick"+e]=i,t.addEventListener(he,n,!1),t.addEventListener(ue,o,!1),t.addEventListener("dblclick",i,!1),this}function U(t,i){var e=t[le+he+i],n=t[le+ue+i],o=t[le+"dblclick"+i];return t.removeEventListener(he,e,!1),t.removeEventListener(ue,n,!1),bi||t.removeEventListener("dblclick",o,!1),this}function V(t){return"string"==typeof t?document.getElementById(t):t}function q(t,i){var e=t.style[i]||t.currentStyle&&t.currentStyle[i];if((!e||"auto"===e)&&document.defaultView){var n=document.defaultView.getComputedStyle(t,null);e=n?n[i]:null}return"auto"===e?null:e}function G(t,i,e){var n=document.createElement(t);return n.className=i||"",e&&e.appendChild(n),n}function K(t){var i=t.parentNode;i&&i.removeChild(t)}function Y(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function X(t){var i=t.parentNode;i.lastChild!==t&&i.appendChild(t)}function J(t){var i=t.parentNode;i.firstChild!==t&&i.insertBefore(t,i.firstChild)}function $(t,i){if(void 0!==t.classList)return t.classList.contains(i);var e=et(t);return e.length>0&&new RegExp("(^|\\s)"+i+"(\\s|$)").test(e)}function Q(t,i){if(void 0!==t.classList)for(var e=u(i),n=0,o=e.length;n<o;n++)t.classList.add(e[n]);else if(!$(t,i)){var s=et(t);it(t,(s?s+" ":"")+i)}}function tt(t,i){void 0!==t.classList?t.classList.remove(i):it(t,h((" "+et(t)+" ").replace(" "+i+" "," ")))}function it(t,i){void 0===t.className.baseVal?t.className=i:t.className.baseVal=i}function et(t){return void 0===t.className.baseVal?t.className:t.className.baseVal}function nt(t,i){"opacity"in t.style?t.style.opacity=i:"filter"in t.style&&ot(t,i)}function ot(t,i){var e=!1,n="DXImageTransform.Microsoft.Alpha";try{e=t.filters.item(n)}catch(t){if(1===i)return}i=Math.round(100*i),e?(e.Enabled=100!==i,e.Opacity=i):t.style.filter+=" progid:"+n+"(opacity="+i+")"}function st(t){for(var i=document.documentElement.style,e=0;e<t.length;e++)if(t[e]in i)return t[e];return!1}function rt(t,i,e){var n=i||new x(0,0);t.style[ce]=(Ri?"translate("+n.x+"px,"+n.y+"px)":"translate3d("+n.x+"px,"+n.y+"px,0)")+(e?" scale("+e+")":"")}function at(t,i){t._leaflet_pos=i,ji?rt(t,i):(t.style.left=i.x+"px",t.style.top=i.y+"px")}function ht(t){return t._leaflet_pos||new x(0,0)}function ut(){mt(window,"dragstart",Pt)}function lt(){ft(window,"dragstart",Pt)}function ct(t){for(;-1===t.tabIndex;)t=t.parentNode;t.style&&(_t(),me=t,fe=t.style.outline,t.style.outline="none",mt(window,"keydown",_t))}function _t(){me&&(me.style.outline=fe,me=void 0,fe=void 0,ft(window,"keydown",_t))}function dt(t){do{t=t.parentNode}while(!(t.offsetWidth&&t.offsetHeight||t===document.body));return t}function pt(t){var i=t.getBoundingClientRect();return{x:i.width/t.offsetWidth||1,y:i.height/t.offsetHeight||1,boundingClientRect:i}}function mt(t,i,e,n){if("object"==typeof i)for(var o in i)gt(t,o,i[o],e);else for(var s=0,r=(i=u(i)).length;s<r;s++)gt(t,i[s],e,n);return this}function ft(t,i,e,n){if("object"==typeof i)for(var o in i)vt(t,o,i[o],e);else if(i)for(var s=0,r=(i=u(i)).length;s<r;s++)vt(t,i[s],e,n);else{for(var a in t[ye])vt(t,a,t[ye][a]);delete t[ye]}return this}function gt(t,i,e,o){var s=i+n(e)+(o?"_"+n(o):"");if(t[ye]&&t[ye][s])return this;var r=function(i){return e.call(o||t,i||window.event)},a=r;Vi&&0===i.indexOf("touch")?B(t,i,r,s):!qi||"dblclick"!==i||!F||Vi&&Ei?"addEventListener"in t?"mousewheel"===i?t.addEventListener("onwheel"in t?"wheel":"mousewheel",r,!1):"mouseenter"===i||"mouseleave"===i?(r=function(i){i=i||window.event,Ct(t,i)&&a(i)},t.addEventListener("mouseenter"===i?"mouseover":"mouseout",r,!1)):("click"===i&&zi&&(r=function(t){St(t,a)}),t.addEventListener(i,r,!1)):"attachEvent"in t&&t.attachEvent("on"+i,r):F(t,r,s),t[ye]=t[ye]||{},t[ye][s]=r}function vt(t,i,e,o){var s=i+n(e)+(o?"_"+n(o):""),r=t[ye]&&t[ye][s];if(!r)return this;Vi&&0===i.indexOf("touch")?I(t,i,s):!qi||"dblclick"!==i||!U||Vi&&Ei?"removeEventListener"in t?"mousewheel"===i?t.removeEventListener("onwheel"in t?"wheel":"mousewheel",r,!1):t.removeEventListener("mouseenter"===i?"mouseover":"mouseleave"===i?"mouseout":i,r,!1):"detachEvent"in t&&t.detachEvent("on"+i,r):U(t,s),t[ye][s]=null}function yt(t){return t.stopPropagation?t.stopPropagation():t.originalEvent?t.originalEvent._stopped=!0:t.cancelBubble=!0,Mt(t),this}function xt(t){return gt(t,"mousewheel",yt),this}function wt(t){return mt(t,"mousedown touchstart dblclick",yt),gt(t,"click",zt),this}function Pt(t){return t.preventDefault?t.preventDefault():t.returnValue=!1,this}function Lt(t){return Pt(t),yt(t),this}function bt(t,i){if(!i)return new x(t.clientX,t.clientY);var e=pt(i),n=e.boundingClientRect;return new x((t.clientX-n.left)/e.x-i.clientLeft,(t.clientY-n.top)/e.y-i.clientTop)}function Tt(t){return bi?t.wheelDeltaY/2:t.deltaY&&0===t.deltaMode?-t.deltaY/xe:t.deltaY&&1===t.deltaMode?20*-t.deltaY:t.deltaY&&2===t.deltaMode?60*-t.deltaY:t.deltaX||t.deltaZ?0:t.wheelDelta?(t.wheelDeltaY||t.wheelDelta)/2:t.detail&&Math.abs(t.detail)<32765?20*-t.detail:t.detail?t.detail/-32765*60:0}function zt(t){we[t.type]=!0}function Mt(t){var i=we[t.type];return we[t.type]=!1,i}function Ct(t,i){var e=i.relatedTarget;if(!e)return!0;try{for(;e&&e!==t;)e=e.parentNode}catch(t){return!1}return e!==t}function St(t,i){var e=t.timeStamp||t.originalEvent&&t.originalEvent.timeStamp,n=ge&&e-ge;n&&n>100&&n<500||t.target._simulatedClick&&!t._simulated?Lt(t):(ge=e,i(t))}function Zt(t,i){if(!i||!t.length)return t.slice();var e=i*i;return t=Bt(t,e),t=kt(t,e)}function Et(t,i,e){return Math.sqrt(Dt(t,i,e,!0))}function kt(t,i){var e=t.length,n=new(typeof Uint8Array!=void 0+""?Uint8Array:Array)(e);n[0]=n[e-1]=1,At(t,n,i,0,e-1);var o,s=[];for(o=0;o<e;o++)n[o]&&s.push(t[o]);return s}function At(t,i,e,n,o){var s,r,a,h=0;for(r=n+1;r<=o-1;r++)(a=Dt(t[r],t[n],t[o],!0))>h&&(s=r,h=a);h>e&&(i[s]=1,At(t,i,e,n,s),At(t,i,e,s,o))}function Bt(t,i){for(var e=[t[0]],n=1,o=0,s=t.length;n<s;n++)Nt(t[n],t[o])>i&&(e.push(t[n]),o=n);return o<s-1&&e.push(t[s-1]),e}function It(t,i,e,n,o){var s,r,a,h=n?ke:Rt(t,e),u=Rt(i,e);for(ke=u;;){if(!(h|u))return[t,i];if(h&u)return!1;a=Rt(r=Ot(t,i,s=h||u,e,o),e),s===h?(t=r,h=a):(i=r,u=a)}}function Ot(t,i,e,n,o){var s,r,a=i.x-t.x,h=i.y-t.y,u=n.min,l=n.max;return 8&e?(s=t.x+a*(l.y-t.y)/h,r=l.y):4&e?(s=t.x+a*(u.y-t.y)/h,r=u.y):2&e?(s=l.x,r=t.y+h*(l.x-t.x)/a):1&e&&(s=u.x,r=t.y+h*(u.x-t.x)/a),new x(s,r,o)}function Rt(t,i){var e=0;return t.x<i.min.x?e|=1:t.x>i.max.x&&(e|=2),t.y<i.min.y?e|=4:t.y>i.max.y&&(e|=8),e}function Nt(t,i){var e=i.x-t.x,n=i.y-t.y;return e*e+n*n}function Dt(t,i,e,n){var o,s=i.x,r=i.y,a=e.x-s,h=e.y-r,u=a*a+h*h;return u>0&&((o=((t.x-s)*a+(t.y-r)*h)/u)>1?(s=e.x,r=e.y):o>0&&(s+=a*o,r+=h*o)),a=t.x-s,h=t.y-r,n?a*a+h*h:new x(s,r)}function jt(t){return!oi(t[0])||"object"!=typeof t[0][0]&&void 0!==t[0][0]}function Wt(t){return console.warn("Deprecated use of _flat, please use L.LineUtil.isFlat instead."),jt(t)}function Ht(t,i,e){var n,o,s,r,a,h,u,l,c,_=[1,4,2,8];for(o=0,u=t.length;o<u;o++)t[o]._code=Rt(t[o],i);for(r=0;r<4;r++){for(l=_[r],n=[],o=0,s=(u=t.length)-1;o<u;s=o++)a=t[o],h=t[s],a._code&l?h._code&l||((c=Ot(h,a,l,i,e))._code=Rt(c,i),n.push(c)):(h._code&l&&((c=Ot(h,a,l,i,e))._code=Rt(c,i),n.push(c)),n.push(a));t=n}return t}function Ft(t,i){var e,n,o,s,r="Feature"===t.type?t.geometry:t,a=r?r.coordinates:null,h=[],u=i&&i.pointToLayer,l=i&&i.coordsToLatLng||Ut;if(!a&&!r)return null;switch(r.type){case"Point":return e=l(a),u?u(t,e):new $e(e);case"MultiPoint":for(o=0,s=a.length;o<s;o++)e=l(a[o]),h.push(u?u(t,e):new $e(e));return new Ke(h);case"LineString":case"MultiLineString":return n=Vt(a,"LineString"===r.type?0:1,l),new nn(n,i);case"Polygon":case"MultiPolygon":return n=Vt(a,"Polygon"===r.type?1:2,l),new on(n,i);case"GeometryCollection":for(o=0,s=r.geometries.length;o<s;o++){var c=Ft({geometry:r.geometries[o],type:"Feature",properties:t.properties},i);c&&h.push(c)}return new Ke(h);default:throw new Error("Invalid GeoJSON object.")}}function Ut(t){return new M(t[1],t[0],t[2])}function Vt(t,i,e){for(var n,o=[],s=0,r=t.length;s<r;s++)n=i?Vt(t[s],i-1,e):(e||Ut)(t[s]),o.push(n);return o}function qt(t,i){return i="number"==typeof i?i:6,void 0!==t.alt?[a(t.lng,i),a(t.lat,i),a(t.alt,i)]:[a(t.lng,i),a(t.lat,i)]}function Gt(t,i,e,n){for(var o=[],s=0,r=t.length;s<r;s++)o.push(i?Gt(t[s],i-1,e,n):qt(t[s],n));return!i&&e&&o.push(o[0]),o}function Kt(t,e){return t.feature?i({},t.feature,{geometry:e}):Yt(e)}function Yt(t){return"Feature"===t.type||"FeatureCollection"===t.type?t:{type:"Feature",properties:{},geometry:t}}function Xt(t,i){return new sn(t,i)}function Jt(t,i){return new mn(t,i)}function $t(t){return Xi?new vn(t):null}function Qt(t){return Ji||$i?new Pn(t):null}var ti=Object.freeze;Object.freeze=function(t){return t};var ii=Object.create||function(){function t(){}return function(i){return t.prototype=i,new t}}(),ei=0,ni=/\{ *([\w_-]+) *\}/g,oi=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)},si="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=",ri=0,ai=window.requestAnimationFrame||p("RequestAnimationFrame")||m,hi=window.cancelAnimationFrame||p("CancelAnimationFrame")||p("CancelRequestAnimationFrame")||function(t){window.clearTimeout(t)},ui=(Object.freeze||Object)({freeze:ti,extend:i,create:ii,bind:e,lastId:ei,stamp:n,throttle:o,wrapNum:s,falseFn:r,formatNum:a,trim:h,splitWords:u,setOptions:l,getParamString:c,template:_,isArray:oi,indexOf:d,emptyImageUrl:si,requestFn:ai,cancelFn:hi,requestAnimFrame:f,cancelAnimFrame:g});v.extend=function(t){var e=function(){this.initialize&&this.initialize.apply(this,arguments),this.callInitHooks()},n=e.__super__=this.prototype,o=ii(n);o.constructor=e,e.prototype=o;for(var s in this)this.hasOwnProperty(s)&&"prototype"!==s&&"__super__"!==s&&(e[s]=this[s]);return t.statics&&(i(e,t.statics),delete t.statics),t.includes&&(y(t.includes),i.apply(null,[o].concat(t.includes)),delete t.includes),o.options&&(t.options=i(ii(o.options),t.options)),i(o,t),o._initHooks=[],o.callInitHooks=function(){if(!this._initHooksCalled){n.callInitHooks&&n.callInitHooks.call(this),this._initHooksCalled=!0;for(var t=0,i=o._initHooks.length;t<i;t++)o._initHooks[t].call(this)}},e},v.include=function(t){return i(this.prototype,t),this},v.mergeOptions=function(t){return i(this.prototype.options,t),this},v.addInitHook=function(t){var i=Array.prototype.slice.call(arguments,1),e="function"==typeof t?t:function(){this[t].apply(this,i)};return this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(e),this};var li={on:function(t,i,e){if("object"==typeof t)for(var n in t)this._on(n,t[n],i);else for(var o=0,s=(t=u(t)).length;o<s;o++)this._on(t[o],i,e);return this},off:function(t,i,e){if(t)if("object"==typeof t)for(var n in t)this._off(n,t[n],i);else for(var o=0,s=(t=u(t)).length;o<s;o++)this._off(t[o],i,e);else delete this._events;return this},_on:function(t,i,e){this._events=this._events||{};var n=this._events[t];n||(n=[],this._events[t]=n),e===this&&(e=void 0);for(var o={fn:i,ctx:e},s=n,r=0,a=s.length;r<a;r++)if(s[r].fn===i&&s[r].ctx===e)return;s.push(o)},_off:function(t,i,e){var n,o,s;if(this._events&&(n=this._events[t]))if(i){if(e===this&&(e=void 0),n)for(o=0,s=n.length;o<s;o++){var a=n[o];if(a.ctx===e&&a.fn===i)return a.fn=r,this._firingCount&&(this._events[t]=n=n.slice()),void n.splice(o,1)}}else{for(o=0,s=n.length;o<s;o++)n[o].fn=r;delete this._events[t]}},fire:function(t,e,n){if(!this.listens(t,n))return this;var o=i({},e,{type:t,target:this,sourceTarget:e&&e.sourceTarget||this});if(this._events){var s=this._events[t];if(s){this._firingCount=this._firingCount+1||1;for(var r=0,a=s.length;r<a;r++){var h=s[r];h.fn.call(h.ctx||this,o)}this._firingCount--}}return n&&this._propagateEvent(o),this},listens:function(t,i){var e=this._events&&this._events[t];if(e&&e.length)return!0;if(i)for(var n in this._eventParents)if(this._eventParents[n].listens(t,i))return!0;return!1},once:function(t,i,n){if("object"==typeof t){for(var o in t)this.once(o,t[o],i);return this}var s=e(function(){this.off(t,i,n).off(t,s,n)},this);return this.on(t,i,n).on(t,s,n)},addEventParent:function(t){return this._eventParents=this._eventParents||{},this._eventParents[n(t)]=t,this},removeEventParent:function(t){return this._eventParents&&delete this._eventParents[n(t)],this},_propagateEvent:function(t){for(var e in this._eventParents)this._eventParents[e].fire(t.type,i({layer:t.target,propagatedFrom:t.target},t),!0)}};li.addEventListener=li.on,li.removeEventListener=li.clearAllEventListeners=li.off,li.addOneTimeEventListener=li.once,li.fireEvent=li.fire,li.hasEventListeners=li.listens;var ci=v.extend(li),_i=Math.trunc||function(t){return t>0?Math.floor(t):Math.ceil(t)};x.prototype={clone:function(){return new x(this.x,this.y)},add:function(t){return this.clone()._add(w(t))},_add:function(t){return this.x+=t.x,this.y+=t.y,this},subtract:function(t){return this.clone()._subtract(w(t))},_subtract:function(t){return this.x-=t.x,this.y-=t.y,this},divideBy:function(t){return this.clone()._divideBy(t)},_divideBy:function(t){return this.x/=t,this.y/=t,this},multiplyBy:function(t){return this.clone()._multiplyBy(t)},_multiplyBy:function(t){return this.x*=t,this.y*=t,this},scaleBy:function(t){return new x(this.x*t.x,this.y*t.y)},unscaleBy:function(t){return new x(this.x/t.x,this.y/t.y)},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.clone()._ceil()},_ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},trunc:function(){return this.clone()._trunc()},_trunc:function(){return this.x=_i(this.x),this.y=_i(this.y),this},distanceTo:function(t){var i=(t=w(t)).x-this.x,e=t.y-this.y;return Math.sqrt(i*i+e*e)},equals:function(t){return(t=w(t)).x===this.x&&t.y===this.y},contains:function(t){return t=w(t),Math.abs(t.x)<=Math.abs(this.x)&&Math.abs(t.y)<=Math.abs(this.y)},toString:function(){return"Point("+a(this.x)+", "+a(this.y)+")"}},P.prototype={extend:function(t){return t=w(t),this.min||this.max?(this.min.x=Math.min(t.x,this.min.x),this.max.x=Math.max(t.x,this.max.x),this.min.y=Math.min(t.y,this.min.y),this.max.y=Math.max(t.y,this.max.y)):(this.min=t.clone(),this.max=t.clone()),this},getCenter:function(t){return new x((this.min.x+this.max.x)/2,(this.min.y+this.max.y)/2,t)},getBottomLeft:function(){return new x(this.min.x,this.max.y)},getTopRight:function(){return new x(this.max.x,this.min.y)},getTopLeft:function(){return this.min},getBottomRight:function(){return this.max},getSize:function(){return this.max.subtract(this.min)},contains:function(t){var i,e;return(t="number"==typeof t[0]||t instanceof x?w(t):b(t))instanceof P?(i=t.min,e=t.max):i=e=t,i.x>=this.min.x&&e.x<=this.max.x&&i.y>=this.min.y&&e.y<=this.max.y},intersects:function(t){t=b(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>=i.x&&n.x<=e.x,r=o.y>=i.y&&n.y<=e.y;return s&&r},overlaps:function(t){t=b(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>i.x&&n.x<e.x,r=o.y>i.y&&n.y<e.y;return s&&r},isValid:function(){return!(!this.min||!this.max)}},T.prototype={extend:function(t){var i,e,n=this._southWest,o=this._northEast;if(t instanceof M)i=t,e=t;else{if(!(t instanceof T))return t?this.extend(C(t)||z(t)):this;if(i=t._southWest,e=t._northEast,!i||!e)return this}return n||o?(n.lat=Math.min(i.lat,n.lat),n.lng=Math.min(i.lng,n.lng),o.lat=Math.max(e.lat,o.lat),o.lng=Math.max(e.lng,o.lng)):(this._southWest=new M(i.lat,i.lng),this._northEast=new M(e.lat,e.lng)),this},pad:function(t){var i=this._southWest,e=this._northEast,n=Math.abs(i.lat-e.lat)*t,o=Math.abs(i.lng-e.lng)*t;return new T(new M(i.lat-n,i.lng-o),new M(e.lat+n,e.lng+o))},getCenter:function(){return new M((this._southWest.lat+this._northEast.lat)/2,(this._southWest.lng+this._northEast.lng)/2)},getSouthWest:function(){return this._southWest},getNorthEast:function(){return this._northEast},getNorthWest:function(){return new M(this.getNorth(),this.getWest())},getSouthEast:function(){return new M(this.getSouth(),this.getEast())},getWest:function(){return this._southWest.lng},getSouth:function(){return this._southWest.lat},getEast:function(){return this._northEast.lng},getNorth:function(){return this._northEast.lat},contains:function(t){t="number"==typeof t[0]||t instanceof M||"lat"in t?C(t):z(t);var i,e,n=this._southWest,o=this._northEast;return t instanceof T?(i=t.getSouthWest(),e=t.getNorthEast()):i=e=t,i.lat>=n.lat&&e.lat<=o.lat&&i.lng>=n.lng&&e.lng<=o.lng},intersects:function(t){t=z(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>=i.lat&&n.lat<=e.lat,r=o.lng>=i.lng&&n.lng<=e.lng;return s&&r},overlaps:function(t){t=z(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>i.lat&&n.lat<e.lat,r=o.lng>i.lng&&n.lng<e.lng;return s&&r},toBBoxString:function(){return[this.getWest(),this.getSouth(),this.getEast(),this.getNorth()].join(",")},equals:function(t,i){return!!t&&(t=z(t),this._southWest.equals(t.getSouthWest(),i)&&this._northEast.equals(t.getNorthEast(),i))},isValid:function(){return!(!this._southWest||!this._northEast)}},M.prototype={equals:function(t,i){return!!t&&(t=C(t),Math.max(Math.abs(this.lat-t.lat),Math.abs(this.lng-t.lng))<=(void 0===i?1e-9:i))},toString:function(t){return"LatLng("+a(this.lat,t)+", "+a(this.lng,t)+")"},distanceTo:function(t){return pi.distance(this,C(t))},wrap:function(){return pi.wrapLatLng(this)},toBounds:function(t){var i=180*t/40075017,e=i/Math.cos(Math.PI/180*this.lat);return z([this.lat-i,this.lng-e],[this.lat+i,this.lng+e])},clone:function(){return new M(this.lat,this.lng,this.alt)}};var di={latLngToPoint:function(t,i){var e=this.projection.project(t),n=this.scale(i);return this.transformation._transform(e,n)},pointToLatLng:function(t,i){var e=this.scale(i),n=this.transformation.untransform(t,e);return this.projection.unproject(n)},project:function(t){return this.projection.project(t)},unproject:function(t){return this.projection.unproject(t)},scale:function(t){return 256*Math.pow(2,t)},zoom:function(t){return Math.log(t/256)/Math.LN2},getProjectedBounds:function(t){if(this.infinite)return null;var i=this.projection.bounds,e=this.scale(t);return new P(this.transformation.transform(i.min,e),this.transformation.transform(i.max,e))},infinite:!1,wrapLatLng:function(t){var i=this.wrapLng?s(t.lng,this.wrapLng,!0):t.lng;return new M(this.wrapLat?s(t.lat,this.wrapLat,!0):t.lat,i,t.alt)},wrapLatLngBounds:function(t){var i=t.getCenter(),e=this.wrapLatLng(i),n=i.lat-e.lat,o=i.lng-e.lng;if(0===n&&0===o)return t;var s=t.getSouthWest(),r=t.getNorthEast();return new T(new M(s.lat-n,s.lng-o),new M(r.lat-n,r.lng-o))}},pi=i({},di,{wrapLng:[-180,180],R:6371e3,distance:function(t,i){var e=Math.PI/180,n=t.lat*e,o=i.lat*e,s=Math.sin((i.lat-t.lat)*e/2),r=Math.sin((i.lng-t.lng)*e/2),a=s*s+Math.cos(n)*Math.cos(o)*r*r,h=2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a));return this.R*h}}),mi={R:6378137,MAX_LATITUDE:85.0511287798,project:function(t){var i=Math.PI/180,e=this.MAX_LATITUDE,n=Math.max(Math.min(e,t.lat),-e),o=Math.sin(n*i);return new x(this.R*t.lng*i,this.R*Math.log((1+o)/(1-o))/2)},unproject:function(t){var i=180/Math.PI;return new M((2*Math.atan(Math.exp(t.y/this.R))-Math.PI/2)*i,t.x*i/this.R)},bounds:function(){var t=6378137*Math.PI;return new P([-t,-t],[t,t])}()};S.prototype={transform:function(t,i){return this._transform(t.clone(),i)},_transform:function(t,i){return i=i||1,t.x=i*(this._a*t.x+this._b),t.y=i*(this._c*t.y+this._d),t},untransform:function(t,i){return i=i||1,new x((t.x/i-this._b)/this._a,(t.y/i-this._d)/this._c)}};var fi,gi,vi,yi=i({},pi,{code:"EPSG:3857",projection:mi,transformation:function(){var t=.5/(Math.PI*mi.R);return Z(t,.5,-t,.5)}()}),xi=i({},yi,{code:"EPSG:900913"}),wi=document.documentElement.style,Pi="ActiveXObject"in window,Li=Pi&&!document.addEventListener,bi="msLaunchUri"in navigator&&!("documentMode"in document),Ti=A("webkit"),zi=A("android"),Mi=A("android 2")||A("android 3"),Ci=parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1],10),Si=zi&&A("Google")&&Ci<537&&!("AudioNode"in window),Zi=!!window.opera,Ei=A("chrome"),ki=A("gecko")&&!Ti&&!Zi&&!Pi,Ai=!Ei&&A("safari"),Bi=A("phantom"),Ii="OTransition"in wi,Oi=0===navigator.platform.indexOf("Win"),Ri=Pi&&"transition"in wi,Ni="WebKitCSSMatrix"in window&&"m11"in new window.WebKitCSSMatrix&&!Mi,Di="MozPerspective"in wi,ji=!window.L_DISABLE_3D&&(Ri||Ni||Di)&&!Ii&&!Bi,Wi="undefined"!=typeof orientation||A("mobile"),Hi=Wi&&Ti,Fi=Wi&&Ni,Ui=!window.PointerEvent&&window.MSPointerEvent,Vi=!(!window.PointerEvent&&!Ui),qi=!window.L_NO_TOUCH&&(Vi||"ontouchstart"in window||window.DocumentTouch&&document instanceof window.DocumentTouch),Gi=Wi&&Zi,Ki=Wi&&ki,Yi=(window.devicePixelRatio||window.screen.deviceXDPI/window.screen.logicalXDPI)>1,Xi=!!document.createElement("canvas").getContext,Ji=!(!document.createElementNS||!E("svg").createSVGRect),$i=!Ji&&function(){try{var t=document.createElement("div");t.innerHTML='<v:shape adj="1"/>';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(t){return!1}}(),Qi=(Object.freeze||Object)({ie:Pi,ielt9:Li,edge:bi,webkit:Ti,android:zi,android23:Mi,androidStock:Si,opera:Zi,chrome:Ei,gecko:ki,safari:Ai,phantom:Bi,opera12:Ii,win:Oi,ie3d:Ri,webkit3d:Ni,gecko3d:Di,any3d:ji,mobile:Wi,mobileWebkit:Hi,mobileWebkit3d:Fi,msPointer:Ui,pointer:Vi,touch:qi,mobileOpera:Gi,mobileGecko:Ki,retina:Yi,canvas:Xi,svg:Ji,vml:$i}),te=Ui?"MSPointerDown":"pointerdown",ie=Ui?"MSPointerMove":"pointermove",ee=Ui?"MSPointerUp":"pointerup",ne=Ui?"MSPointerCancel":"pointercancel",oe=["INPUT","SELECT","OPTION"],se={},re=!1,ae=0,he=Ui?"MSPointerDown":Vi?"pointerdown":"touchstart",ue=Ui?"MSPointerUp":Vi?"pointerup":"touchend",le="_leaflet_",ce=st(["transform","webkitTransform","OTransform","MozTransform","msTransform"]),_e=st(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),de="webkitTransition"===_e||"OTransition"===_e?_e+"End":"transitionend";if("onselectstart"in document)fi=function(){mt(window,"selectstart",Pt)},gi=function(){ft(window,"selectstart",Pt)};else{var pe=st(["userSelect","WebkitUserSelect","OUserSelect","MozUserSelect","msUserSelect"]);fi=function(){if(pe){var t=document.documentElement.style;vi=t[pe],t[pe]="none"}},gi=function(){pe&&(document.documentElement.style[pe]=vi,vi=void 0)}}var me,fe,ge,ve=(Object.freeze||Object)({TRANSFORM:ce,TRANSITION:_e,TRANSITION_END:de,get:V,getStyle:q,create:G,remove:K,empty:Y,toFront:X,toBack:J,hasClass:$,addClass:Q,removeClass:tt,setClass:it,getClass:et,setOpacity:nt,testProp:st,setTransform:rt,setPosition:at,getPosition:ht,disableTextSelection:fi,enableTextSelection:gi,disableImageDrag:ut,enableImageDrag:lt,preventOutline:ct,restoreOutline:_t,getSizedParentNode:dt,getScale:pt}),ye="_leaflet_events",xe=Oi&&Ei?2*window.devicePixelRatio:ki?window.devicePixelRatio:1,we={},Pe=(Object.freeze||Object)({on:mt,off:ft,stopPropagation:yt,disableScrollPropagation:xt,disableClickPropagation:wt,preventDefault:Pt,stop:Lt,getMousePosition:bt,getWheelDelta:Tt,fakeStop:zt,skipped:Mt,isExternalTarget:Ct,addListener:mt,removeListener:ft}),Le=ci.extend({run:function(t,i,e,n){this.stop(),this._el=t,this._inProgress=!0,this._duration=e||.25,this._easeOutPower=1/Math.max(n||.5,.2),this._startPos=ht(t),this._offset=i.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(!0),this._complete())},_animate:function(){this._animId=f(this._animate,this),this._step()},_step:function(t){var i=+new Date-this._startTime,e=1e3*this._duration;i<e?this._runFrame(this._easeOut(i/e),t):(this._runFrame(1),this._complete())},_runFrame:function(t,i){var e=this._startPos.add(this._offset.multiplyBy(t));i&&e._round(),at(this._el,e),this.fire("step")},_complete:function(){g(this._animId),this._inProgress=!1,this.fire("end")},_easeOut:function(t){return 1-Math.pow(1-t,this._easeOutPower)}}),be=ci.extend({options:{crs:yi,center:void 0,zoom:void 0,minZoom:void 0,maxZoom:void 0,layers:[],maxBounds:void 0,renderer:void 0,zoomAnimation:!0,zoomAnimationThreshold:4,fadeAnimation:!0,markerZoomAnimation:!0,transform3DLimit:8388608,zoomSnap:1,zoomDelta:1,trackResize:!0},initialize:function(t,i){i=l(this,i),this._initContainer(t),this._initLayout(),this._onResize=e(this._onResize,this),this._initEvents(),i.maxBounds&&this.setMaxBounds(i.maxBounds),void 0!==i.zoom&&(this._zoom=this._limitZoom(i.zoom)),i.center&&void 0!==i.zoom&&this.setView(C(i.center),i.zoom,{reset:!0}),this._handlers=[],this._layers={},this._zoomBoundLayers={},this._sizeChanged=!0,this.callInitHooks(),this._zoomAnimated=_e&&ji&&!Gi&&this.options.zoomAnimation,this._zoomAnimated&&(this._createAnimProxy(),mt(this._proxy,de,this._catchTransitionEnd,this)),this._addLayers(this.options.layers)},setView:function(t,e,n){return e=void 0===e?this._zoom:this._limitZoom(e),t=this._limitCenter(C(t),e,this.options.maxBounds),n=n||{},this._stop(),this._loaded&&!n.reset&&!0!==n&&(void 0!==n.animate&&(n.zoom=i({animate:n.animate},n.zoom),n.pan=i({animate:n.animate,duration:n.duration},n.pan)),this._zoom!==e?this._tryAnimatedZoom&&this._tryAnimatedZoom(t,e,n.zoom):this._tryAnimatedPan(t,n.pan))?(clearTimeout(this._sizeTimer),this):(this._resetView(t,e),this)},setZoom:function(t,i){return this._loaded?this.setView(this.getCenter(),t,{zoom:i}):(this._zoom=t,this)},zoomIn:function(t,i){return t=t||(ji?this.options.zoomDelta:1),this.setZoom(this._zoom+t,i)},zoomOut:function(t,i){return t=t||(ji?this.options.zoomDelta:1),this.setZoom(this._zoom-t,i)},setZoomAround:function(t,i,e){var n=this.getZoomScale(i),o=this.getSize().divideBy(2),s=(t instanceof x?t:this.latLngToContainerPoint(t)).subtract(o).multiplyBy(1-1/n),r=this.containerPointToLatLng(o.add(s));return this.setView(r,i,{zoom:e})},_getBoundsCenterZoom:function(t,i){i=i||{},t=t.getBounds?t.getBounds():z(t);var e=w(i.paddingTopLeft||i.padding||[0,0]),n=w(i.paddingBottomRight||i.padding||[0,0]),o=this.getBoundsZoom(t,!1,e.add(n));if((o="number"==typeof i.maxZoom?Math.min(i.maxZoom,o):o)===1/0)return{center:t.getCenter(),zoom:o};var s=n.subtract(e).divideBy(2),r=this.project(t.getSouthWest(),o),a=this.project(t.getNorthEast(),o);return{center:this.unproject(r.add(a).divideBy(2).add(s),o),zoom:o}},fitBounds:function(t,i){if(!(t=z(t)).isValid())throw new Error("Bounds are not valid.");var e=this._getBoundsCenterZoom(t,i);return this.setView(e.center,e.zoom,i)},fitWorld:function(t){return this.fitBounds([[-90,-180],[90,180]],t)},panTo:function(t,i){return this.setView(t,this._zoom,{pan:i})},panBy:function(t,i){if(t=w(t).round(),i=i||{},!t.x&&!t.y)return this.fire("moveend");if(!0!==i.animate&&!this.getSize().contains(t))return this._resetView(this.unproject(this.project(this.getCenter()).add(t)),this.getZoom()),this;if(this._panAnim||(this._panAnim=new Le,this._panAnim.on({step:this._onPanTransitionStep,end:this._onPanTransitionEnd},this)),i.noMoveStart||this.fire("movestart"),!1!==i.animate){Q(this._mapPane,"leaflet-pan-anim");var e=this._getMapPanePos().subtract(t).round();this._panAnim.run(this._mapPane,e,i.duration||.25,i.easeLinearity)}else this._rawPanBy(t),this.fire("move").fire("moveend");return this},flyTo:function(t,i,e){function n(t){var i=(g*g-m*m+(t?-1:1)*x*x*v*v)/(2*(t?g:m)*x*v),e=Math.sqrt(i*i+1)-i;return e<1e-9?-18:Math.log(e)}function o(t){return(Math.exp(t)-Math.exp(-t))/2}function s(t){return(Math.exp(t)+Math.exp(-t))/2}function r(t){return o(t)/s(t)}function a(t){return m*(s(w)/s(w+y*t))}function h(t){return m*(s(w)*r(w+y*t)-o(w))/x}function u(t){return 1-Math.pow(1-t,1.5)}function l(){var e=(Date.now()-P)/b,n=u(e)*L;e<=1?(this._flyToFrame=f(l,this),this._move(this.unproject(c.add(_.subtract(c).multiplyBy(h(n)/v)),p),this.getScaleZoom(m/a(n),p),{flyTo:!0})):this._move(t,i)._moveEnd(!0)}if(!1===(e=e||{}).animate||!ji)return this.setView(t,i,e);this._stop();var c=this.project(this.getCenter()),_=this.project(t),d=this.getSize(),p=this._zoom;t=C(t),i=void 0===i?p:i;var m=Math.max(d.x,d.y),g=m*this.getZoomScale(p,i),v=_.distanceTo(c)||1,y=1.42,x=y*y,w=n(0),P=Date.now(),L=(n(1)-w)/y,b=e.duration?1e3*e.duration:1e3*L*.8;return this._moveStart(!0,e.noMoveStart),l.call(this),this},flyToBounds:function(t,i){var e=this._getBoundsCenterZoom(t,i);return this.flyTo(e.center,e.zoom,i)},setMaxBounds:function(t){return(t=z(t)).isValid()?(this.options.maxBounds&&this.off("moveend",this._panInsideMaxBounds),this.options.maxBounds=t,this._loaded&&this._panInsideMaxBounds(),this.on("moveend",this._panInsideMaxBounds)):(this.options.maxBounds=null,this.off("moveend",this._panInsideMaxBounds))},setMinZoom:function(t){var i=this.options.minZoom;return this.options.minZoom=t,this._loaded&&i!==t&&(this.fire("zoomlevelschange"),this.getZoom()<this.options.minZoom)?this.setZoom(t):this},setMaxZoom:function(t){var i=this.options.maxZoom;return this.options.maxZoom=t,this._loaded&&i!==t&&(this.fire("zoomlevelschange"),this.getZoom()>this.options.maxZoom)?this.setZoom(t):this},panInsideBounds:function(t,i){this._enforcingBounds=!0;var e=this.getCenter(),n=this._limitCenter(e,this._zoom,z(t));return e.equals(n)||this.panTo(n,i),this._enforcingBounds=!1,this},invalidateSize:function(t){if(!this._loaded)return this;t=i({animate:!1,pan:!0},!0===t?{animate:!0}:t);var n=this.getSize();this._sizeChanged=!0,this._lastCenter=null;var o=this.getSize(),s=n.divideBy(2).round(),r=o.divideBy(2).round(),a=s.subtract(r);return a.x||a.y?(t.animate&&t.pan?this.panBy(a):(t.pan&&this._rawPanBy(a),this.fire("move"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(e(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:n,newSize:o})):this},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire("viewreset"),this._stop()},locate:function(t){if(t=this._locateOptions=i({timeout:1e4,watch:!1},t),!("geolocation"in navigator))return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var n=e(this._handleGeolocationResponse,this),o=e(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(n,o,t):navigator.geolocation.getCurrentPosition(n,o,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var i=t.code,e=t.message||(1===i?"permission denied":2===i?"position unavailable":"timeout");this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:i,message:"Geolocation error: "+e+"."})},_handleGeolocationResponse:function(t){var i=new M(t.coords.latitude,t.coords.longitude),e=i.toBounds(2*t.coords.accuracy),n=this._locateOptions;if(n.setView){var o=this.getBoundsZoom(e);this.setView(i,n.maxZoom?Math.min(o,n.maxZoom):o)}var s={latlng:i,bounds:e,timestamp:t.timestamp};for(var r in t.coords)"number"==typeof t.coords[r]&&(s[r]=t.coords[r]);this.fire("locationfound",s)},addHandler:function(t,i){if(!i)return this;var e=this[t]=new i(this);return this._handlers.push(e),this.options[t]&&e.enable(),this},remove:function(){if(this._initEvents(!0),this._containerId!==this._container._leaflet_id)throw new Error("Map container is being reused by another instance");try{delete this._container._leaflet_id,delete this._containerId}catch(t){this._container._leaflet_id=void 0,this._containerId=void 0}void 0!==this._locationWatchId&&this.stopLocate(),this._stop(),K(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._resizeRequest&&(g(this._resizeRequest),this._resizeRequest=null),this._clearHandlers(),this._loaded&&this.fire("unload");var t;for(t in this._layers)this._layers[t].remove();for(t in this._panes)K(this._panes[t]);return this._layers=[],this._panes=[],delete this._mapPane,delete this._renderer,this},createPane:function(t,i){var e=G("div","leaflet-pane"+(t?" leaflet-"+t.replace("Pane","")+"-pane":""),i||this._mapPane);return t&&(this._panes[t]=e),e},getCenter:function(){return this._checkIfLoaded(),this._lastCenter&&!this._moved()?this._lastCenter:this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds();return new T(this.unproject(t.getBottomLeft()),this.unproject(t.getTopRight()))},getMinZoom:function(){return void 0===this.options.minZoom?this._layersMinZoom||0:this.options.minZoom},getMaxZoom:function(){return void 0===this.options.maxZoom?void 0===this._layersMaxZoom?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,i,e){t=z(t),e=w(e||[0,0]);var n=this.getZoom()||0,o=this.getMinZoom(),s=this.getMaxZoom(),r=t.getNorthWest(),a=t.getSouthEast(),h=this.getSize().subtract(e),u=b(this.project(a,n),this.project(r,n)).getSize(),l=ji?this.options.zoomSnap:1,c=h.x/u.x,_=h.y/u.y,d=i?Math.max(c,_):Math.min(c,_);return n=this.getScaleZoom(d,n),l&&(n=Math.round(n/(l/100))*(l/100),n=i?Math.ceil(n/l)*l:Math.floor(n/l)*l),Math.max(o,Math.min(s,n))},getSize:function(){return this._size&&!this._sizeChanged||(this._size=new x(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(t,i){var e=this._getTopLeftPoint(t,i);return new P(e,e.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._pixelOrigin},getPixelWorldBounds:function(t){return this.options.crs.getProjectedBounds(void 0===t?this.getZoom():t)},getPane:function(t){return"string"==typeof t?this._panes[t]:t},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t,i){var e=this.options.crs;return i=void 0===i?this._zoom:i,e.scale(t)/e.scale(i)},getScaleZoom:function(t,i){var e=this.options.crs;i=void 0===i?this._zoom:i;var n=e.zoom(t*e.scale(i));return isNaN(n)?1/0:n},project:function(t,i){return i=void 0===i?this._zoom:i,this.options.crs.latLngToPoint(C(t),i)},unproject:function(t,i){return i=void 0===i?this._zoom:i,this.options.crs.pointToLatLng(w(t),i)},layerPointToLatLng:function(t){var i=w(t).add(this.getPixelOrigin());return this.unproject(i)},latLngToLayerPoint:function(t){return this.project(C(t))._round()._subtract(this.getPixelOrigin())},wrapLatLng:function(t){return this.options.crs.wrapLatLng(C(t))},wrapLatLngBounds:function(t){return this.options.crs.wrapLatLngBounds(z(t))},distance:function(t,i){return this.options.crs.distance(C(t),C(i))},containerPointToLayerPoint:function(t){return w(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return w(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){var i=this.containerPointToLayerPoint(w(t));return this.layerPointToLatLng(i)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(C(t)))},mouseEventToContainerPoint:function(t){return bt(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){var i=this._container=V(t);if(!i)throw new Error("Map container not found.");if(i._leaflet_id)throw new Error("Map container is already initialized.");mt(i,"scroll",this._onScroll,this),this._containerId=n(i)},_initLayout:function(){var t=this._container;this._fadeAnimated=this.options.fadeAnimation&&ji,Q(t,"leaflet-container"+(qi?" leaflet-touch":"")+(Yi?" leaflet-retina":"")+(Li?" leaflet-oldie":"")+(Ai?" leaflet-safari":"")+(this._fadeAnimated?" leaflet-fade-anim":""));var i=q(t,"position");"absolute"!==i&&"relative"!==i&&"fixed"!==i&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._paneRenderers={},this._mapPane=this.createPane("mapPane",this._container),at(this._mapPane,new x(0,0)),this.createPane("tilePane"),this.createPane("shadowPane"),this.createPane("overlayPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane"),this.options.markerZoomAnimation||(Q(t.markerPane,"leaflet-zoom-hide"),Q(t.shadowPane,"leaflet-zoom-hide"))},_resetView:function(t,i){at(this._mapPane,new x(0,0));var e=!this._loaded;this._loaded=!0,i=this._limitZoom(i),this.fire("viewprereset");var n=this._zoom!==i;this._moveStart(n,!1)._move(t,i)._moveEnd(n),this.fire("viewreset"),e&&this.fire("load")},_moveStart:function(t,i){return t&&this.fire("zoomstart"),i||this.fire("movestart"),this},_move:function(t,i,e){void 0===i&&(i=this._zoom);var n=this._zoom!==i;return this._zoom=i,this._lastCenter=t,this._pixelOrigin=this._getNewPixelOrigin(t),(n||e&&e.pinch)&&this.fire("zoom",e),this.fire("move",e)},_moveEnd:function(t){return t&&this.fire("zoomend"),this.fire("moveend")},_stop:function(){return g(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(t){at(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){this._enforcingBounds||this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(t){this._targets={},this._targets[n(this._container)]=this;var i=t?ft:mt;i(this._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress",this._handleDOMEvent,this),this.options.trackResize&&i(window,"resize",this._onResize,this),ji&&this.options.transform3DLimit&&(t?this.off:this.on).call(this,"moveend",this._onMoveEnd)},_onResize:function(){g(this._resizeRequest),this._resizeRequest=f(function(){this.invalidateSize({debounceMoveend:!0})},this)},_onScroll:function(){this._container.scrollTop=0,this._container.scrollLeft=0},_onMoveEnd:function(){var t=this._getMapPanePos();Math.max(Math.abs(t.x),Math.abs(t.y))>=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(t,i){for(var e,o=[],s="mouseout"===i||"mouseover"===i,r=t.target||t.srcElement,a=!1;r;){if((e=this._targets[n(r)])&&("click"===i||"preclick"===i)&&!t._simulated&&this._draggableMoved(e)){a=!0;break}if(e&&e.listens(i,!0)){if(s&&!Ct(r,t))break;if(o.push(e),s)break}if(r===this._container)break;r=r.parentNode}return o.length||a||s||!Ct(r,t)||(o=[this]),o},_handleDOMEvent:function(t){if(this._loaded&&!Mt(t)){var i=t.type;"mousedown"!==i&&"keypress"!==i||ct(t.target||t.srcElement),this._fireDOMEvent(t,i)}},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(t,e,n){if("click"===t.type){var o=i({},t);o.type="preclick",this._fireDOMEvent(o,o.type,n)}if(!t._stopped&&(n=(n||[]).concat(this._findEventTargets(t,e))).length){var s=n[0];"contextmenu"===e&&s.listens(e,!0)&&Pt(t);var r={originalEvent:t};if("keypress"!==t.type){var a=s.getLatLng&&(!s._radius||s._radius<=10);r.containerPoint=a?this.latLngToContainerPoint(s.getLatLng()):this.mouseEventToContainerPoint(t),r.layerPoint=this.containerPointToLayerPoint(r.containerPoint),r.latlng=a?s.getLatLng():this.layerPointToLatLng(r.layerPoint)}for(var h=0;h<n.length;h++)if(n[h].fire(e,r,!0),r.originalEvent._stopped||!1===n[h].options.bubblingMouseEvents&&-1!==d(this._mouseEvents,e))return}},_draggableMoved:function(t){return(t=t.dragging&&t.dragging.enabled()?t:this).dragging&&t.dragging.moved()||this.boxZoom&&this.boxZoom.moved()},_clearHandlers:function(){for(var t=0,i=this._handlers.length;t<i;t++)this._handlers[t].disable()},whenReady:function(t,i){return this._loaded?t.call(i||this,{target:this}):this.on("load",t,i),this},_getMapPanePos:function(){return ht(this._mapPane)||new x(0,0)},_moved:function(){var t=this._getMapPanePos();return t&&!t.equals([0,0])},_getTopLeftPoint:function(t,i){return(t&&void 0!==i?this._getNewPixelOrigin(t,i):this.getPixelOrigin()).subtract(this._getMapPanePos())},_getNewPixelOrigin:function(t,i){var e=this.getSize()._divideBy(2);return this.project(t,i)._subtract(e)._add(this._getMapPanePos())._round()},_latLngToNewLayerPoint:function(t,i,e){var n=this._getNewPixelOrigin(e,i);return this.project(t,i)._subtract(n)},_latLngBoundsToNewLayerBounds:function(t,i,e){var n=this._getNewPixelOrigin(e,i);return b([this.project(t.getSouthWest(),i)._subtract(n),this.project(t.getNorthWest(),i)._subtract(n),this.project(t.getSouthEast(),i)._subtract(n),this.project(t.getNorthEast(),i)._subtract(n)])},_getCenterLayerPoint:function(){return this.containerPointToLayerPoint(this.getSize()._divideBy(2))},_getCenterOffset:function(t){return this.latLngToLayerPoint(t).subtract(this._getCenterLayerPoint())},_limitCenter:function(t,i,e){if(!e)return t;var n=this.project(t,i),o=this.getSize().divideBy(2),s=new P(n.subtract(o),n.add(o)),r=this._getBoundsOffset(s,e,i);return r.round().equals([0,0])?t:this.unproject(n.add(r),i)},_limitOffset:function(t,i){if(!i)return t;var e=this.getPixelBounds(),n=new P(e.min.add(t),e.max.add(t));return t.add(this._getBoundsOffset(n,i))},_getBoundsOffset:function(t,i,e){var n=b(this.project(i.getNorthEast(),e),this.project(i.getSouthWest(),e)),o=n.min.subtract(t.min),s=n.max.subtract(t.max);return new x(this._rebound(o.x,-s.x),this._rebound(o.y,-s.y))},_rebound:function(t,i){return t+i>0?Math.round(t-i)/2:Math.max(0,Math.ceil(t))-Math.max(0,Math.floor(i))},_limitZoom:function(t){var i=this.getMinZoom(),e=this.getMaxZoom(),n=ji?this.options.zoomSnap:1;return n&&(t=Math.round(t/n)*n),Math.max(i,Math.min(e,t))},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){tt(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_tryAnimatedPan:function(t,i){var e=this._getCenterOffset(t)._trunc();return!(!0!==(i&&i.animate)&&!this.getSize().contains(e))&&(this.panBy(e,i),!0)},_createAnimProxy:function(){var t=this._proxy=G("div","leaflet-proxy leaflet-zoom-animated");this._panes.mapPane.appendChild(t),this.on("zoomanim",function(t){var i=ce,e=this._proxy.style[i];rt(this._proxy,this.project(t.center,t.zoom),this.getZoomScale(t.zoom,1)),e===this._proxy.style[i]&&this._animatingZoom&&this._onZoomTransitionEnd()},this),this.on("load moveend",function(){var t=this.getCenter(),i=this.getZoom();rt(this._proxy,this.project(t,i),this.getZoomScale(i,1))},this),this._on("unload",this._destroyAnimProxy,this)},_destroyAnimProxy:function(){K(this._proxy),delete this._proxy},_catchTransitionEnd:function(t){this._animatingZoom&&t.propertyName.indexOf("transform")>=0&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(t,i,e){if(this._animatingZoom)return!0;if(e=e||{},!this._zoomAnimated||!1===e.animate||this._nothingToAnimate()||Math.abs(i-this._zoom)>this.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(i),o=this._getCenterOffset(t)._divideBy(1-1/n);return!(!0!==e.animate&&!this.getSize().contains(o))&&(f(function(){this._moveStart(!0,!1)._animateZoom(t,i,!0)},this),!0)},_animateZoom:function(t,i,n,o){this._mapPane&&(n&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=i,Q(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:i,noUpdate:o}),setTimeout(e(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&tt(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom),f(function(){this._moveEnd(!0)},this))}}),Te=v.extend({options:{position:"topright"},initialize:function(t){l(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var i=this._map;return i&&i.removeControl(this),this.options.position=t,i&&i.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var i=this._container=this.onAdd(t),e=this.getPosition(),n=t._controlCorners[e];return Q(i,"leaflet-control"),-1!==e.indexOf("bottom")?n.insertBefore(i,n.firstChild):n.appendChild(i),this},remove:function(){return this._map?(K(this._container),this.onRemove&&this.onRemove(this._map),this._map=null,this):this},_refocusOnMap:function(t){this._map&&t&&t.screenX>0&&t.screenY>0&&this._map.getContainer().focus()}}),ze=function(t){return new Te(t)};be.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.remove(),this},_initControlPos:function(){function t(t,o){var s=e+t+" "+e+o;i[t+o]=G("div",s,n)}var i=this._controlCorners={},e="leaflet-",n=this._controlContainer=G("div",e+"control-container",this._container);t("top","left"),t("top","right"),t("bottom","left"),t("bottom","right")},_clearControlPos:function(){for(var t in this._controlCorners)K(this._controlCorners[t]);K(this._controlContainer),delete this._controlCorners,delete this._controlContainer}});var Me=Te.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0,hideSingleBase:!1,sortLayers:!1,sortFunction:function(t,i,e,n){return e<n?-1:n<e?1:0}},initialize:function(t,i,e){l(this,e),this._layerControlInputs=[],this._layers=[],this._lastZIndex=0,this._handlingClick=!1;for(var n in t)this._addLayer(t[n],n);for(n in i)this._addLayer(i[n],n,!0)},onAdd:function(t){this._initLayout(),this._update(),this._map=t,t.on("zoomend",this._checkDisabledLayers,this);for(var i=0;i<this._layers.length;i++)this._layers[i].layer.on("add remove",this._onLayerChange,this);return this._container},addTo:function(t){return Te.prototype.addTo.call(this,t),this._expandIfNotCollapsed()},onRemove:function(){this._map.off("zoomend",this._checkDisabledLayers,this);for(var t=0;t<this._layers.length;t++)this._layers[t].layer.off("add remove",this._onLayerChange,this)},addBaseLayer:function(t,i){return this._addLayer(t,i),this._map?this._update():this},addOverlay:function(t,i){return this._addLayer(t,i,!0),this._map?this._update():this},removeLayer:function(t){t.off("add remove",this._onLayerChange,this);var i=this._getLayer(n(t));return i&&this._layers.splice(this._layers.indexOf(i),1),this._map?this._update():this},expand:function(){Q(this._container,"leaflet-control-layers-expanded"),this._form.style.height=null;var t=this._map.getSize().y-(this._container.offsetTop+50);return t<this._form.clientHeight?(Q(this._form,"leaflet-control-layers-scrollbar"),this._form.style.height=t+"px"):tt(this._form,"leaflet-control-layers-scrollbar"),this._checkDisabledLayers(),this},collapse:function(){return tt(this._container,"leaflet-control-layers-expanded"),this},_initLayout:function(){var t="leaflet-control-layers",i=this._container=G("div",t),e=this.options.collapsed;i.setAttribute("aria-haspopup",!0),wt(i),xt(i);var n=this._form=G("form",t+"-list");e&&(this._map.on("click",this.collapse,this),zi||mt(i,{mouseenter:this.expand,mouseleave:this.collapse},this));var o=this._layersLink=G("a",t+"-toggle",i);o.href="#",o.title="Layers",qi?(mt(o,"click",Lt),mt(o,"click",this.expand,this)):mt(o,"focus",this.expand,this),e||this.expand(),this._baseLayersList=G("div",t+"-base",n),this._separator=G("div",t+"-separator",n),this._overlaysList=G("div",t+"-overlays",n),i.appendChild(n)},_getLayer:function(t){for(var i=0;i<this._layers.length;i++)if(this._layers[i]&&n(this._layers[i].layer)===t)return this._layers[i]},_addLayer:function(t,i,n){this._map&&t.on("add remove",this._onLayerChange,this),this._layers.push({layer:t,name:i,overlay:n}),this.options.sortLayers&&this._layers.sort(e(function(t,i){return this.options.sortFunction(t.layer,i.layer,t.name,i.name)},this)),this.options.autoZIndex&&t.setZIndex&&(this._lastZIndex++,t.setZIndex(this._lastZIndex)),this._expandIfNotCollapsed()},_update:function(){if(!this._container)return this;Y(this._baseLayersList),Y(this._overlaysList),this._layerControlInputs=[];var t,i,e,n,o=0;for(e=0;e<this._layers.length;e++)n=this._layers[e],this._addItem(n),i=i||n.overlay,t=t||!n.overlay,o+=n.overlay?0:1;return this.options.hideSingleBase&&(t=t&&o>1,this._baseLayersList.style.display=t?"":"none"),this._separator.style.display=i&&t?"":"none",this},_onLayerChange:function(t){this._handlingClick||this._update();var i=this._getLayer(n(t.target)),e=i.overlay?"add"===t.type?"overlayadd":"overlayremove":"add"===t.type?"baselayerchange":null;e&&this._map.fire(e,i)},_createRadioElement:function(t,i){var e='<input type="radio" class="leaflet-control-layers-selector" name="'+t+'"'+(i?' checked="checked"':"")+"/>",n=document.createElement("div");return n.innerHTML=e,n.firstChild},_addItem:function(t){var i,e=document.createElement("label"),o=this._map.hasLayer(t.layer);t.overlay?((i=document.createElement("input")).type="checkbox",i.className="leaflet-control-layers-selector",i.defaultChecked=o):i=this._createRadioElement("leaflet-base-layers",o),this._layerControlInputs.push(i),i.layerId=n(t.layer),mt(i,"click",this._onInputClick,this);var s=document.createElement("span");s.innerHTML=" "+t.name;var r=document.createElement("div");return e.appendChild(r),r.appendChild(i),r.appendChild(s),(t.overlay?this._overlaysList:this._baseLayersList).appendChild(e),this._checkDisabledLayers(),e},_onInputClick:function(){var t,i,e=this._layerControlInputs,n=[],o=[];this._handlingClick=!0;for(var s=e.length-1;s>=0;s--)t=e[s],i=this._getLayer(t.layerId).layer,t.checked?n.push(i):t.checked||o.push(i);for(s=0;s<o.length;s++)this._map.hasLayer(o[s])&&this._map.removeLayer(o[s]);for(s=0;s<n.length;s++)this._map.hasLayer(n[s])||this._map.addLayer(n[s]);this._handlingClick=!1,this._refocusOnMap()},_checkDisabledLayers:function(){for(var t,i,e=this._layerControlInputs,n=this._map.getZoom(),o=e.length-1;o>=0;o--)t=e[o],i=this._getLayer(t.layerId).layer,t.disabled=void 0!==i.options.minZoom&&n<i.options.minZoom||void 0!==i.options.maxZoom&&n>i.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expand:function(){return this.expand()},_collapse:function(){return this.collapse()}}),Ce=Te.extend({options:{position:"topleft",zoomInText:"+",zoomInTitle:"Zoom in",zoomOutText:"&#x2212;",zoomOutTitle:"Zoom out"},onAdd:function(t){var i="leaflet-control-zoom",e=G("div",i+" leaflet-bar"),n=this.options;return this._zoomInButton=this._createButton(n.zoomInText,n.zoomInTitle,i+"-in",e,this._zoomIn),this._zoomOutButton=this._createButton(n.zoomOutText,n.zoomOutTitle,i+"-out",e,this._zoomOut),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),e},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(t){!this._disabled&&this._map._zoom<this._map.getMaxZoom()&&this._map.zoomIn(this._map.options.zoomDelta*(t.shiftKey?3:1))},_zoomOut:function(t){!this._disabled&&this._map._zoom>this._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(t.shiftKey?3:1))},_createButton:function(t,i,e,n,o){var s=G("a",e,n);return s.innerHTML=t,s.href="#",s.title=i,s.setAttribute("role","button"),s.setAttribute("aria-label",i),wt(s),mt(s,"click",Lt),mt(s,"click",o,this),mt(s,"click",this._refocusOnMap,this),s},_updateDisabled:function(){var t=this._map,i="leaflet-disabled";tt(this._zoomInButton,i),tt(this._zoomOutButton,i),(this._disabled||t._zoom===t.getMinZoom())&&Q(this._zoomOutButton,i),(this._disabled||t._zoom===t.getMaxZoom())&&Q(this._zoomInButton,i)}});be.mergeOptions({zoomControl:!0}),be.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new Ce,this.addControl(this.zoomControl))});var Se=Te.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var i=G("div","leaflet-control-scale"),e=this.options;return this._addScales(e,"leaflet-control-scale-line",i),t.on(e.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,i,e){t.metric&&(this._mScale=G("div",i,e)),t.imperial&&(this._iScale=G("div",i,e))},_update:function(){var t=this._map,i=t.getSize().y/2,e=t.distance(t.containerPointToLatLng([0,i]),t.containerPointToLatLng([this.options.maxWidth,i]));this._updateScales(e)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var i=this._getRoundNum(t),e=i<1e3?i+" m":i/1e3+" km";this._updateScale(this._mScale,e,i/t)},_updateImperial:function(t){var i,e,n,o=3.2808399*t;o>5280?(i=o/5280,e=this._getRoundNum(i),this._updateScale(this._iScale,e+" mi",e/i)):(n=this._getRoundNum(o),this._updateScale(this._iScale,n+" ft",n/o))},_updateScale:function(t,i,e){t.style.width=Math.round(this.options.maxWidth*e)+"px",t.innerHTML=i},_getRoundNum:function(t){var i=Math.pow(10,(Math.floor(t)+"").length-1),e=t/i;return e=e>=10?10:e>=5?5:e>=3?3:e>=2?2:1,i*e}}),Ze=Te.extend({options:{position:"bottomright",prefix:'<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'},initialize:function(t){l(this,t),this._attributions={}},onAdd:function(t){t.attributionControl=this,this._container=G("div","leaflet-control-attribution"),wt(this._container);for(var i in t._layers)t._layers[i].getAttribution&&this.addAttribution(t._layers[i].getAttribution());return this._update(),this._container},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t?(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update(),this):this},removeAttribution:function(t){return t?(this._attributions[t]&&(this._attributions[t]--,this._update()),this):this},_update:function(){if(this._map){var t=[];for(var i in this._attributions)this._attributions[i]&&t.push(i);var e=[];this.options.prefix&&e.push(this.options.prefix),t.length&&e.push(t.join(", ")),this._container.innerHTML=e.join(" | ")}}});be.mergeOptions({attributionControl:!0}),be.addInitHook(function(){this.options.attributionControl&&(new Ze).addTo(this)});Te.Layers=Me,Te.Zoom=Ce,Te.Scale=Se,Te.Attribution=Ze,ze.layers=function(t,i,e){return new Me(t,i,e)},ze.zoom=function(t){return new Ce(t)},ze.scale=function(t){return new Se(t)},ze.attribution=function(t){return new Ze(t)};var Ee=v.extend({initialize:function(t){this._map=t},enable:function(){return this._enabled?this:(this._enabled=!0,this.addHooks(),this)},disable:function(){return this._enabled?(this._enabled=!1,this.removeHooks(),this):this},enabled:function(){return!!this._enabled}});Ee.addTo=function(t,i){return t.addHandler(i,this),this};var ke,Ae={Events:li},Be=qi?"touchstart mousedown":"mousedown",Ie={mousedown:"mouseup",touchstart:"touchend",pointerdown:"touchend",MSPointerDown:"touchend"},Oe={mousedown:"mousemove",touchstart:"touchmove",pointerdown:"touchmove",MSPointerDown:"touchmove"},Re=ci.extend({options:{clickTolerance:3},initialize:function(t,i,e,n){l(this,n),this._element=t,this._dragStartTarget=i||t,this._preventOutline=e},enable:function(){this._enabled||(mt(this._dragStartTarget,Be,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(Re._dragging===this&&this.finishDrag(),ft(this._dragStartTarget,Be,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){if(!t._simulated&&this._enabled&&(this._moved=!1,!$(this._element,"leaflet-zoom-anim")&&!(Re._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||(Re._dragging=this,this._preventOutline&&ct(this._element),ut(),fi(),this._moving)))){this.fire("down");var i=t.touches?t.touches[0]:t,e=dt(this._element);this._startPoint=new x(i.clientX,i.clientY),this._parentScale=pt(e),mt(document,Oe[t.type],this._onMove,this),mt(document,Ie[t.type],this._onUp,this)}},_onMove:function(t){if(!t._simulated&&this._enabled)if(t.touches&&t.touches.length>1)this._moved=!0;else{var i=t.touches&&1===t.touches.length?t.touches[0]:t,e=new x(i.clientX,i.clientY)._subtract(this._startPoint);(e.x||e.y)&&(Math.abs(e.x)+Math.abs(e.y)<this.options.clickTolerance||(e.x/=this._parentScale.x,e.y/=this._parentScale.y,Pt(t),this._moved||(this.fire("dragstart"),this._moved=!0,this._startPos=ht(this._element).subtract(e),Q(document.body,"leaflet-dragging"),this._lastTarget=t.target||t.srcElement,window.SVGElementInstance&&this._lastTarget instanceof SVGElementInstance&&(this._lastTarget=this._lastTarget.correspondingUseElement),Q(this._lastTarget,"leaflet-drag-target")),this._newPos=this._startPos.add(e),this._moving=!0,g(this._animRequest),this._lastEvent=t,this._animRequest=f(this._updatePosition,this,!0)))}},_updatePosition:function(){var t={originalEvent:this._lastEvent};this.fire("predrag",t),at(this._element,this._newPos),this.fire("drag",t)},_onUp:function(t){!t._simulated&&this._enabled&&this.finishDrag()},finishDrag:function(){tt(document.body,"leaflet-dragging"),this._lastTarget&&(tt(this._lastTarget,"leaflet-drag-target"),this._lastTarget=null);for(var t in Oe)ft(document,Oe[t],this._onMove,this),ft(document,Ie[t],this._onUp,this);lt(),gi(),this._moved&&this._moving&&(g(this._animRequest),this.fire("dragend",{distance:this._newPos.distanceTo(this._startPos)})),this._moving=!1,Re._dragging=!1}}),Ne=(Object.freeze||Object)({simplify:Zt,pointToSegmentDistance:Et,closestPointOnSegment:function(t,i,e){return Dt(t,i,e)},clipSegment:It,_getEdgeIntersection:Ot,_getBitCode:Rt,_sqClosestPointOnSegment:Dt,isFlat:jt,_flat:Wt}),De=(Object.freeze||Object)({clipPolygon:Ht}),je={project:function(t){return new x(t.lng,t.lat)},unproject:function(t){return new M(t.y,t.x)},bounds:new P([-180,-90],[180,90])},We={R:6378137,R_MINOR:6356752.314245179,bounds:new P([-20037508.34279,-15496570.73972],[20037508.34279,18764656.23138]),project:function(t){var i=Math.PI/180,e=this.R,n=t.lat*i,o=this.R_MINOR/e,s=Math.sqrt(1-o*o),r=s*Math.sin(n),a=Math.tan(Math.PI/4-n/2)/Math.pow((1-r)/(1+r),s/2);return n=-e*Math.log(Math.max(a,1e-10)),new x(t.lng*i*e,n)},unproject:function(t){for(var i,e=180/Math.PI,n=this.R,o=this.R_MINOR/n,s=Math.sqrt(1-o*o),r=Math.exp(-t.y/n),a=Math.PI/2-2*Math.atan(r),h=0,u=.1;h<15&&Math.abs(u)>1e-7;h++)i=s*Math.sin(a),i=Math.pow((1-i)/(1+i),s/2),a+=u=Math.PI/2-2*Math.atan(r*i)-a;return new M(a*e,t.x*e/n)}},He=(Object.freeze||Object)({LonLat:je,Mercator:We,SphericalMercator:mi}),Fe=i({},pi,{code:"EPSG:3395",projection:We,transformation:function(){var t=.5/(Math.PI*We.R);return Z(t,.5,-t,.5)}()}),Ue=i({},pi,{code:"EPSG:4326",projection:je,transformation:Z(1/180,1,-1/180,.5)}),Ve=i({},di,{projection:je,transformation:Z(1,0,-1,0),scale:function(t){return Math.pow(2,t)},zoom:function(t){return Math.log(t)/Math.LN2},distance:function(t,i){var e=i.lng-t.lng,n=i.lat-t.lat;return Math.sqrt(e*e+n*n)},infinite:!0});di.Earth=pi,di.EPSG3395=Fe,di.EPSG3857=yi,di.EPSG900913=xi,di.EPSG4326=Ue,di.Simple=Ve;var qe=ci.extend({options:{pane:"overlayPane",attribution:null,bubblingMouseEvents:!0},addTo:function(t){return t.addLayer(this),this},remove:function(){return this.removeFrom(this._map||this._mapToAdd)},removeFrom:function(t){return t&&t.removeLayer(this),this},getPane:function(t){return this._map.getPane(t?this.options[t]||t:this.options.pane)},addInteractiveTarget:function(t){return this._map._targets[n(t)]=this,this},removeInteractiveTarget:function(t){return delete this._map._targets[n(t)],this},getAttribution:function(){return this.options.attribution},_layerAdd:function(t){var i=t.target;if(i.hasLayer(this)){if(this._map=i,this._zoomAnimated=i._zoomAnimated,this.getEvents){var e=this.getEvents();i.on(e,this),this.once("remove",function(){i.off(e,this)},this)}this.onAdd(i),this.getAttribution&&i.attributionControl&&i.attributionControl.addAttribution(this.getAttribution()),this.fire("add"),i.fire("layeradd",{layer:this})}}});be.include({addLayer:function(t){if(!t._layerAdd)throw new Error("The provided object is not a Layer.");var i=n(t);return this._layers[i]?this:(this._layers[i]=t,t._mapToAdd=this,t.beforeAdd&&t.beforeAdd(this),this.whenReady(t._layerAdd,t),this)},removeLayer:function(t){var i=n(t);return this._layers[i]?(this._loaded&&t.onRemove(this),t.getAttribution&&this.attributionControl&&this.attributionControl.removeAttribution(t.getAttribution()),delete this._layers[i],this._loaded&&(this.fire("layerremove",{layer:t}),t.fire("remove")),t._map=t._mapToAdd=null,this):this},hasLayer:function(t){return!!t&&n(t)in this._layers},eachLayer:function(t,i){for(var e in this._layers)t.call(i,this._layers[e]);return this},_addLayers:function(t){for(var i=0,e=(t=t?oi(t)?t:[t]:[]).length;i<e;i++)this.addLayer(t[i])},_addZoomLimit:function(t){!isNaN(t.options.maxZoom)&&isNaN(t.options.minZoom)||(this._zoomBoundLayers[n(t)]=t,this._updateZoomLevels())},_removeZoomLimit:function(t){var i=n(t);this._zoomBoundLayers[i]&&(delete this._zoomBoundLayers[i],this._updateZoomLevels())},_updateZoomLevels:function(){var t=1/0,i=-1/0,e=this._getZoomSpan();for(var n in this._zoomBoundLayers){var o=this._zoomBoundLayers[n].options;t=void 0===o.minZoom?t:Math.min(t,o.minZoom),i=void 0===o.maxZoom?i:Math.max(i,o.maxZoom)}this._layersMaxZoom=i===-1/0?void 0:i,this._layersMinZoom=t===1/0?void 0:t,e!==this._getZoomSpan()&&this.fire("zoomlevelschange"),void 0===this.options.maxZoom&&this._layersMaxZoom&&this.getZoom()>this._layersMaxZoom&&this.setZoom(this._layersMaxZoom),void 0===this.options.minZoom&&this._layersMinZoom&&this.getZoom()<this._layersMinZoom&&this.setZoom(this._layersMinZoom)}});var Ge=qe.extend({initialize:function(t,i){l(this,i),this._layers={};var e,n;if(t)for(e=0,n=t.length;e<n;e++)this.addLayer(t[e])},addLayer:function(t){var i=this.getLayerId(t);return this._layers[i]=t,this._map&&this._map.addLayer(t),this},removeLayer:function(t){var i=t in this._layers?t:this.getLayerId(t);return this._map&&this._layers[i]&&this._map.removeLayer(this._layers[i]),delete this._layers[i],this},hasLayer:function(t){return!!t&&(t in this._layers||this.getLayerId(t)in this._layers)},clearLayers:function(){return this.eachLayer(this.removeLayer,this)},invoke:function(t){var i,e,n=Array.prototype.slice.call(arguments,1);for(i in this._layers)(e=this._layers[i])[t]&&e[t].apply(e,n);return this},onAdd:function(t){this.eachLayer(t.addLayer,t)},onRemove:function(t){this.eachLayer(t.removeLayer,t)},eachLayer:function(t,i){for(var e in this._layers)t.call(i,this._layers[e]);return this},getLayer:function(t){return this._layers[t]},getLayers:function(){var t=[];return this.eachLayer(t.push,t),t},setZIndex:function(t){return this.invoke("setZIndex",t)},getLayerId:function(t){return n(t)}}),Ke=Ge.extend({addLayer:function(t){return this.hasLayer(t)?this:(t.addEventParent(this),Ge.prototype.addLayer.call(this,t),this.fire("layeradd",{layer:t}))},removeLayer:function(t){return this.hasLayer(t)?(t in this._layers&&(t=this._layers[t]),t.removeEventParent(this),Ge.prototype.removeLayer.call(this,t),this.fire("layerremove",{layer:t})):this},setStyle:function(t){return this.invoke("setStyle",t)},bringToFront:function(){return this.invoke("bringToFront")},bringToBack:function(){return this.invoke("bringToBack")},getBounds:function(){var t=new T;for(var i in this._layers){var e=this._layers[i];t.extend(e.getBounds?e.getBounds():e.getLatLng())}return t}}),Ye=v.extend({options:{popupAnchor:[0,0],tooltipAnchor:[0,0]},initialize:function(t){l(this,t)},createIcon:function(t){return this._createIcon("icon",t)},createShadow:function(t){return this._createIcon("shadow",t)},_createIcon:function(t,i){var e=this._getIconUrl(t);if(!e){if("icon"===t)throw new Error("iconUrl not set in Icon options (see the docs).");return null}var n=this._createImg(e,i&&"IMG"===i.tagName?i:null);return this._setIconStyles(n,t),n},_setIconStyles:function(t,i){var e=this.options,n=e[i+"Size"];"number"==typeof n&&(n=[n,n]);var o=w(n),s=w("shadow"===i&&e.shadowAnchor||e.iconAnchor||o&&o.divideBy(2,!0));t.className="leaflet-marker-"+i+" "+(e.className||""),s&&(t.style.marginLeft=-s.x+"px",t.style.marginTop=-s.y+"px"),o&&(t.style.width=o.x+"px",t.style.height=o.y+"px")},_createImg:function(t,i){return i=i||document.createElement("img"),i.src=t,i},_getIconUrl:function(t){return Yi&&this.options[t+"RetinaUrl"]||this.options[t+"Url"]}}),Xe=Ye.extend({options:{iconUrl:"marker-icon.png",iconRetinaUrl:"marker-icon-2x.png",shadowUrl:"marker-shadow.png",iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],tooltipAnchor:[16,-28],shadowSize:[41,41]},_getIconUrl:function(t){return Xe.imagePath||(Xe.imagePath=this._detectIconPath()),(this.options.imagePath||Xe.imagePath)+Ye.prototype._getIconUrl.call(this,t)},_detectIconPath:function(){var t=G("div","leaflet-default-icon-path",document.body),i=q(t,"background-image")||q(t,"backgroundImage");return document.body.removeChild(t),i=null===i||0!==i.indexOf("url")?"":i.replace(/^url\(["']?/,"").replace(/marker-icon\.png["']?\)$/,"")}}),Je=Ee.extend({initialize:function(t){this._marker=t},addHooks:function(){var t=this._marker._icon;this._draggable||(this._draggable=new Re(t,t,!0)),this._draggable.on({dragstart:this._onDragStart,predrag:this._onPreDrag,drag:this._onDrag,dragend:this._onDragEnd},this).enable(),Q(t,"leaflet-marker-draggable")},removeHooks:function(){this._draggable.off({dragstart:this._onDragStart,predrag:this._onPreDrag,drag:this._onDrag,dragend:this._onDragEnd},this).disable(),this._marker._icon&&tt(this._marker._icon,"leaflet-marker-draggable")},moved:function(){return this._draggable&&this._draggable._moved},_adjustPan:function(t){var i=this._marker,e=i._map,n=this._marker.options.autoPanSpeed,o=this._marker.options.autoPanPadding,s=ht(i._icon),r=e.getPixelBounds(),a=e.getPixelOrigin(),h=b(r.min._subtract(a).add(o),r.max._subtract(a).subtract(o));if(!h.contains(s)){var u=w((Math.max(h.max.x,s.x)-h.max.x)/(r.max.x-h.max.x)-(Math.min(h.min.x,s.x)-h.min.x)/(r.min.x-h.min.x),(Math.max(h.max.y,s.y)-h.max.y)/(r.max.y-h.max.y)-(Math.min(h.min.y,s.y)-h.min.y)/(r.min.y-h.min.y)).multiplyBy(n);e.panBy(u,{animate:!1}),this._draggable._newPos._add(u),this._draggable._startPos._add(u),at(i._icon,this._draggable._newPos),this._onDrag(t),this._panRequest=f(this._adjustPan.bind(this,t))}},_onDragStart:function(){this._oldLatLng=this._marker.getLatLng(),this._marker.closePopup().fire("movestart").fire("dragstart")},_onPreDrag:function(t){this._marker.options.autoPan&&(g(this._panRequest),this._panRequest=f(this._adjustPan.bind(this,t)))},_onDrag:function(t){var i=this._marker,e=i._shadow,n=ht(i._icon),o=i._map.layerPointToLatLng(n);e&&at(e,n),i._latlng=o,t.latlng=o,t.oldLatLng=this._oldLatLng,i.fire("move",t).fire("drag",t)},_onDragEnd:function(t){g(this._panRequest),delete this._oldLatLng,this._marker.fire("moveend").fire("dragend",t)}}),$e=qe.extend({options:{icon:new Xe,interactive:!0,keyboard:!0,title:"",alt:"",zIndexOffset:0,opacity:1,riseOnHover:!1,riseOffset:250,pane:"markerPane",bubblingMouseEvents:!1,draggable:!1,autoPan:!1,autoPanPadding:[50,50],autoPanSpeed:10},initialize:function(t,i){l(this,i),this._latlng=C(t)},onAdd:function(t){this._zoomAnimated=this._zoomAnimated&&t.options.markerZoomAnimation,this._zoomAnimated&&t.on("zoomanim",this._animateZoom,this),this._initIcon(),this.update()},onRemove:function(t){this.dragging&&this.dragging.enabled()&&(this.options.draggable=!0,this.dragging.removeHooks()),delete this.dragging,this._zoomAnimated&&t.off("zoomanim",this._animateZoom,this),this._removeIcon(),this._removeShadow()},getEvents:function(){return{zoom:this.update,viewreset:this.update}},getLatLng:function(){return this._latlng},setLatLng:function(t){var i=this._latlng;return this._latlng=C(t),this.update(),this.fire("move",{oldLatLng:i,latlng:this._latlng})},setZIndexOffset:function(t){return this.options.zIndexOffset=t,this.update()},setIcon:function(t){return this.options.icon=t,this._map&&(this._initIcon(),this.update()),this._popup&&this.bindPopup(this._popup,this._popup.options),this},getElement:function(){return this._icon},update:function(){if(this._icon&&this._map){var t=this._map.latLngToLayerPoint(this._latlng).round();this._setPos(t)}return this},_initIcon:function(){var t=this.options,i="leaflet-zoom-"+(this._zoomAnimated?"animated":"hide"),e=t.icon.createIcon(this._icon),n=!1;e!==this._icon&&(this._icon&&this._removeIcon(),n=!0,t.title&&(e.title=t.title),"IMG"===e.tagName&&(e.alt=t.alt||"")),Q(e,i),t.keyboard&&(e.tabIndex="0"),this._icon=e,t.riseOnHover&&this.on({mouseover:this._bringToFront,mouseout:this._resetZIndex});var o=t.icon.createShadow(this._shadow),s=!1;o!==this._shadow&&(this._removeShadow(),s=!0),o&&(Q(o,i),o.alt=""),this._shadow=o,t.opacity<1&&this._updateOpacity(),n&&this.getPane().appendChild(this._icon),this._initInteraction(),o&&s&&this.getPane("shadowPane").appendChild(this._shadow)},_removeIcon:function(){this.options.riseOnHover&&this.off({mouseover:this._bringToFront,mouseout:this._resetZIndex}),K(this._icon),this.removeInteractiveTarget(this._icon),this._icon=null},_removeShadow:function(){this._shadow&&K(this._shadow),this._shadow=null},_setPos:function(t){at(this._icon,t),this._shadow&&at(this._shadow,t),this._zIndex=t.y+this.options.zIndexOffset,this._resetZIndex()},_updateZIndex:function(t){this._icon.style.zIndex=this._zIndex+t},_animateZoom:function(t){var i=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center).round();this._setPos(i)},_initInteraction:function(){if(this.options.interactive&&(Q(this._icon,"leaflet-interactive"),this.addInteractiveTarget(this._icon),Je)){var t=this.options.draggable;this.dragging&&(t=this.dragging.enabled(),this.dragging.disable()),this.dragging=new Je(this),t&&this.dragging.enable()}},setOpacity:function(t){return this.options.opacity=t,this._map&&this._updateOpacity(),this},_updateOpacity:function(){var t=this.options.opacity;nt(this._icon,t),this._shadow&&nt(this._shadow,t)},_bringToFront:function(){this._updateZIndex(this.options.riseOffset)},_resetZIndex:function(){this._updateZIndex(0)},_getPopupAnchor:function(){return this.options.icon.options.popupAnchor},_getTooltipAnchor:function(){return this.options.icon.options.tooltipAnchor}}),Qe=qe.extend({options:{stroke:!0,color:"#3388ff",weight:3,opacity:1,lineCap:"round",lineJoin:"round",dashArray:null,dashOffset:null,fill:!1,fillColor:null,fillOpacity:.2,fillRule:"evenodd",interactive:!0,bubblingMouseEvents:!0},beforeAdd:function(t){this._renderer=t.getRenderer(this)},onAdd:function(){this._renderer._initPath(this),this._reset(),this._renderer._addPath(this)},onRemove:function(){this._renderer._removePath(this)},redraw:function(){return this._map&&this._renderer._updatePath(this),this},setStyle:function(t){return l(this,t),this._renderer&&this._renderer._updateStyle(this),this},bringToFront:function(){return this._renderer&&this._renderer._bringToFront(this),this},bringToBack:function(){return this._renderer&&this._renderer._bringToBack(this),this},getElement:function(){return this._path},_reset:function(){this._project(),this._update()},_clickTolerance:function(){return(this.options.stroke?this.options.weight/2:0)+this._renderer.options.tolerance}}),tn=Qe.extend({options:{fill:!0,radius:10},initialize:function(t,i){l(this,i),this._latlng=C(t),this._radius=this.options.radius},setLatLng:function(t){return this._latlng=C(t),this.redraw(),this.fire("move",{latlng:this._latlng})},getLatLng:function(){return this._latlng},setRadius:function(t){return this.options.radius=this._radius=t,this.redraw()},getRadius:function(){return this._radius},setStyle:function(t){var i=t&&t.radius||this._radius;return Qe.prototype.setStyle.call(this,t),this.setRadius(i),this},_project:function(){this._point=this._map.latLngToLayerPoint(this._latlng),this._updateBounds()},_updateBounds:function(){var t=this._radius,i=this._radiusY||t,e=this._clickTolerance(),n=[t+e,i+e];this._pxBounds=new P(this._point.subtract(n),this._point.add(n))},_update:function(){this._map&&this._updatePath()},_updatePath:function(){this._renderer._updateCircle(this)},_empty:function(){return this._radius&&!this._renderer._bounds.intersects(this._pxBounds)},_containsPoint:function(t){return t.distanceTo(this._point)<=this._radius+this._clickTolerance()}}),en=tn.extend({initialize:function(t,e,n){if("number"==typeof e&&(e=i({},n,{radius:e})),l(this,e),this._latlng=C(t),isNaN(this.options.radius))throw new Error("Circle radius cannot be NaN");this._mRadius=this.options.radius},setRadius:function(t){return this._mRadius=t,this.redraw()},getRadius:function(){return this._mRadius},getBounds:function(){var t=[this._radius,this._radiusY||this._radius];return new T(this._map.layerPointToLatLng(this._point.subtract(t)),this._map.layerPointToLatLng(this._point.add(t)))},setStyle:Qe.prototype.setStyle,_project:function(){var t=this._latlng.lng,i=this._latlng.lat,e=this._map,n=e.options.crs;if(n.distance===pi.distance){var o=Math.PI/180,s=this._mRadius/pi.R/o,r=e.project([i+s,t]),a=e.project([i-s,t]),h=r.add(a).divideBy(2),u=e.unproject(h).lat,l=Math.acos((Math.cos(s*o)-Math.sin(i*o)*Math.sin(u*o))/(Math.cos(i*o)*Math.cos(u*o)))/o;(isNaN(l)||0===l)&&(l=s/Math.cos(Math.PI/180*i)),this._point=h.subtract(e.getPixelOrigin()),this._radius=isNaN(l)?0:h.x-e.project([u,t-l]).x,this._radiusY=h.y-r.y}else{var c=n.unproject(n.project(this._latlng).subtract([this._mRadius,0]));this._point=e.latLngToLayerPoint(this._latlng),this._radius=this._point.x-e.latLngToLayerPoint(c).x}this._updateBounds()}}),nn=Qe.extend({options:{smoothFactor:1,noClip:!1},initialize:function(t,i){l(this,i),this._setLatLngs(t)},getLatLngs:function(){return this._latlngs},setLatLngs:function(t){return this._setLatLngs(t),this.redraw()},isEmpty:function(){return!this._latlngs.length},closestLayerPoint:function(t){for(var i,e,n=1/0,o=null,s=Dt,r=0,a=this._parts.length;r<a;r++)for(var h=this._parts[r],u=1,l=h.length;u<l;u++){var c=s(t,i=h[u-1],e=h[u],!0);c<n&&(n=c,o=s(t,i,e))}return o&&(o.distance=Math.sqrt(n)),o},getCenter:function(){if(!this._map)throw new Error("Must add layer to map before using getCenter()");var t,i,e,n,o,s,r,a=this._rings[0],h=a.length;if(!h)return null;for(t=0,i=0;t<h-1;t++)i+=a[t].distanceTo(a[t+1])/2;if(0===i)return this._map.layerPointToLatLng(a[0]);for(t=0,n=0;t<h-1;t++)if(o=a[t],s=a[t+1],e=o.distanceTo(s),(n+=e)>i)return r=(n-i)/e,this._map.layerPointToLatLng([s.x-r*(s.x-o.x),s.y-r*(s.y-o.y)])},getBounds:function(){return this._bounds},addLatLng:function(t,i){return i=i||this._defaultShape(),t=C(t),i.push(t),this._bounds.extend(t),this.redraw()},_setLatLngs:function(t){this._bounds=new T,this._latlngs=this._convertLatLngs(t)},_defaultShape:function(){return jt(this._latlngs)?this._latlngs:this._latlngs[0]},_convertLatLngs:function(t){for(var i=[],e=jt(t),n=0,o=t.length;n<o;n++)e?(i[n]=C(t[n]),this._bounds.extend(i[n])):i[n]=this._convertLatLngs(t[n]);return i},_project:function(){var t=new P;this._rings=[],this._projectLatlngs(this._latlngs,this._rings,t);var i=this._clickTolerance(),e=new x(i,i);this._bounds.isValid()&&t.isValid()&&(t.min._subtract(e),t.max._add(e),this._pxBounds=t)},_projectLatlngs:function(t,i,e){var n,o,s=t[0]instanceof M,r=t.length;if(s){for(o=[],n=0;n<r;n++)o[n]=this._map.latLngToLayerPoint(t[n]),e.extend(o[n]);i.push(o)}else for(n=0;n<r;n++)this._projectLatlngs(t[n],i,e)},_clipPoints:function(){var t=this._renderer._bounds;if(this._parts=[],this._pxBounds&&this._pxBounds.intersects(t))if(this.options.noClip)this._parts=this._rings;else{var i,e,n,o,s,r,a,h=this._parts;for(i=0,n=0,o=this._rings.length;i<o;i++)for(e=0,s=(a=this._rings[i]).length;e<s-1;e++)(r=It(a[e],a[e+1],t,e,!0))&&(h[n]=h[n]||[],h[n].push(r[0]),r[1]===a[e+1]&&e!==s-2||(h[n].push(r[1]),n++))}},_simplifyPoints:function(){for(var t=this._parts,i=this.options.smoothFactor,e=0,n=t.length;e<n;e++)t[e]=Zt(t[e],i)},_update:function(){this._map&&(this._clipPoints(),this._simplifyPoints(),this._updatePath())},_updatePath:function(){this._renderer._updatePoly(this)},_containsPoint:function(t,i){var e,n,o,s,r,a,h=this._clickTolerance();if(!this._pxBounds||!this._pxBounds.contains(t))return!1;for(e=0,s=this._parts.length;e<s;e++)for(n=0,o=(r=(a=this._parts[e]).length)-1;n<r;o=n++)if((i||0!==n)&&Et(t,a[o],a[n])<=h)return!0;return!1}});nn._flat=Wt;var on=nn.extend({options:{fill:!0},isEmpty:function(){return!this._latlngs.length||!this._latlngs[0].length},getCenter:function(){if(!this._map)throw new Error("Must add layer to map before using getCenter()");var t,i,e,n,o,s,r,a,h,u=this._rings[0],l=u.length;if(!l)return null;for(s=r=a=0,t=0,i=l-1;t<l;i=t++)e=u[t],n=u[i],o=e.y*n.x-n.y*e.x,r+=(e.x+n.x)*o,a+=(e.y+n.y)*o,s+=3*o;return h=0===s?u[0]:[r/s,a/s],this._map.layerPointToLatLng(h)},_convertLatLngs:function(t){var i=nn.prototype._convertLatLngs.call(this,t),e=i.length;return e>=2&&i[0]instanceof M&&i[0].equals(i[e-1])&&i.pop(),i},_setLatLngs:function(t){nn.prototype._setLatLngs.call(this,t),jt(this._latlngs)&&(this._latlngs=[this._latlngs])},_defaultShape:function(){return jt(this._latlngs[0])?this._latlngs[0]:this._latlngs[0][0]},_clipPoints:function(){var t=this._renderer._bounds,i=this.options.weight,e=new x(i,i);if(t=new P(t.min.subtract(e),t.max.add(e)),this._parts=[],this._pxBounds&&this._pxBounds.intersects(t))if(this.options.noClip)this._parts=this._rings;else for(var n,o=0,s=this._rings.length;o<s;o++)(n=Ht(this._rings[o],t,!0)).length&&this._parts.push(n)},_updatePath:function(){this._renderer._updatePoly(this,!0)},_containsPoint:function(t){var i,e,n,o,s,r,a,h,u=!1;if(!this._pxBounds||!this._pxBounds.contains(t))return!1;for(o=0,a=this._parts.length;o<a;o++)for(s=0,r=(h=(i=this._parts[o]).length)-1;s<h;r=s++)e=i[s],n=i[r],e.y>t.y!=n.y>t.y&&t.x<(n.x-e.x)*(t.y-e.y)/(n.y-e.y)+e.x&&(u=!u);return u||nn.prototype._containsPoint.call(this,t,!0)}}),sn=Ke.extend({initialize:function(t,i){l(this,i),this._layers={},t&&this.addData(t)},addData:function(t){var i,e,n,o=oi(t)?t:t.features;if(o){for(i=0,e=o.length;i<e;i++)((n=o[i]).geometries||n.geometry||n.features||n.coordinates)&&this.addData(n);return this}var s=this.options;if(s.filter&&!s.filter(t))return this;var r=Ft(t,s);return r?(r.feature=Yt(t),r.defaultOptions=r.options,this.resetStyle(r),s.onEachFeature&&s.onEachFeature(t,r),this.addLayer(r)):this},resetStyle:function(t){return t.options=i({},t.defaultOptions),this._setLayerStyle(t,this.options.style),this},setStyle:function(t){return this.eachLayer(function(i){this._setLayerStyle(i,t)},this)},_setLayerStyle:function(t,i){"function"==typeof i&&(i=i(t.feature)),t.setStyle&&t.setStyle(i)}}),rn={toGeoJSON:function(t){return Kt(this,{type:"Point",coordinates:qt(this.getLatLng(),t)})}};$e.include(rn),en.include(rn),tn.include(rn),nn.include({toGeoJSON:function(t){var i=!jt(this._latlngs),e=Gt(this._latlngs,i?1:0,!1,t);return Kt(this,{type:(i?"Multi":"")+"LineString",coordinates:e})}}),on.include({toGeoJSON:function(t){var i=!jt(this._latlngs),e=i&&!jt(this._latlngs[0]),n=Gt(this._latlngs,e?2:i?1:0,!0,t);return i||(n=[n]),Kt(this,{type:(e?"Multi":"")+"Polygon",coordinates:n})}}),Ge.include({toMultiPoint:function(t){var i=[];return this.eachLayer(function(e){i.push(e.toGeoJSON(t).geometry.coordinates)}),Kt(this,{type:"MultiPoint",coordinates:i})},toGeoJSON:function(t){var i=this.feature&&this.feature.geometry&&this.feature.geometry.type;if("MultiPoint"===i)return this.toMultiPoint(t);var e="GeometryCollection"===i,n=[];return this.eachLayer(function(i){if(i.toGeoJSON){var o=i.toGeoJSON(t);if(e)n.push(o.geometry);else{var s=Yt(o);"FeatureCollection"===s.type?n.push.apply(n,s.features):n.push(s)}}}),e?Kt(this,{geometries:n,type:"GeometryCollection"}):{type:"FeatureCollection",features:n}}});var an=Xt,hn=qe.extend({options:{opacity:1,alt:"",interactive:!1,crossOrigin:!1,errorOverlayUrl:"",zIndex:1,className:""},initialize:function(t,i,e){this._url=t,this._bounds=z(i),l(this,e)},onAdd:function(){this._image||(this._initImage(),this.options.opacity<1&&this._updateOpacity()),this.options.interactive&&(Q(this._image,"leaflet-interactive"),this.addInteractiveTarget(this._image)),this.getPane().appendChild(this._image),this._reset()},onRemove:function(){K(this._image),this.options.interactive&&this.removeInteractiveTarget(this._image)},setOpacity:function(t){return this.options.opacity=t,this._image&&this._updateOpacity(),this},setStyle:function(t){return t.opacity&&this.setOpacity(t.opacity),this},bringToFront:function(){return this._map&&X(this._image),this},bringToBack:function(){return this._map&&J(this._image),this},setUrl:function(t){return this._url=t,this._image&&(this._image.src=t),this},setBounds:function(t){return this._bounds=z(t),this._map&&this._reset(),this},getEvents:function(){var t={zoom:this._reset,viewreset:this._reset};return this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},getBounds:function(){return this._bounds},getElement:function(){return this._image},_initImage:function(){var t="IMG"===this._url.tagName,i=this._image=t?this._url:G("img");Q(i,"leaflet-image-layer"),this._zoomAnimated&&Q(i,"leaflet-zoom-animated"),this.options.className&&Q(i,this.options.className),i.onselectstart=r,i.onmousemove=r,i.onload=e(this.fire,this,"load"),i.onerror=e(this._overlayOnError,this,"error"),(this.options.crossOrigin||""===this.options.crossOrigin)&&(i.crossOrigin=!0===this.options.crossOrigin?"":this.options.crossOrigin),this.options.zIndex&&this._updateZIndex(),t?this._url=i.src:(i.src=this._url,i.alt=this.options.alt)},_animateZoom:function(t){var i=this._map.getZoomScale(t.zoom),e=this._map._latLngBoundsToNewLayerBounds(this._bounds,t.zoom,t.center).min;rt(this._image,e,i)},_reset:function(){var t=this._image,i=new P(this._map.latLngToLayerPoint(this._bounds.getNorthWest()),this._map.latLngToLayerPoint(this._bounds.getSouthEast())),e=i.getSize();at(t,i.min),t.style.width=e.x+"px",t.style.height=e.y+"px"},_updateOpacity:function(){nt(this._image,this.options.opacity)},_updateZIndex:function(){this._image&&void 0!==this.options.zIndex&&null!==this.options.zIndex&&(this._image.style.zIndex=this.options.zIndex)},_overlayOnError:function(){this.fire("error");var t=this.options.errorOverlayUrl;t&&this._url!==t&&(this._url=t,this._image.src=t)}}),un=hn.extend({options:{autoplay:!0,loop:!0},_initImage:function(){var t="VIDEO"===this._url.tagName,i=this._image=t?this._url:G("video");if(Q(i,"leaflet-image-layer"),this._zoomAnimated&&Q(i,"leaflet-zoom-animated"),i.onselectstart=r,i.onmousemove=r,i.onloadeddata=e(this.fire,this,"load"),t){for(var n=i.getElementsByTagName("source"),o=[],s=0;s<n.length;s++)o.push(n[s].src);this._url=n.length>0?o:[i.src]}else{oi(this._url)||(this._url=[this._url]),i.autoplay=!!this.options.autoplay,i.loop=!!this.options.loop;for(var a=0;a<this._url.length;a++){var h=G("source");h.src=this._url[a],i.appendChild(h)}}}}),ln=qe.extend({options:{offset:[0,7],className:"",pane:"popupPane"},initialize:function(t,i){l(this,t),this._source=i},onAdd:function(t){this._zoomAnimated=t._zoomAnimated,this._container||this._initLayout(),t._fadeAnimated&&nt(this._container,0),clearTimeout(this._removeTimeout),this.getPane().appendChild(this._container),this.update(),t._fadeAnimated&&nt(this._container,1),this.bringToFront()},onRemove:function(t){t._fadeAnimated?(nt(this._container,0),this._removeTimeout=setTimeout(e(K,void 0,this._container),200)):K(this._container)},getLatLng:function(){return this._latlng},setLatLng:function(t){return this._latlng=C(t),this._map&&(this._updatePosition(),this._adjustPan()),this},getContent:function(){return this._content},setContent:function(t){return this._content=t,this.update(),this},getElement:function(){return this._container},update:function(){this._map&&(this._container.style.visibility="hidden",this._updateContent(),this._updateLayout(),this._updatePosition(),this._container.style.visibility="",this._adjustPan())},getEvents:function(){var t={zoom:this._updatePosition,viewreset:this._updatePosition};return this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},isOpen:function(){return!!this._map&&this._map.hasLayer(this)},bringToFront:function(){return this._map&&X(this._container),this},bringToBack:function(){return this._map&&J(this._container),this},_updateContent:function(){if(this._content){var t=this._contentNode,i="function"==typeof this._content?this._content(this._source||this):this._content;if("string"==typeof i)t.innerHTML=i;else{for(;t.hasChildNodes();)t.removeChild(t.firstChild);t.appendChild(i)}this.fire("contentupdate")}},_updatePosition:function(){if(this._map){var t=this._map.latLngToLayerPoint(this._latlng),i=w(this.options.offset),e=this._getAnchor();this._zoomAnimated?at(this._container,t.add(e)):i=i.add(t).add(e);var n=this._containerBottom=-i.y,o=this._containerLeft=-Math.round(this._containerWidth/2)+i.x;this._container.style.bottom=n+"px",this._container.style.left=o+"px"}},_getAnchor:function(){return[0,0]}}),cn=ln.extend({options:{maxWidth:300,minWidth:50,maxHeight:null,autoPan:!0,autoPanPaddingTopLeft:null,autoPanPaddingBottomRight:null,autoPanPadding:[5,5],keepInView:!1,closeButton:!0,autoClose:!0,closeOnEscapeKey:!0,className:""},openOn:function(t){return t.openPopup(this),this},onAdd:function(t){ln.prototype.onAdd.call(this,t),t.fire("popupopen",{popup:this}),this._source&&(this._source.fire("popupopen",{popup:this},!0),this._source instanceof Qe||this._source.on("preclick",yt))},onRemove:function(t){ln.prototype.onRemove.call(this,t),t.fire("popupclose",{popup:this}),this._source&&(this._source.fire("popupclose",{popup:this},!0),this._source instanceof Qe||this._source.off("preclick",yt))},getEvents:function(){var t=ln.prototype.getEvents.call(this);return(void 0!==this.options.closeOnClick?this.options.closeOnClick:this._map.options.closePopupOnClick)&&(t.preclick=this._close),this.options.keepInView&&(t.moveend=this._adjustPan),t},_close:function(){this._map&&this._map.closePopup(this)},_initLayout:function(){var t="leaflet-popup",i=this._container=G("div",t+" "+(this.options.className||"")+" leaflet-zoom-animated"),e=this._wrapper=G("div",t+"-content-wrapper",i);if(this._contentNode=G("div",t+"-content",e),wt(e),xt(this._contentNode),mt(e,"contextmenu",yt),this._tipContainer=G("div",t+"-tip-container",i),this._tip=G("div",t+"-tip",this._tipContainer),this.options.closeButton){var n=this._closeButton=G("a",t+"-close-button",i);n.href="#close",n.innerHTML="&#215;",mt(n,"click",this._onCloseButtonClick,this)}},_updateLayout:function(){var t=this._contentNode,i=t.style;i.width="",i.whiteSpace="nowrap";var e=t.offsetWidth;e=Math.min(e,this.options.maxWidth),e=Math.max(e,this.options.minWidth),i.width=e+1+"px",i.whiteSpace="",i.height="";var n=t.offsetHeight,o=this.options.maxHeight;o&&n>o?(i.height=o+"px",Q(t,"leaflet-popup-scrolled")):tt(t,"leaflet-popup-scrolled"),this._containerWidth=this._container.offsetWidth},_animateZoom:function(t){var i=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center),e=this._getAnchor();at(this._container,i.add(e))},_adjustPan:function(){if(!(!this.options.autoPan||this._map._panAnim&&this._map._panAnim._inProgress)){var t=this._map,i=parseInt(q(this._container,"marginBottom"),10)||0,e=this._container.offsetHeight+i,n=this._containerWidth,o=new x(this._containerLeft,-e-this._containerBottom);o._add(ht(this._container));var s=t.layerPointToContainerPoint(o),r=w(this.options.autoPanPadding),a=w(this.options.autoPanPaddingTopLeft||r),h=w(this.options.autoPanPaddingBottomRight||r),u=t.getSize(),l=0,c=0;s.x+n+h.x>u.x&&(l=s.x+n-u.x+h.x),s.x-l-a.x<0&&(l=s.x-a.x),s.y+e+h.y>u.y&&(c=s.y+e-u.y+h.y),s.y-c-a.y<0&&(c=s.y-a.y),(l||c)&&t.fire("autopanstart").panBy([l,c])}},_onCloseButtonClick:function(t){this._close(),Lt(t)},_getAnchor:function(){return w(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}});be.mergeOptions({closePopupOnClick:!0}),be.include({openPopup:function(t,i,e){return t instanceof cn||(t=new cn(e).setContent(t)),i&&t.setLatLng(i),this.hasLayer(t)?this:(this._popup&&this._popup.options.autoClose&&this.closePopup(),this._popup=t,this.addLayer(t))},closePopup:function(t){return t&&t!==this._popup||(t=this._popup,this._popup=null),t&&this.removeLayer(t),this}}),qe.include({bindPopup:function(t,i){return t instanceof cn?(l(t,i),this._popup=t,t._source=this):(this._popup&&!i||(this._popup=new cn(i,this)),this._popup.setContent(t)),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(t,i){if(t instanceof qe||(i=t,t=this),t instanceof Ke)for(var e in this._layers){t=this._layers[e];break}return i||(i=t.getCenter?t.getCenter():t.getLatLng()),this._popup&&this._map&&(this._popup._source=t,this._popup.update(),this._map.openPopup(this._popup,i)),this},closePopup:function(){return this._popup&&this._popup._close(),this},togglePopup:function(t){return this._popup&&(this._popup._map?this.closePopup():this.openPopup(t)),this},isPopupOpen:function(){return!!this._popup&&this._popup.isOpen()},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},getPopup:function(){return this._popup},_openPopup:function(t){var i=t.layer||t.target;this._popup&&this._map&&(Lt(t),i instanceof Qe?this.openPopup(t.layer||t.target,t.latlng):this._map.hasLayer(this._popup)&&this._popup._source===i?this.closePopup():this.openPopup(i,t.latlng))},_movePopup:function(t){this._popup.setLatLng(t.latlng)},_onKeyPress:function(t){13===t.originalEvent.keyCode&&this._openPopup(t)}});var _n=ln.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,interactive:!1,opacity:.9},onAdd:function(t){ln.prototype.onAdd.call(this,t),this.setOpacity(this.options.opacity),t.fire("tooltipopen",{tooltip:this}),this._source&&this._source.fire("tooltipopen",{tooltip:this},!0)},onRemove:function(t){ln.prototype.onRemove.call(this,t),t.fire("tooltipclose",{tooltip:this}),this._source&&this._source.fire("tooltipclose",{tooltip:this},!0)},getEvents:function(){var t=ln.prototype.getEvents.call(this);return qi&&!this.options.permanent&&(t.preclick=this._close),t},_close:function(){this._map&&this._map.closeTooltip(this)},_initLayout:function(){var t="leaflet-tooltip "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=G("div",t)},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var i=this._map,e=this._container,n=i.latLngToContainerPoint(i.getCenter()),o=i.layerPointToContainerPoint(t),s=this.options.direction,r=e.offsetWidth,a=e.offsetHeight,h=w(this.options.offset),u=this._getAnchor();"top"===s?t=t.add(w(-r/2+h.x,-a+h.y+u.y,!0)):"bottom"===s?t=t.subtract(w(r/2-h.x,-h.y,!0)):"center"===s?t=t.subtract(w(r/2+h.x,a/2-u.y+h.y,!0)):"right"===s||"auto"===s&&o.x<n.x?(s="right",t=t.add(w(h.x+u.x,u.y-a/2+h.y,!0))):(s="left",t=t.subtract(w(r+u.x-h.x,a/2-u.y-h.y,!0))),tt(e,"leaflet-tooltip-right"),tt(e,"leaflet-tooltip-left"),tt(e,"leaflet-tooltip-top"),tt(e,"leaflet-tooltip-bottom"),Q(e,"leaflet-tooltip-"+s),at(e,t)},_updatePosition:function(){var t=this._map.latLngToLayerPoint(this._latlng);this._setPosition(t)},setOpacity:function(t){this.options.opacity=t,this._container&&nt(this._container,t)},_animateZoom:function(t){var i=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);this._setPosition(i)},_getAnchor:function(){return w(this._source&&this._source._getTooltipAnchor&&!this.options.sticky?this._source._getTooltipAnchor():[0,0])}});be.include({openTooltip:function(t,i,e){return t instanceof _n||(t=new _n(e).setContent(t)),i&&t.setLatLng(i),this.hasLayer(t)?this:this.addLayer(t)},closeTooltip:function(t){return t&&this.removeLayer(t),this}}),qe.include({bindTooltip:function(t,i){return t instanceof _n?(l(t,i),this._tooltip=t,t._source=this):(this._tooltip&&!i||(this._tooltip=new _n(i,this)),this._tooltip.setContent(t)),this._initTooltipInteractions(),this._tooltip.options.permanent&&this._map&&this._map.hasLayer(this)&&this.openTooltip(),this},unbindTooltip:function(){return this._tooltip&&(this._initTooltipInteractions(!0),this.closeTooltip(),this._tooltip=null),this},_initTooltipInteractions:function(t){if(t||!this._tooltipHandlersAdded){var i=t?"off":"on",e={remove:this.closeTooltip,move:this._moveTooltip};this._tooltip.options.permanent?e.add=this._openTooltip:(e.mouseover=this._openTooltip,e.mouseout=this.closeTooltip,this._tooltip.options.sticky&&(e.mousemove=this._moveTooltip),qi&&(e.click=this._openTooltip)),this[i](e),this._tooltipHandlersAdded=!t}},openTooltip:function(t,i){if(t instanceof qe||(i=t,t=this),t instanceof Ke)for(var e in this._layers){t=this._layers[e];break}return i||(i=t.getCenter?t.getCenter():t.getLatLng()),this._tooltip&&this._map&&(this._tooltip._source=t,this._tooltip.update(),this._map.openTooltip(this._tooltip,i),this._tooltip.options.interactive&&this._tooltip._container&&(Q(this._tooltip._container,"leaflet-clickable"),this.addInteractiveTarget(this._tooltip._container))),this},closeTooltip:function(){return this._tooltip&&(this._tooltip._close(),this._tooltip.options.interactive&&this._tooltip._container&&(tt(this._tooltip._container,"leaflet-clickable"),this.removeInteractiveTarget(this._tooltip._container))),this},toggleTooltip:function(t){return this._tooltip&&(this._tooltip._map?this.closeTooltip():this.openTooltip(t)),this},isTooltipOpen:function(){return this._tooltip.isOpen()},setTooltipContent:function(t){return this._tooltip&&this._tooltip.setContent(t),this},getTooltip:function(){return this._tooltip},_openTooltip:function(t){var i=t.layer||t.target;this._tooltip&&this._map&&this.openTooltip(i,this._tooltip.options.sticky?t.latlng:void 0)},_moveTooltip:function(t){var i,e,n=t.latlng;this._tooltip.options.sticky&&t.originalEvent&&(i=this._map.mouseEventToContainerPoint(t.originalEvent),e=this._map.containerPointToLayerPoint(i),n=this._map.layerPointToLatLng(e)),this._tooltip.setLatLng(n)}});var dn=Ye.extend({options:{iconSize:[12,12],html:!1,bgPos:null,className:"leaflet-div-icon"},createIcon:function(t){var i=t&&"DIV"===t.tagName?t:document.createElement("div"),e=this.options;if(i.innerHTML=!1!==e.html?e.html:"",e.bgPos){var n=w(e.bgPos);i.style.backgroundPosition=-n.x+"px "+-n.y+"px"}return this._setIconStyles(i,"icon"),i},createShadow:function(){return null}});Ye.Default=Xe;var pn=qe.extend({options:{tileSize:256,opacity:1,updateWhenIdle:Wi,updateWhenZooming:!0,updateInterval:200,zIndex:1,bounds:null,minZoom:0,maxZoom:void 0,maxNativeZoom:void 0,minNativeZoom:void 0,noWrap:!1,pane:"tilePane",className:"",keepBuffer:2},initialize:function(t){l(this,t)},onAdd:function(){this._initContainer(),this._levels={},this._tiles={},this._resetView(),this._update()},beforeAdd:function(t){t._addZoomLimit(this)},onRemove:function(t){this._removeAllTiles(),K(this._container),t._removeZoomLimit(this),this._container=null,this._tileZoom=void 0},bringToFront:function(){return this._map&&(X(this._container),this._setAutoZIndex(Math.max)),this},bringToBack:function(){return this._map&&(J(this._container),this._setAutoZIndex(Math.min)),this},getContainer:function(){return this._container},setOpacity:function(t){return this.options.opacity=t,this._updateOpacity(),this},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},isLoading:function(){return this._loading},redraw:function(){return this._map&&(this._removeAllTiles(),this._update()),this},getEvents:function(){var t={viewprereset:this._invalidateAll,viewreset:this._resetView,zoom:this._resetView,moveend:this._onMoveEnd};return this.options.updateWhenIdle||(this._onMove||(this._onMove=o(this._onMoveEnd,this.options.updateInterval,this)),t.move=this._onMove),this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},createTile:function(){return document.createElement("div")},getTileSize:function(){var t=this.options.tileSize;return t instanceof x?t:new x(t,t)},_updateZIndex:function(){this._container&&void 0!==this.options.zIndex&&null!==this.options.zIndex&&(this._container.style.zIndex=this.options.zIndex)},_setAutoZIndex:function(t){for(var i,e=this.getPane().children,n=-t(-1/0,1/0),o=0,s=e.length;o<s;o++)i=e[o].style.zIndex,e[o]!==this._container&&i&&(n=t(n,+i));isFinite(n)&&(this.options.zIndex=n+t(-1,1),this._updateZIndex())},_updateOpacity:function(){if(this._map&&!Li){nt(this._container,this.options.opacity);var t=+new Date,i=!1,e=!1;for(var n in this._tiles){var o=this._tiles[n];if(o.current&&o.loaded){var s=Math.min(1,(t-o.loaded)/200);nt(o.el,s),s<1?i=!0:(o.active?e=!0:this._onOpaqueTile(o),o.active=!0)}}e&&!this._noPrune&&this._pruneTiles(),i&&(g(this._fadeFrame),this._fadeFrame=f(this._updateOpacity,this))}},_onOpaqueTile:r,_initContainer:function(){this._container||(this._container=G("div","leaflet-layer "+(this.options.className||"")),this._updateZIndex(),this.options.opacity<1&&this._updateOpacity(),this.getPane().appendChild(this._container))},_updateLevels:function(){var t=this._tileZoom,i=this.options.maxZoom;if(void 0!==t){for(var e in this._levels)this._levels[e].el.children.length||e===t?(this._levels[e].el.style.zIndex=i-Math.abs(t-e),this._onUpdateLevel(e)):(K(this._levels[e].el),this._removeTilesAtZoom(e),this._onRemoveLevel(e),delete this._levels[e]);var n=this._levels[t],o=this._map;return n||((n=this._levels[t]={}).el=G("div","leaflet-tile-container leaflet-zoom-animated",this._container),n.el.style.zIndex=i,n.origin=o.project(o.unproject(o.getPixelOrigin()),t).round(),n.zoom=t,this._setZoomTransform(n,o.getCenter(),o.getZoom()),n.el.offsetWidth,this._onCreateLevel(n)),this._level=n,n}},_onUpdateLevel:r,_onRemoveLevel:r,_onCreateLevel:r,_pruneTiles:function(){if(this._map){var t,i,e=this._map.getZoom();if(e>this.options.maxZoom||e<this.options.minZoom)this._removeAllTiles();else{for(t in this._tiles)(i=this._tiles[t]).retain=i.current;for(t in this._tiles)if((i=this._tiles[t]).current&&!i.active){var n=i.coords;this._retainParent(n.x,n.y,n.z,n.z-5)||this._retainChildren(n.x,n.y,n.z,n.z+2)}for(t in this._tiles)this._tiles[t].retain||this._removeTile(t)}}},_removeTilesAtZoom:function(t){for(var i in this._tiles)this._tiles[i].coords.z===t&&this._removeTile(i)},_removeAllTiles:function(){for(var t in this._tiles)this._removeTile(t)},_invalidateAll:function(){for(var t in this._levels)K(this._levels[t].el),this._onRemoveLevel(t),delete this._levels[t];this._removeAllTiles(),this._tileZoom=void 0},_retainParent:function(t,i,e,n){var o=Math.floor(t/2),s=Math.floor(i/2),r=e-1,a=new x(+o,+s);a.z=+r;var h=this._tileCoordsToKey(a),u=this._tiles[h];return u&&u.active?(u.retain=!0,!0):(u&&u.loaded&&(u.retain=!0),r>n&&this._retainParent(o,s,r,n))},_retainChildren:function(t,i,e,n){for(var o=2*t;o<2*t+2;o++)for(var s=2*i;s<2*i+2;s++){var r=new x(o,s);r.z=e+1;var a=this._tileCoordsToKey(r),h=this._tiles[a];h&&h.active?h.retain=!0:(h&&h.loaded&&(h.retain=!0),e+1<n&&this._retainChildren(o,s,e+1,n))}},_resetView:function(t){var i=t&&(t.pinch||t.flyTo);this._setView(this._map.getCenter(),this._map.getZoom(),i,i)},_animateZoom:function(t){this._setView(t.center,t.zoom,!0,t.noUpdate)},_clampZoom:function(t){var i=this.options;return void 0!==i.minNativeZoom&&t<i.minNativeZoom?i.minNativeZoom:void 0!==i.maxNativeZoom&&i.maxNativeZoom<t?i.maxNativeZoom:t},_setView:function(t,i,e,n){var o=this._clampZoom(Math.round(i));(void 0!==this.options.maxZoom&&o>this.options.maxZoom||void 0!==this.options.minZoom&&o<this.options.minZoom)&&(o=void 0);var s=this.options.updateWhenZooming&&o!==this._tileZoom;n&&!s||(this._tileZoom=o,this._abortLoading&&this._abortLoading(),this._updateLevels(),this._resetGrid(),void 0!==o&&this._update(t),e||this._pruneTiles(),this._noPrune=!!e),this._setZoomTransforms(t,i)},_setZoomTransforms:function(t,i){for(var e in this._levels)this._setZoomTransform(this._levels[e],t,i)},_setZoomTransform:function(t,i,e){var n=this._map.getZoomScale(e,t.zoom),o=t.origin.multiplyBy(n).subtract(this._map._getNewPixelOrigin(i,e)).round();ji?rt(t.el,o,n):at(t.el,o)},_resetGrid:function(){var t=this._map,i=t.options.crs,e=this._tileSize=this.getTileSize(),n=this._tileZoom,o=this._map.getPixelWorldBounds(this._tileZoom);o&&(this._globalTileRange=this._pxBoundsToTileRange(o)),this._wrapX=i.wrapLng&&!this.options.noWrap&&[Math.floor(t.project([0,i.wrapLng[0]],n).x/e.x),Math.ceil(t.project([0,i.wrapLng[1]],n).x/e.y)],this._wrapY=i.wrapLat&&!this.options.noWrap&&[Math.floor(t.project([i.wrapLat[0],0],n).y/e.x),Math.ceil(t.project([i.wrapLat[1],0],n).y/e.y)]},_onMoveEnd:function(){this._map&&!this._map._animatingZoom&&this._update()},_getTiledPixelBounds:function(t){var i=this._map,e=i._animatingZoom?Math.max(i._animateToZoom,i.getZoom()):i.getZoom(),n=i.getZoomScale(e,this._tileZoom),o=i.project(t,this._tileZoom).floor(),s=i.getSize().divideBy(2*n);return new P(o.subtract(s),o.add(s))},_update:function(t){var i=this._map;if(i){var e=this._clampZoom(i.getZoom());if(void 0===t&&(t=i.getCenter()),void 0!==this._tileZoom){var n=this._getTiledPixelBounds(t),o=this._pxBoundsToTileRange(n),s=o.getCenter(),r=[],a=this.options.keepBuffer,h=new P(o.getBottomLeft().subtract([a,-a]),o.getTopRight().add([a,-a]));if(!(isFinite(o.min.x)&&isFinite(o.min.y)&&isFinite(o.max.x)&&isFinite(o.max.y)))throw new Error("Attempted to load an infinite number of tiles");for(var u in this._tiles){var l=this._tiles[u].coords;l.z===this._tileZoom&&h.contains(new x(l.x,l.y))||(this._tiles[u].current=!1)}if(Math.abs(e-this._tileZoom)>1)this._setView(t,e);else{for(var c=o.min.y;c<=o.max.y;c++)for(var _=o.min.x;_<=o.max.x;_++){var d=new x(_,c);if(d.z=this._tileZoom,this._isValidTile(d)){var p=this._tiles[this._tileCoordsToKey(d)];p?p.current=!0:r.push(d)}}if(r.sort(function(t,i){return t.distanceTo(s)-i.distanceTo(s)}),0!==r.length){this._loading||(this._loading=!0,this.fire("loading"));var m=document.createDocumentFragment();for(_=0;_<r.length;_++)this._addTile(r[_],m);this._level.el.appendChild(m)}}}}},_isValidTile:function(t){var i=this._map.options.crs;if(!i.infinite){var e=this._globalTileRange;if(!i.wrapLng&&(t.x<e.min.x||t.x>e.max.x)||!i.wrapLat&&(t.y<e.min.y||t.y>e.max.y))return!1}if(!this.options.bounds)return!0;var n=this._tileCoordsToBounds(t);return z(this.options.bounds).overlaps(n)},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToNwSe:function(t){var i=this._map,e=this.getTileSize(),n=t.scaleBy(e),o=n.add(e);return[i.unproject(n,t.z),i.unproject(o,t.z)]},_tileCoordsToBounds:function(t){var i=this._tileCoordsToNwSe(t),e=new T(i[0],i[1]);return this.options.noWrap||(e=this._map.wrapLatLngBounds(e)),e},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var i=t.split(":"),e=new x(+i[0],+i[1]);return e.z=+i[2],e},_removeTile:function(t){var i=this._tiles[t];i&&(K(i.el),delete this._tiles[t],this.fire("tileunload",{tile:i.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){Q(t,"leaflet-tile");var i=this.getTileSize();t.style.width=i.x+"px",t.style.height=i.y+"px",t.onselectstart=r,t.onmousemove=r,Li&&this.options.opacity<1&&nt(t,this.options.opacity),zi&&!Mi&&(t.style.WebkitBackfaceVisibility="hidden")},_addTile:function(t,i){var n=this._getTilePos(t),o=this._tileCoordsToKey(t),s=this.createTile(this._wrapCoords(t),e(this._tileReady,this,t));this._initTile(s),this.createTile.length<2&&f(e(this._tileReady,this,t,null,s)),at(s,n),this._tiles[o]={el:s,coords:t,current:!0},i.appendChild(s),this.fire("tileloadstart",{tile:s,coords:t})},_tileReady:function(t,i,n){i&&this.fire("tileerror",{error:i,tile:n,coords:t});var o=this._tileCoordsToKey(t);(n=this._tiles[o])&&(n.loaded=+new Date,this._map._fadeAnimated?(nt(n.el,0),g(this._fadeFrame),this._fadeFrame=f(this._updateOpacity,this)):(n.active=!0,this._pruneTiles()),i||(Q(n.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:n.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),Li||!this._map._fadeAnimated?f(this._pruneTiles,this):setTimeout(e(this._pruneTiles,this),250)))},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var i=new x(this._wrapX?s(t.x,this._wrapX):t.x,this._wrapY?s(t.y,this._wrapY):t.y);return i.z=t.z,i},_pxBoundsToTileRange:function(t){var i=this.getTileSize();return new P(t.min.unscaleBy(i).floor(),t.max.unscaleBy(i).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}}),mn=pn.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1},initialize:function(t,i){this._url=t,(i=l(this,i)).detectRetina&&Yi&&i.maxZoom>0&&(i.tileSize=Math.floor(i.tileSize/2),i.zoomReverse?(i.zoomOffset--,i.minZoom++):(i.zoomOffset++,i.maxZoom--),i.minZoom=Math.max(0,i.minZoom)),"string"==typeof i.subdomains&&(i.subdomains=i.subdomains.split("")),zi||this.on("tileunload",this._onTileRemove)},setUrl:function(t,i){return this._url=t,i||this.redraw(),this},createTile:function(t,i){var n=document.createElement("img");return mt(n,"load",e(this._tileOnLoad,this,i,n)),mt(n,"error",e(this._tileOnError,this,i,n)),(this.options.crossOrigin||""===this.options.crossOrigin)&&(n.crossOrigin=!0===this.options.crossOrigin?"":this.options.crossOrigin),n.alt="",n.setAttribute("role","presentation"),n.src=this.getTileUrl(t),n},getTileUrl:function(t){var e={r:Yi?"@2x":"",s:this._getSubdomain(t),x:t.x,y:t.y,z:this._getZoomForUrl()};if(this._map&&!this._map.options.crs.infinite){var n=this._globalTileRange.max.y-t.y;this.options.tms&&(e.y=n),e["-y"]=n}return _(this._url,i(e,this.options))},_tileOnLoad:function(t,i){Li?setTimeout(e(t,this,null,i),0):t(null,i)},_tileOnError:function(t,i,e){var n=this.options.errorTileUrl;n&&i.getAttribute("src")!==n&&(i.src=n),t(e,i)},_onTileRemove:function(t){t.tile.onload=null},_getZoomForUrl:function(){var t=this._tileZoom,i=this.options.maxZoom,e=this.options.zoomReverse,n=this.options.zoomOffset;return e&&(t=i-t),t+n},_getSubdomain:function(t){var i=Math.abs(t.x+t.y)%this.options.subdomains.length;return this.options.subdomains[i]},_abortLoading:function(){var t,i;for(t in this._tiles)this._tiles[t].coords.z!==this._tileZoom&&((i=this._tiles[t].el).onload=r,i.onerror=r,i.complete||(i.src=si,K(i),delete this._tiles[t]))},_removeTile:function(t){var i=this._tiles[t];if(i)return Si||i.el.setAttribute("src",si),pn.prototype._removeTile.call(this,t)},_tileReady:function(t,i,e){if(this._map&&(!e||e.getAttribute("src")!==si))return pn.prototype._tileReady.call(this,t,i,e)}}),fn=mn.extend({defaultWmsParams:{service:"WMS",request:"GetMap",layers:"",styles:"",format:"image/jpeg",transparent:!1,version:"1.1.1"},options:{crs:null,uppercase:!1},initialize:function(t,e){this._url=t;var n=i({},this.defaultWmsParams);for(var o in e)o in this.options||(n[o]=e[o]);var s=(e=l(this,e)).detectRetina&&Yi?2:1,r=this.getTileSize();n.width=r.x*s,n.height=r.y*s,this.wmsParams=n},onAdd:function(t){this._crs=this.options.crs||t.options.crs,this._wmsVersion=parseFloat(this.wmsParams.version);var i=this._wmsVersion>=1.3?"crs":"srs";this.wmsParams[i]=this._crs.code,mn.prototype.onAdd.call(this,t)},getTileUrl:function(t){var i=this._tileCoordsToNwSe(t),e=this._crs,n=b(e.project(i[0]),e.project(i[1])),o=n.min,s=n.max,r=(this._wmsVersion>=1.3&&this._crs===Ue?[o.y,o.x,s.y,s.x]:[o.x,o.y,s.x,s.y]).join(","),a=mn.prototype.getTileUrl.call(this,t);return a+c(this.wmsParams,a,this.options.uppercase)+(this.options.uppercase?"&BBOX=":"&bbox=")+r},setParams:function(t,e){return i(this.wmsParams,t),e||this.redraw(),this}});mn.WMS=fn,Jt.wms=function(t,i){return new fn(t,i)};var gn=qe.extend({options:{padding:.1,tolerance:0},initialize:function(t){l(this,t),n(this),this._layers=this._layers||{}},onAdd:function(){this._container||(this._initContainer(),this._zoomAnimated&&Q(this._container,"leaflet-zoom-animated")),this.getPane().appendChild(this._container),this._update(),this.on("update",this._updatePaths,this)},onRemove:function(){this.off("update",this._updatePaths,this),this._destroyContainer()},getEvents:function(){var t={viewreset:this._reset,zoom:this._onZoom,moveend:this._update,zoomend:this._onZoomEnd};return this._zoomAnimated&&(t.zoomanim=this._onAnimZoom),t},_onAnimZoom:function(t){this._updateTransform(t.center,t.zoom)},_onZoom:function(){this._updateTransform(this._map.getCenter(),this._map.getZoom())},_updateTransform:function(t,i){var e=this._map.getZoomScale(i,this._zoom),n=ht(this._container),o=this._map.getSize().multiplyBy(.5+this.options.padding),s=this._map.project(this._center,i),r=this._map.project(t,i).subtract(s),a=o.multiplyBy(-e).add(n).add(o).subtract(r);ji?rt(this._container,a,e):at(this._container,a)},_reset:function(){this._update(),this._updateTransform(this._center,this._zoom);for(var t in this._layers)this._layers[t]._reset()},_onZoomEnd:function(){for(var t in this._layers)this._layers[t]._project()},_updatePaths:function(){for(var t in this._layers)this._layers[t]._update()},_update:function(){var t=this.options.padding,i=this._map.getSize(),e=this._map.containerPointToLayerPoint(i.multiplyBy(-t)).round();this._bounds=new P(e,e.add(i.multiplyBy(1+2*t)).round()),this._center=this._map.getCenter(),this._zoom=this._map.getZoom()}}),vn=gn.extend({getEvents:function(){var t=gn.prototype.getEvents.call(this);return t.viewprereset=this._onViewPreReset,t},_onViewPreReset:function(){this._postponeUpdatePaths=!0},onAdd:function(){gn.prototype.onAdd.call(this),this._draw()},_initContainer:function(){var t=this._container=document.createElement("canvas");mt(t,"mousemove",o(this._onMouseMove,32,this),this),mt(t,"click dblclick mousedown mouseup contextmenu",this._onClick,this),mt(t,"mouseout",this._handleMouseOut,this),this._ctx=t.getContext("2d")},_destroyContainer:function(){g(this._redrawRequest),delete this._ctx,K(this._container),ft(this._container),delete this._container},_updatePaths:function(){if(!this._postponeUpdatePaths){this._redrawBounds=null;for(var t in this._layers)this._layers[t]._update();this._redraw()}},_update:function(){if(!this._map._animatingZoom||!this._bounds){this._drawnLayers={},gn.prototype._update.call(this);var t=this._bounds,i=this._container,e=t.getSize(),n=Yi?2:1;at(i,t.min),i.width=n*e.x,i.height=n*e.y,i.style.width=e.x+"px",i.style.height=e.y+"px",Yi&&this._ctx.scale(2,2),this._ctx.translate(-t.min.x,-t.min.y),this.fire("update")}},_reset:function(){gn.prototype._reset.call(this),this._postponeUpdatePaths&&(this._postponeUpdatePaths=!1,this._updatePaths())},_initPath:function(t){this._updateDashArray(t),this._layers[n(t)]=t;var i=t._order={layer:t,prev:this._drawLast,next:null};this._drawLast&&(this._drawLast.next=i),this._drawLast=i,this._drawFirst=this._drawFirst||this._drawLast},_addPath:function(t){this._requestRedraw(t)},_removePath:function(t){var i=t._order,e=i.next,o=i.prev;e?e.prev=o:this._drawLast=o,o?o.next=e:this._drawFirst=e,delete this._drawnLayers[t._leaflet_id],delete t._order,delete this._layers[n(t)],this._requestRedraw(t)},_updatePath:function(t){this._extendRedrawBounds(t),t._project(),t._update(),this._requestRedraw(t)},_updateStyle:function(t){this._updateDashArray(t),this._requestRedraw(t)},_updateDashArray:function(t){if("string"==typeof t.options.dashArray){var i,e=t.options.dashArray.split(/[, ]+/),n=[];for(i=0;i<e.length;i++)n.push(Number(e[i]));t.options._dashArray=n}else t.options._dashArray=t.options.dashArray},_requestRedraw:function(t){this._map&&(this._extendRedrawBounds(t),this._redrawRequest=this._redrawRequest||f(this._redraw,this))},_extendRedrawBounds:function(t){if(t._pxBounds){var i=(t.options.weight||0)+1;this._redrawBounds=this._redrawBounds||new P,this._redrawBounds.extend(t._pxBounds.min.subtract([i,i])),this._redrawBounds.extend(t._pxBounds.max.add([i,i]))}},_redraw:function(){this._redrawRequest=null,this._redrawBounds&&(this._redrawBounds.min._floor(),this._redrawBounds.max._ceil()),this._clear(),this._draw(),this._redrawBounds=null},_clear:function(){var t=this._redrawBounds;if(t){var i=t.getSize();this._ctx.clearRect(t.min.x,t.min.y,i.x,i.y)}else this._ctx.clearRect(0,0,this._container.width,this._container.height)},_draw:function(){var t,i=this._redrawBounds;if(this._ctx.save(),i){var e=i.getSize();this._ctx.beginPath(),this._ctx.rect(i.min.x,i.min.y,e.x,e.y),this._ctx.clip()}this._drawing=!0;for(var n=this._drawFirst;n;n=n.next)t=n.layer,(!i||t._pxBounds&&t._pxBounds.intersects(i))&&t._updatePath();this._drawing=!1,this._ctx.restore()},_updatePoly:function(t,i){if(this._drawing){var e,n,o,s,r=t._parts,a=r.length,h=this._ctx;if(a){for(this._drawnLayers[t._leaflet_id]=t,h.beginPath(),e=0;e<a;e++){for(n=0,o=r[e].length;n<o;n++)s=r[e][n],h[n?"lineTo":"moveTo"](s.x,s.y);i&&h.closePath()}this._fillStroke(h,t)}}},_updateCircle:function(t){if(this._drawing&&!t._empty()){var i=t._point,e=this._ctx,n=Math.max(Math.round(t._radius),1),o=(Math.max(Math.round(t._radiusY),1)||n)/n;this._drawnLayers[t._leaflet_id]=t,1!==o&&(e.save(),e.scale(1,o)),e.beginPath(),e.arc(i.x,i.y/o,n,0,2*Math.PI,!1),1!==o&&e.restore(),this._fillStroke(e,t)}},_fillStroke:function(t,i){var e=i.options;e.fill&&(t.globalAlpha=e.fillOpacity,t.fillStyle=e.fillColor||e.color,t.fill(e.fillRule||"evenodd")),e.stroke&&0!==e.weight&&(t.setLineDash&&t.setLineDash(i.options&&i.options._dashArray||[]),t.globalAlpha=e.opacity,t.lineWidth=e.weight,t.strokeStyle=e.color,t.lineCap=e.lineCap,t.lineJoin=e.lineJoin,t.stroke())},_onClick:function(t){for(var i,e,n=this._map.mouseEventToLayerPoint(t),o=this._drawFirst;o;o=o.next)(i=o.layer).options.interactive&&i._containsPoint(n)&&!this._map._draggableMoved(i)&&(e=i);e&&(zt(t),this._fireEvent([e],t))},_onMouseMove:function(t){if(this._map&&!this._map.dragging.moving()&&!this._map._animatingZoom){var i=this._map.mouseEventToLayerPoint(t);this._handleMouseHover(t,i)}},_handleMouseOut:function(t){var i=this._hoveredLayer;i&&(tt(this._container,"leaflet-interactive"),this._fireEvent([i],t,"mouseout"),this._hoveredLayer=null)},_handleMouseHover:function(t,i){for(var e,n,o=this._drawFirst;o;o=o.next)(e=o.layer).options.interactive&&e._containsPoint(i)&&(n=e);n!==this._hoveredLayer&&(this._handleMouseOut(t),n&&(Q(this._container,"leaflet-interactive"),this._fireEvent([n],t,"mouseover"),this._hoveredLayer=n)),this._hoveredLayer&&this._fireEvent([this._hoveredLayer],t)},_fireEvent:function(t,i,e){this._map._fireDOMEvent(i,e||i.type,t)},_bringToFront:function(t){var i=t._order,e=i.next,n=i.prev;e&&(e.prev=n,n?n.next=e:e&&(this._drawFirst=e),i.prev=this._drawLast,this._drawLast.next=i,i.next=null,this._drawLast=i,this._requestRedraw(t))},_bringToBack:function(t){var i=t._order,e=i.next,n=i.prev;n&&(n.next=e,e?e.prev=n:n&&(this._drawLast=n),i.prev=null,i.next=this._drawFirst,this._drawFirst.prev=i,this._drawFirst=i,this._requestRedraw(t))}}),yn=function(){try{return document.namespaces.add("lvml","urn:schemas-microsoft-com:vml"),function(t){return document.createElement("<lvml:"+t+' class="lvml">')}}catch(t){return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),xn={_initContainer:function(){this._container=G("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(gn.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var i=t._container=yn("shape");Q(i,"leaflet-vml-shape "+(this.options.className||"")),i.coordsize="1 1",t._path=yn("path"),i.appendChild(t._path),this._updateStyle(t),this._layers[n(t)]=t},_addPath:function(t){var i=t._container;this._container.appendChild(i),t.options.interactive&&t.addInteractiveTarget(i)},_removePath:function(t){var i=t._container;K(i),t.removeInteractiveTarget(i),delete this._layers[n(t)]},_updateStyle:function(t){var i=t._stroke,e=t._fill,n=t.options,o=t._container;o.stroked=!!n.stroke,o.filled=!!n.fill,n.stroke?(i||(i=t._stroke=yn("stroke")),o.appendChild(i),i.weight=n.weight+"px",i.color=n.color,i.opacity=n.opacity,n.dashArray?i.dashStyle=oi(n.dashArray)?n.dashArray.join(" "):n.dashArray.replace(/( *, *)/g," "):i.dashStyle="",i.endcap=n.lineCap.replace("butt","flat"),i.joinstyle=n.lineJoin):i&&(o.removeChild(i),t._stroke=null),n.fill?(e||(e=t._fill=yn("fill")),o.appendChild(e),e.color=n.fillColor||n.color,e.opacity=n.fillOpacity):e&&(o.removeChild(e),t._fill=null)},_updateCircle:function(t){var i=t._point.round(),e=Math.round(t._radius),n=Math.round(t._radiusY||e);this._setPath(t,t._empty()?"M0 0":"AL "+i.x+","+i.y+" "+e+","+n+" 0,23592600")},_setPath:function(t,i){t._path.v=i},_bringToFront:function(t){X(t._container)},_bringToBack:function(t){J(t._container)}},wn=$i?yn:E,Pn=gn.extend({getEvents:function(){var t=gn.prototype.getEvents.call(this);return t.zoomstart=this._onZoomStart,t},_initContainer:function(){this._container=wn("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=wn("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){K(this._container),ft(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_onZoomStart:function(){this._update()},_update:function(){if(!this._map._animatingZoom||!this._bounds){gn.prototype._update.call(this);var t=this._bounds,i=t.getSize(),e=this._container;this._svgSize&&this._svgSize.equals(i)||(this._svgSize=i,e.setAttribute("width",i.x),e.setAttribute("height",i.y)),at(e,t.min),e.setAttribute("viewBox",[t.min.x,t.min.y,i.x,i.y].join(" ")),this.fire("update")}},_initPath:function(t){var i=t._path=wn("path");t.options.className&&Q(i,t.options.className),t.options.interactive&&Q(i,"leaflet-interactive"),this._updateStyle(t),this._layers[n(t)]=t},_addPath:function(t){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){K(t._path),t.removeInteractiveTarget(t._path),delete this._layers[n(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var i=t._path,e=t.options;i&&(e.stroke?(i.setAttribute("stroke",e.color),i.setAttribute("stroke-opacity",e.opacity),i.setAttribute("stroke-width",e.weight),i.setAttribute("stroke-linecap",e.lineCap),i.setAttribute("stroke-linejoin",e.lineJoin),e.dashArray?i.setAttribute("stroke-dasharray",e.dashArray):i.removeAttribute("stroke-dasharray"),e.dashOffset?i.setAttribute("stroke-dashoffset",e.dashOffset):i.removeAttribute("stroke-dashoffset")):i.setAttribute("stroke","none"),e.fill?(i.setAttribute("fill",e.fillColor||e.color),i.setAttribute("fill-opacity",e.fillOpacity),i.setAttribute("fill-rule",e.fillRule||"evenodd")):i.setAttribute("fill","none"))},_updatePoly:function(t,i){this._setPath(t,k(t._parts,i))},_updateCircle:function(t){var i=t._point,e=Math.max(Math.round(t._radius),1),n="a"+e+","+(Math.max(Math.round(t._radiusY),1)||e)+" 0 1,0 ",o=t._empty()?"M0 0":"M"+(i.x-e)+","+i.y+n+2*e+",0 "+n+2*-e+",0 ";this._setPath(t,o)},_setPath:function(t,i){t._path.setAttribute("d",i)},_bringToFront:function(t){X(t._path)},_bringToBack:function(t){J(t._path)}});$i&&Pn.include(xn),be.include({getRenderer:function(t){var i=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer;return i||(i=this._renderer=this._createRenderer()),this.hasLayer(i)||this.addLayer(i),i},_getPaneRenderer:function(t){if("overlayPane"===t||void 0===t)return!1;var i=this._paneRenderers[t];return void 0===i&&(i=this._createRenderer({pane:t}),this._paneRenderers[t]=i),i},_createRenderer:function(t){return this.options.preferCanvas&&$t(t)||Qt(t)}});var Ln=on.extend({initialize:function(t,i){on.prototype.initialize.call(this,this._boundsToLatLngs(t),i)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return t=z(t),[t.getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});Pn.create=wn,Pn.pointsToPath=k,sn.geometryToLayer=Ft,sn.coordsToLatLng=Ut,sn.coordsToLatLngs=Vt,sn.latLngToCoords=qt,sn.latLngsToCoords=Gt,sn.getFeature=Kt,sn.asFeature=Yt,be.mergeOptions({boxZoom:!0});var bn=Ee.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._resetStateTimeout=0,t.on("unload",this._destroy,this)},addHooks:function(){mt(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){ft(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){K(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){0!==this._resetStateTimeout&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(t){if(!t.shiftKey||1!==t.which&&1!==t.button)return!1;this._clearDeferredResetState(),this._resetState(),fi(),ut(),this._startPoint=this._map.mouseEventToContainerPoint(t),mt(document,{contextmenu:Lt,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=G("div","leaflet-zoom-box",this._container),Q(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var i=new P(this._point,this._startPoint),e=i.getSize();at(this._box,i.min),this._box.style.width=e.x+"px",this._box.style.height=e.y+"px"},_finish:function(){this._moved&&(K(this._box),tt(this._container,"leaflet-crosshair")),gi(),lt(),ft(document,{contextmenu:Lt,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){if((1===t.which||1===t.button)&&(this._finish(),this._moved)){this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(e(this._resetState,this),0);var i=new T(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point));this._map.fitBounds(i).fire("boxzoomend",{boxZoomBounds:i})}},_onKeyDown:function(t){27===t.keyCode&&this._finish()}});be.addInitHook("addHandler","boxZoom",bn),be.mergeOptions({doubleClickZoom:!0});var Tn=Ee.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var i=this._map,e=i.getZoom(),n=i.options.zoomDelta,o=t.originalEvent.shiftKey?e-n:e+n;"center"===i.options.doubleClickZoom?i.setZoom(o):i.setZoomAround(t.containerPoint,o)}});be.addInitHook("addHandler","doubleClickZoom",Tn),be.mergeOptions({dragging:!0,inertia:!Mi,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0});var zn=Ee.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new Re(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),t.on("zoomend",this._onZoomEnd,this),t.whenReady(this._onZoomEnd,this))}Q(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){tt(this._map._container,"leaflet-grab"),tt(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var t=this._map;if(t._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity){var i=z(this._map.options.maxBounds);this._offsetLimit=b(this._map.latLngToContainerPoint(i.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(i.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))}else this._offsetLimit=null;t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){if(this._map.options.inertia){var i=this._lastTime=+new Date,e=this._lastPos=this._draggable._absPos||this._draggable._newPos;this._positions.push(e),this._times.push(i),this._prunePositions(i)}this._map.fire("move",t).fire("drag",t)},_prunePositions:function(t){for(;this._positions.length>1&&t-this._times[0]>50;)this._positions.shift(),this._times.shift()},_onZoomEnd:function(){var t=this._map.getSize().divideBy(2),i=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=i.subtract(t).x,this._worldWidth=this._map.getPixelWorldBounds().getSize().x},_viscousLimit:function(t,i){return t-(t-i)*this._viscosity},_onPreDragLimit:function(){if(this._viscosity&&this._offsetLimit){var t=this._draggable._newPos.subtract(this._draggable._startPos),i=this._offsetLimit;t.x<i.min.x&&(t.x=this._viscousLimit(t.x,i.min.x)),t.y<i.min.y&&(t.y=this._viscousLimit(t.y,i.min.y)),t.x>i.max.x&&(t.x=this._viscousLimit(t.x,i.max.x)),t.y>i.max.y&&(t.y=this._viscousLimit(t.y,i.max.y)),this._draggable._newPos=this._draggable._startPos.add(t)}},_onPreDragWrap:function(){var t=this._worldWidth,i=Math.round(t/2),e=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-i+e)%t+i-e,s=(n+i+e)%t-i-e,r=Math.abs(o+e)<Math.abs(s+e)?o:s;this._draggable._absPos=this._draggable._newPos.clone(),this._draggable._newPos.x=r},_onDragEnd:function(t){var i=this._map,e=i.options,n=!e.inertia||this._times.length<2;if(i.fire("dragend",t),n)i.fire("moveend");else{this._prunePositions(+new Date);var o=this._lastPos.subtract(this._positions[0]),s=(this._lastTime-this._times[0])/1e3,r=e.easeLinearity,a=o.multiplyBy(r/s),h=a.distanceTo([0,0]),u=Math.min(e.inertiaMaxSpeed,h),l=a.multiplyBy(u/h),c=u/(e.inertiaDeceleration*r),_=l.multiplyBy(-c/2).round();_.x||_.y?(_=i._limitOffset(_,i.options.maxBounds),f(function(){i.panBy(_,{duration:c,easeLinearity:r,noMoveStart:!0,animate:!0})})):i.fire("moveend")}}});be.addInitHook("addHandler","dragging",zn),be.mergeOptions({keyboard:!0,keyboardPanDelta:80});var Mn=Ee.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61,171],zoomOut:[189,109,54,173]},initialize:function(t){this._map=t,this._setPanDelta(t.options.keyboardPanDelta),this._setZoomDelta(t.options.zoomDelta)},addHooks:function(){var t=this._map._container;t.tabIndex<=0&&(t.tabIndex="0"),mt(t,{focus:this._onFocus,blur:this._onBlur,mousedown:this._onMouseDown},this),this._map.on({focus:this._addHooks,blur:this._removeHooks},this)},removeHooks:function(){this._removeHooks(),ft(this._map._container,{focus:this._onFocus,blur:this._onBlur,mousedown:this._onMouseDown},this),this._map.off({focus:this._addHooks,blur:this._removeHooks},this)},_onMouseDown:function(){if(!this._focused){var t=document.body,i=document.documentElement,e=t.scrollTop||i.scrollTop,n=t.scrollLeft||i.scrollLeft;this._map._container.focus(),window.scrollTo(n,e)}},_onFocus:function(){this._focused=!0,this._map.fire("focus")},_onBlur:function(){this._focused=!1,this._map.fire("blur")},_setPanDelta:function(t){var i,e,n=this._panKeys={},o=this.keyCodes;for(i=0,e=o.left.length;i<e;i++)n[o.left[i]]=[-1*t,0];for(i=0,e=o.right.length;i<e;i++)n[o.right[i]]=[t,0];for(i=0,e=o.down.length;i<e;i++)n[o.down[i]]=[0,t];for(i=0,e=o.up.length;i<e;i++)n[o.up[i]]=[0,-1*t]},_setZoomDelta:function(t){var i,e,n=this._zoomKeys={},o=this.keyCodes;for(i=0,e=o.zoomIn.length;i<e;i++)n[o.zoomIn[i]]=t;for(i=0,e=o.zoomOut.length;i<e;i++)n[o.zoomOut[i]]=-t},_addHooks:function(){mt(document,"keydown",this._onKeyDown,this)},_removeHooks:function(){ft(document,"keydown",this._onKeyDown,this)},_onKeyDown:function(t){if(!(t.altKey||t.ctrlKey||t.metaKey)){var i,e=t.keyCode,n=this._map;if(e in this._panKeys)n._panAnim&&n._panAnim._inProgress||(i=this._panKeys[e],t.shiftKey&&(i=w(i).multiplyBy(3)),n.panBy(i),n.options.maxBounds&&n.panInsideBounds(n.options.maxBounds));else if(e in this._zoomKeys)n.setZoom(n.getZoom()+(t.shiftKey?3:1)*this._zoomKeys[e]);else{if(27!==e||!n._popup||!n._popup.options.closeOnEscapeKey)return;n.closePopup()}Lt(t)}}});be.addInitHook("addHandler","keyboard",Mn),be.mergeOptions({scrollWheelZoom:!0,wheelDebounceTime:40,wheelPxPerZoomLevel:60});var Cn=Ee.extend({addHooks:function(){mt(this._map._container,"mousewheel",this._onWheelScroll,this),this._delta=0},removeHooks:function(){ft(this._map._container,"mousewheel",this._onWheelScroll,this)},_onWheelScroll:function(t){var i=Tt(t),n=this._map.options.wheelDebounceTime;this._delta+=i,this._lastMousePos=this._map.mouseEventToContainerPoint(t),this._startTime||(this._startTime=+new Date);var o=Math.max(n-(+new Date-this._startTime),0);clearTimeout(this._timer),this._timer=setTimeout(e(this._performZoom,this),o),Lt(t)},_performZoom:function(){var t=this._map,i=t.getZoom(),e=this._map.options.zoomSnap||0;t._stop();var n=this._delta/(4*this._map.options.wheelPxPerZoomLevel),o=4*Math.log(2/(1+Math.exp(-Math.abs(n))))/Math.LN2,s=e?Math.ceil(o/e)*e:o,r=t._limitZoom(i+(this._delta>0?s:-s))-i;this._delta=0,this._startTime=null,r&&("center"===t.options.scrollWheelZoom?t.setZoom(i+r):t.setZoomAround(this._lastMousePos,i+r))}});be.addInitHook("addHandler","scrollWheelZoom",Cn),be.mergeOptions({tap:!0,tapTolerance:15});var Sn=Ee.extend({addHooks:function(){mt(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){ft(this._map._container,"touchstart",this._onDown,this)},_onDown:function(t){if(t.touches){if(Pt(t),this._fireClick=!0,t.touches.length>1)return this._fireClick=!1,void clearTimeout(this._holdTimeout);var i=t.touches[0],n=i.target;this._startPos=this._newPos=new x(i.clientX,i.clientY),n.tagName&&"a"===n.tagName.toLowerCase()&&Q(n,"leaflet-active"),this._holdTimeout=setTimeout(e(function(){this._isTapValid()&&(this._fireClick=!1,this._onUp(),this._simulateEvent("contextmenu",i))},this),1e3),this._simulateEvent("mousedown",i),mt(document,{touchmove:this._onMove,touchend:this._onUp},this)}},_onUp:function(t){if(clearTimeout(this._holdTimeout),ft(document,{touchmove:this._onMove,touchend:this._onUp},this),this._fireClick&&t&&t.changedTouches){var i=t.changedTouches[0],e=i.target;e&&e.tagName&&"a"===e.tagName.toLowerCase()&&tt(e,"leaflet-active"),this._simulateEvent("mouseup",i),this._isTapValid()&&this._simulateEvent("click",i)}},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_onMove:function(t){var i=t.touches[0];this._newPos=new x(i.clientX,i.clientY),this._simulateEvent("mousemove",i)},_simulateEvent:function(t,i){var e=document.createEvent("MouseEvents");e._simulated=!0,i.target._simulatedClick=!0,e.initMouseEvent(t,!0,!0,window,1,i.screenX,i.screenY,i.clientX,i.clientY,!1,!1,!1,!1,0,null),i.target.dispatchEvent(e)}});qi&&!Vi&&be.addInitHook("addHandler","tap",Sn),be.mergeOptions({touchZoom:qi&&!Mi,bounceAtZoomLimits:!0});var Zn=Ee.extend({addHooks:function(){Q(this._map._container,"leaflet-touch-zoom"),mt(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){tt(this._map._container,"leaflet-touch-zoom"),ft(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(t){var i=this._map;if(t.touches&&2===t.touches.length&&!i._animatingZoom&&!this._zooming){var e=i.mouseEventToContainerPoint(t.touches[0]),n=i.mouseEventToContainerPoint(t.touches[1]);this._centerPoint=i.getSize()._divideBy(2),this._startLatLng=i.containerPointToLatLng(this._centerPoint),"center"!==i.options.touchZoom&&(this._pinchStartLatLng=i.containerPointToLatLng(e.add(n)._divideBy(2))),this._startDist=e.distanceTo(n),this._startZoom=i.getZoom(),this._moved=!1,this._zooming=!0,i._stop(),mt(document,"touchmove",this._onTouchMove,this),mt(document,"touchend",this._onTouchEnd,this),Pt(t)}},_onTouchMove:function(t){if(t.touches&&2===t.touches.length&&this._zooming){var i=this._map,n=i.mouseEventToContainerPoint(t.touches[0]),o=i.mouseEventToContainerPoint(t.touches[1]),s=n.distanceTo(o)/this._startDist;if(this._zoom=i.getScaleZoom(s,this._startZoom),!i.options.bounceAtZoomLimits&&(this._zoom<i.getMinZoom()&&s<1||this._zoom>i.getMaxZoom()&&s>1)&&(this._zoom=i._limitZoom(this._zoom)),"center"===i.options.touchZoom){if(this._center=this._startLatLng,1===s)return}else{var r=n._add(o)._divideBy(2)._subtract(this._centerPoint);if(1===s&&0===r.x&&0===r.y)return;this._center=i.unproject(i.project(this._pinchStartLatLng,this._zoom).subtract(r),this._zoom)}this._moved||(i._moveStart(!0,!1),this._moved=!0),g(this._animRequest);var a=e(i._move,i,this._center,this._zoom,{pinch:!0,round:!1});this._animRequest=f(a,this,!0),Pt(t)}},_onTouchEnd:function(){this._moved&&this._zooming?(this._zooming=!1,g(this._animRequest),ft(document,"touchmove",this._onTouchMove),ft(document,"touchend",this._onTouchEnd),this._map.options.zoomAnimation?this._map._animateZoom(this._center,this._map._limitZoom(this._zoom),!0,this._map.options.zoomSnap):this._map._resetView(this._center,this._map._limitZoom(this._zoom))):this._zooming=!1}});be.addInitHook("addHandler","touchZoom",Zn),be.BoxZoom=bn,be.DoubleClickZoom=Tn,be.Drag=zn,be.Keyboard=Mn,be.ScrollWheelZoom=Cn,be.Tap=Sn,be.TouchZoom=Zn,Object.freeze=ti,t.version="1.3.4+HEAD.0e566b2",t.Control=Te,t.control=ze,t.Browser=Qi,t.Evented=ci,t.Mixin=Ae,t.Util=ui,t.Class=v,t.Handler=Ee,t.extend=i,t.bind=e,t.stamp=n,t.setOptions=l,t.DomEvent=Pe,t.DomUtil=ve,t.PosAnimation=Le,t.Draggable=Re,t.LineUtil=Ne,t.PolyUtil=De,t.Point=x,t.point=w,t.Bounds=P,t.bounds=b,t.Transformation=S,t.transformation=Z,t.Projection=He,t.LatLng=M,t.latLng=C,t.LatLngBounds=T,t.latLngBounds=z,t.CRS=di,t.GeoJSON=sn,t.geoJSON=Xt,t.geoJson=an,t.Layer=qe,t.LayerGroup=Ge,t.layerGroup=function(t,i){return new Ge(t,i)},t.FeatureGroup=Ke,t.featureGroup=function(t){return new Ke(t)},t.ImageOverlay=hn,t.imageOverlay=function(t,i,e){return new hn(t,i,e)},t.VideoOverlay=un,t.videoOverlay=function(t,i,e){return new un(t,i,e)},t.DivOverlay=ln,t.Popup=cn,t.popup=function(t,i){return new cn(t,i)},t.Tooltip=_n,t.tooltip=function(t,i){return new _n(t,i)},t.Icon=Ye,t.icon=function(t){return new Ye(t)},t.DivIcon=dn,t.divIcon=function(t){return new dn(t)},t.Marker=$e,t.marker=function(t,i){return new $e(t,i)},t.TileLayer=mn,t.tileLayer=Jt,t.GridLayer=pn,t.gridLayer=function(t){return new pn(t)},t.SVG=Pn,t.svg=Qt,t.Renderer=gn,t.Canvas=vn,t.canvas=$t,t.Path=Qe,t.CircleMarker=tn,t.circleMarker=function(t,i){return new tn(t,i)},t.Circle=en,t.circle=function(t,i,e){return new en(t,i,e)},t.Polyline=nn,t.polyline=function(t,i){return new nn(t,i)},t.Polygon=on,t.polygon=function(t,i){return new on(t,i)},t.Rectangle=Ln,t.rectangle=function(t,i){return new Ln(t,i)},t.Map=be,t.map=function(t,i){return new be(t,i)};var En=window.L;t.noConflict=function(){return window.L=En,this},window.L=t}); \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/resources/maps.common.js b/www/wiki/extensions/Maps/resources/maps.common.js
new file mode 100644
index 00000000..8e0180b4
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/maps.common.js
@@ -0,0 +1,4 @@
+window.maps = new ( function() {
+ this.googlemapsList = [];
+ this.leafletList = [];
+} )();
diff --git a/www/wiki/extensions/Maps/resources/maps.services.js b/www/wiki/extensions/Maps/resources/maps.services.js
new file mode 100644
index 00000000..a23ff0c8
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/maps.services.js
@@ -0,0 +1,86 @@
+/*global jQuery, mediaWiki, maps */
+/*global confirm */
+( function ( $, mw, maps ) {
+ 'use strict';
+
+ /**
+ * @since 3.5
+ *
+ * @param {object} container
+ * @return {this}
+ */
+ var services = function ( container ) {
+
+ if ( $.type( container ) !== 'object' ) {
+ throw new Error( 'The container is not of the correct type ' + $.type( container ) );
+ }
+
+ this.container = container;
+
+ return this;
+ };
+
+ /* Public methods */
+
+ services.prototype = {
+
+ constructor: services,
+
+ /**
+ * @since 3.5
+ *
+ * @param {string} service
+ */
+ render: function( service ) {
+ if ( service === 'googlemaps' || service === 'maps' || service === 'googlemaps3' ) {
+ this.google();
+ }
+
+ if ( service === 'leaflet' || service === 'leafletmaps' ) {
+ this.leaflet();
+ }
+ },
+
+ /**
+ * Google service
+ *
+ * @since 3.5
+ */
+ google: function() {
+
+ var self = this;
+
+ // https://www.mediawiki.org/wiki/ResourceLoader/Modules#mw.loader.using
+ mw.loader.using( 'ext.maps.googlemaps3' ).done( function () {
+
+ if ( typeof google === 'undefined' ) {
+ throw new Error( 'The google map service is unknown, please ensure that the API or module is loaded correctly.' );
+ }
+
+ self.container.find( '.maps-googlemaps3' ).each( function() {
+ var $this = $( this );
+ $this.googlemaps( $.parseJSON( $this.find( 'div').text() ) );
+ } );
+ } );
+ },
+
+ /**
+ * Leaflet service
+ *
+ * @since 3.5
+ */
+ leaflet: function() {
+ mw.loader.using( 'ext.maps.leaflet' ).done( function () {
+ $( '.maps-leaflet' ).each( function() {
+ var $this = $( this );
+ maps.leafletList.push(
+ $this.leafletmaps( $.parseJSON( $this.find( 'div').text() ) )
+ );
+ } );
+ } );
+ }
+ };
+
+ maps.services = services;
+
+}( jQuery, mediaWiki, maps ) );
diff --git a/www/wiki/extensions/Maps/resources/sm.common.js b/www/wiki/extensions/Maps/resources/sm.common.js
new file mode 100644
index 00000000..373c1dd9
--- /dev/null
+++ b/www/wiki/extensions/Maps/resources/sm.common.js
@@ -0,0 +1,76 @@
+/**
+ * JavaScript the Semantic Maps extension.
+ * @see https://www.mediawiki.org/wiki/Extension:Semantic_Maps
+ *
+ * @licence GNU GPL v2++
+ * @author Peter Grassberger < petertheone@gmail.com >
+ */
+window.sm = new ( function( $, mw ) {
+
+ this.buildQueryString = function( query, ajaxcoordproperty, top, right, bottom, left ) {
+ var isCompoundQuery = query.indexOf( '|' ) > -1;
+ var query = query.split( '|' );
+ $.each( query, function( index ) {
+ query[index] += ' [[' + ajaxcoordproperty + '::+]] ';
+ query[index] += '[[' + ajaxcoordproperty + '::>' + bottom + '°, ' + left + '°]] ';
+ query[index] += '[[' + ajaxcoordproperty + '::<' + top + '°, ' + right + '°]]';
+ if( !isCompoundQuery ) {
+ query[index] += '|?' + ajaxcoordproperty;
+ } else {
+ query[index] += ';?' + ajaxcoordproperty;
+ }
+ } );
+ return query.join( ' | ' );
+ };
+
+ /**
+ * Detects semicolons `;` not in square brackets `[]`.
+ *
+ * @param string
+ * @returns {boolean}
+ */
+ this.hasCompoundQuerySemicolon = function( string ) {
+ return /;(?![^[]*])/g.test( string );
+ };
+
+ this.sendQuery = function( query ) {
+ var action = this.hasCompoundQuerySemicolon( query ) ? 'compoundquery' : 'ask';
+ return $.ajax( {
+ method: 'GET',
+ url: mw.util.wikiScript( 'api' ),
+ data: {
+ 'action': action,
+ 'query': query,
+ 'format': 'json'
+ },
+ dataType: 'json'
+ } );
+ };
+
+ this.ajaxUpdateMarker = function( map, query, icon ) {
+ return this.sendQuery( query ).done( function( data ) {
+ if( !data.hasOwnProperty( 'query' ) ||
+ !data.query.hasOwnProperty( 'results' ) ) {
+ return;
+ }
+ // todo: don't remove and recreate all markers..
+ // only add new ones.
+ map.removeMarkers();
+ for( var property in data.query.results ) {
+ if( data.query.results.hasOwnProperty( property ) ) {
+ var location = data.query.results[property];
+ var coordinates = location.printouts[map.options.ajaxcoordproperty][0];
+ var markerOptions = {
+ lat: coordinates.lat,
+ lon: coordinates.lon,
+ title: location.fulltext,
+ text: '<b><a href="' + location.fullurl + '">' + location.fulltext + '</a></b>',
+ icon: icon
+ };
+ map.addMarker( markerOptions );
+ }
+ }
+ } );
+ };
+
+} )( jQuery, mediaWiki );
diff --git a/www/wiki/extensions/Maps/src/DataAccess/CachingGeocoder.php b/www/wiki/extensions/Maps/src/DataAccess/CachingGeocoder.php
new file mode 100644
index 00000000..b27bbc92
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/DataAccess/CachingGeocoder.php
@@ -0,0 +1,46 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Maps\DataAccess;
+
+use BagOStuff;
+use DataValues\Geo\Values\LatLongValue;
+use Jeroen\SimpleGeocoder\Geocoder;
+
+/**
+ * @since 5.0
+ *
+ * @licence GNU GPL v2+
+ * @author HgO < hgo@batato.be >
+ */
+class CachingGeocoder implements Geocoder {
+
+ private $geocoder;
+ private $cache;
+ private $cacheTtl;
+
+ public function __construct( Geocoder $geocoder, BagOStuff $cache, int $cacheTtl ) {
+ $this->geocoder = $geocoder;
+ $this->cache = $cache;
+ $this->cacheTtl = $cacheTtl;
+ }
+
+ /**
+ * @return LatLongValue|null
+ */
+ public function geocode( string $address ) {
+ $key = $this->cache->makeKey( __CLASS__, $address );
+
+ $coordinates = $this->cache->get( $key );
+
+ // There was no entry in the cache, so we retrieve the coordinates
+ if ( $coordinates === false ) {
+ $coordinates = $this->geocoder->geocode( $address );
+
+ $this->cache->set( $key, $coordinates, $this->cacheTtl );
+ }
+
+ return $coordinates;
+ }
+}
diff --git a/www/wiki/extensions/Maps/src/DataAccess/JsonFileParser.php b/www/wiki/extensions/Maps/src/DataAccess/JsonFileParser.php
new file mode 100644
index 00000000..81d6cfa0
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/DataAccess/JsonFileParser.php
@@ -0,0 +1,79 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Maps\DataAccess;
+
+use FileFetcher\FileFetcher;
+use FileFetcher\FileFetchingException;
+use Maps\MapsFactory;
+use ValueParsers\ParseException;
+use ValueParsers\ValueParser;
+
+/**
+ * Returns the content of the JSON file at the specified location as array.
+ * Empty array is returned on failure.
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class JsonFileParser implements ValueParser {
+
+ private $fileFetcher;
+ private $pageContentFetcher;
+ private $defaultNamespace;
+
+ public function __construct( $fileFetcher = null, PageContentFetcher $pageContentFetcher = null ) {
+ $this->fileFetcher = $fileFetcher instanceof FileFetcher
+ ? $fileFetcher : MapsFactory::newDefault()->getGeoJsonFileFetcher();
+
+ $this->pageContentFetcher = $pageContentFetcher instanceof PageContentFetcher
+ ? $pageContentFetcher : MapsFactory::newDefault()->getPageContentFetcher();
+
+ $this->defaultNamespace = NS_GEO_JSON;
+ }
+
+ /**
+ * @param string $fileLocation
+ *
+ * @return array
+ * @throws ParseException
+ */
+ public function parse( $fileLocation ) {
+ $jsonString = $this->getJsonString( $fileLocation );
+
+ if ( $jsonString === null ) {
+ return [];
+ }
+
+ $json = json_decode( $jsonString, true );
+
+ if ( $json === null ) {
+ return [];
+ }
+
+ return $json;
+ }
+
+ private function getJsonString( string $fileLocation ): ?string {
+ $content = $this->pageContentFetcher->getPageContent( $fileLocation, $this->defaultNamespace );
+
+ if ( $content instanceof \JsonContent ) {
+ return $content->getNativeData();
+ }
+
+ // Prevent reading JSON files on the server
+ if( !filter_var( $fileLocation, FILTER_VALIDATE_URL) ) {
+ return null;
+ }
+
+ try {
+ return $this->fileFetcher->fetchFile( $fileLocation );
+ }
+ catch ( FileFetchingException $ex ) {
+ return null;
+ }
+ }
+
+
+}
diff --git a/www/wiki/extensions/Maps/src/DataAccess/MapsFileFetcher.php b/www/wiki/extensions/Maps/src/DataAccess/MapsFileFetcher.php
new file mode 100644
index 00000000..79d7f07f
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/DataAccess/MapsFileFetcher.php
@@ -0,0 +1,26 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Maps\DataAccess;
+
+use FileFetcher\FileFetcher;
+use FileFetcher\FileFetchingException;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class MapsFileFetcher implements FileFetcher {
+
+ public function fetchFile( string $fileUrl ): string {
+ $result = \Http::get( $fileUrl );
+
+ if ( !is_string( $result ) ) {
+ throw new FileFetchingException( $fileUrl );
+ }
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/DataAccess/MediaWikiFileUrlFinder.php b/www/wiki/extensions/Maps/src/DataAccess/MediaWikiFileUrlFinder.php
new file mode 100644
index 00000000..aabb5f38
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/DataAccess/MediaWikiFileUrlFinder.php
@@ -0,0 +1,31 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Maps\DataAccess;
+
+use ImagePage;
+use Maps\FileUrlFinder;
+use Title;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class MediaWikiFileUrlFinder implements FileUrlFinder {
+
+ public function getUrlForFileName( string $fileName ): string {
+ $colonPosition = strpos( $fileName, ':' );
+
+ $titleWithoutPrefix = $colonPosition === false ? $fileName : substr( $fileName, $colonPosition + 1 );
+
+ $title = Title::newFromText( trim( $titleWithoutPrefix ), NS_FILE );
+
+ if ( $title !== null && $title->exists() ) {
+ return ( new ImagePage( $title ) )->getDisplayedFile()->getURL();
+ }
+
+ return trim( $fileName );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/DataAccess/PageContentFetcher.php b/www/wiki/extensions/Maps/src/DataAccess/PageContentFetcher.php
new file mode 100644
index 00000000..514cfe0a
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/DataAccess/PageContentFetcher.php
@@ -0,0 +1,40 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Maps\DataAccess;
+
+use MediaWiki\Storage\RevisionLookup;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class PageContentFetcher {
+
+ private $titleParser;
+ private $revisionLookup;
+
+ public function __construct( \TitleParser $titleParser, RevisionLookup $revisionLookup ) {
+ $this->titleParser = $titleParser;
+ $this->revisionLookup = $revisionLookup;
+ }
+
+ public function getPageContent( string $pageTitle, int $defaultNamespace = NS_MAIN ): ?\Content {
+ try {
+ $title = $this->titleParser->parseTitle( $pageTitle, $defaultNamespace );
+ }
+ catch ( \MalformedTitleException $e ) {
+ return null;
+ }
+
+ $revision = $this->revisionLookup->getRevisionByTitle( $title );
+
+ if ( $revision === null ) {
+ return null;
+ }
+
+ return $revision->getContent( 'main' );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Elements/BaseElement.php b/www/wiki/extensions/Maps/src/Elements/BaseElement.php
new file mode 100644
index 00000000..2e36c3b4
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Elements/BaseElement.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Maps\Elements;
+
+/**
+ * @since 3.0
+ *
+ * @licence GNU GPL v2+
+ * @author Kim Eik < kim@heldig.org >
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+abstract class BaseElement {
+
+ private $title;
+ private $text;
+ private $link;
+
+ public function setTitle( string $title ) {
+ $this->title = trim( $title );
+ }
+
+ public function setText( string $text ) {
+ $this->text = trim( $text );
+ }
+
+ public function setLink( string $link ) {
+ $this->link = $link;
+ }
+
+ public function getArrayValue() {
+ return $this->getJSONObject();
+ }
+
+ /**
+ * @deprecated
+ */
+ public function getJSONObject( string $defText = '', string $defTitle = '' ): array {
+ return [
+ 'text' => $this->text ?? $defText,
+ 'title' => $this->title ?? $defTitle,
+ 'link' => $this->link ?? '',
+ ];
+ }
+
+ public function getText(): string {
+ return $this->text ?? '';
+ }
+
+ public function getTitle(): string {
+ return $this->title ?? '';
+ }
+
+ public function getLink(): string {
+ return $this->link ?? '';
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Elements/BaseFillableElement.php b/www/wiki/extensions/Maps/src/Elements/BaseFillableElement.php
new file mode 100644
index 00000000..cf740fc8
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Elements/BaseFillableElement.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Maps\Elements;
+
+/**
+ * @since 2.0
+ */
+class BaseFillableElement extends BaseStrokableElement {
+
+ protected $fillColor;
+ protected $fillOpacity;
+
+ public function getJSONObject( string $defText = '', string $defTitle = '' ): array {
+ $parentArray = parent::getJSONObject( $defText, $defTitle );
+ $array = [
+ 'fillColor' => $this->hasFillColor() ? $this->getFillColor() : '#FF0000',
+ 'fillOpacity' => $this->hasFillOpacity() ? $this->getFillOpacity() : '0.5',
+ ];
+ return array_merge( $parentArray, $array );
+ }
+
+ public function hasFillColor() {
+ return !is_null( $this->fillColor ) && $this->fillColor !== '';
+ }
+
+ public function getFillColor() {
+ return $this->fillColor;
+ }
+
+ public function setFillColor( $fillColor ) {
+ $this->fillColor = trim( $fillColor );
+ }
+
+ public function hasFillOpacity() {
+ return !is_null( $this->fillOpacity ) && $this->fillOpacity !== '';
+ }
+
+ public function getFillOpacity() {
+ return $this->fillOpacity;
+ }
+
+ public function setFillOpacity( $fillOpacity ) {
+ $this->fillOpacity = trim( $fillOpacity );
+ }
+}
diff --git a/www/wiki/extensions/Maps/src/Elements/BaseStrokableElement.php b/www/wiki/extensions/Maps/src/Elements/BaseStrokableElement.php
new file mode 100644
index 00000000..79befa2f
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Elements/BaseStrokableElement.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace Maps\Elements;
+
+/**
+ * @since 2.0
+ *
+ * @licence GNU GPL v2+
+ * @author Kim Eik < kim@heldig.org >
+ */
+class BaseStrokableElement extends BaseElement {
+
+ protected $strokeColor;
+ protected $strokeOpacity;
+ protected $strokeWeight;
+
+ public function getJSONObject( string $defText = '', string $defTitle = '' ): array {
+ $parentArray = parent::getJSONObject( $defText, $defTitle );
+ $array = [
+ 'strokeColor' => $this->hasStrokeColor() ? $this->getStrokeColor() : '#FF0000',
+ 'strokeOpacity' => $this->hasStrokeOpacity() ? $this->getStrokeOpacity() : '1',
+ 'strokeWeight' => $this->hasStrokeWeight() ? $this->getStrokeWeight() : '2'
+ ];
+ return array_merge( $parentArray, $array );
+ }
+
+ public function hasStrokeColor() {
+ return !is_null( $this->strokeColor ) && $this->strokeColor !== '';
+ }
+
+ public function getStrokeColor() {
+ return $this->strokeColor;
+ }
+
+ public function setStrokeColor( $strokeColor ) {
+ $this->strokeColor = trim( $strokeColor );
+ }
+
+ public function hasStrokeOpacity() {
+ return !is_null( $this->strokeOpacity ) && $this->strokeOpacity !== '';
+ }
+
+ public function getStrokeOpacity() {
+ return $this->strokeOpacity;
+ }
+
+ public function setStrokeOpacity( $strokeOpacity ) {
+ $this->strokeOpacity = trim( $strokeOpacity );
+ }
+
+ public function hasStrokeWeight() {
+ return !is_null( $this->strokeWeight ) && $this->strokeWeight !== '';
+ }
+
+ public function getStrokeWeight() {
+ return $this->strokeWeight;
+ }
+
+ public function setStrokeWeight( $strokeWeight ) {
+ $this->strokeWeight = trim( $strokeWeight );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Elements/Circle.php b/www/wiki/extensions/Maps/src/Elements/Circle.php
new file mode 100644
index 00000000..5b5f9172
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Elements/Circle.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Maps\Elements;
+
+use DataValues\Geo\Values\LatLongValue;
+use InvalidArgumentException;
+
+/**
+ * @since 3.0
+ *
+ * @licence GNU GPL v2+
+ * @author Kim Eik < kim@heldig.org >
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class Circle extends \Maps\Elements\BaseFillableElement {
+
+ private $circleCentre;
+ private $circleRadius;
+
+ public function __construct( LatLongValue $circleCentre, float $circleRadius ) {
+ if ( !is_float( $circleRadius ) && !is_int( $circleRadius ) ) {
+ throw new InvalidArgumentException( '$circleRadius must be a float or int' );
+ }
+
+ if ( $circleRadius <= 0 ) {
+ throw new InvalidArgumentException( '$circleRadius must be greater than zero' );
+ }
+
+ $this->setCircleCentre( $circleCentre );
+ $this->setCircleRadius( $circleRadius );
+ }
+
+ public function getJSONObject( string $defText = '', string $defTitle = '' ): array {
+ return array_merge(
+ parent::getJSONObject( $defText, $defTitle ),
+ [
+ 'centre' => [
+ 'lon' => $this->getCircleCentre()->getLongitude(),
+ 'lat' => $this->getCircleCentre()->getLatitude()
+ ],
+ 'radius' => intval( $this->getCircleRadius() ),
+ ]
+ );
+ }
+
+ public function getCircleCentre(): LatLongValue {
+ return $this->circleCentre;
+ }
+
+ public function setCircleCentre( LatLongValue $circleCentre ) {
+ $this->circleCentre = $circleCentre;
+ }
+
+ public function getCircleRadius(): float {
+ return $this->circleRadius;
+ }
+
+ public function setCircleRadius( float $circleRadius ) {
+ $this->circleRadius = $circleRadius;
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Elements/ImageOverlay.php b/www/wiki/extensions/Maps/src/Elements/ImageOverlay.php
new file mode 100644
index 00000000..4e48f098
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Elements/ImageOverlay.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Maps\Elements;
+
+use DataValues\Geo\Values\LatLongValue;
+
+/**
+ * @since 3.0
+ *
+ * @licence GNU GPL v2+
+ * @author Kim Eik < kim@heldig.org >
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class ImageOverlay extends Rectangle {
+
+ private $imageUrl;
+
+ public function __construct( LatLongValue $boundsNorthEast, LatLongValue $boundsSouthWest, string $image ) {
+ parent::__construct( $boundsNorthEast, $boundsSouthWest );
+
+ $this->imageUrl = $image;
+ }
+
+ public function getImage(): string {
+ return $this->imageUrl;
+ }
+
+ public function getJSONObject( string $defText = '', string $defTitle = '' ): array {
+ $data = parent::getJSONObject( $defText, $defTitle );
+
+ $data['image'] = $this->imageUrl;
+
+ return $data;
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Elements/Line.php b/www/wiki/extensions/Maps/src/Elements/Line.php
new file mode 100644
index 00000000..2ea4ccda
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Elements/Line.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Maps\Elements;
+
+use DataValues\Geo\Values\LatLongValue;
+use InvalidArgumentException;
+
+/**
+ * Class representing a collection of LatLongValue objects forming a line.
+ *
+ * @since 3.0
+ *
+ *
+ * @licence GNU GPL v2+
+ * @author Kim Eik < kim@heldig.org >
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class Line extends \Maps\Elements\BaseStrokableElement {
+
+ /**
+ * @since 3.0
+ *
+ * @var LatLongValue[]
+ */
+ protected $coordinates;
+
+ /**
+ * @since 3.0
+ *
+ * @param LatLongValue[] $coordinates
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct( array $coordinates = [] ) {
+ foreach ( $coordinates as $coordinate ) {
+ if ( !( $coordinate instanceof LatLongValue ) ) {
+ throw new InvalidArgumentException( 'Can only construct Line with LatLongValue objects' );
+ }
+ }
+
+ $this->coordinates = $coordinates;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return LatLongValue[]
+ */
+ public function getLineCoordinates() {
+ return $this->coordinates;
+ }
+
+ public function getJSONObject( string $defText = '', string $defTitle = '' ): array {
+ $parentArray = parent::getJSONObject( $defText, $defTitle );
+ $posArray = [];
+
+ foreach ( $this->coordinates as $mapLocation ) {
+ $posArray[] = [
+ 'lat' => $mapLocation->getLatitude(),
+ 'lon' => $mapLocation->getLongitude()
+ ];
+ }
+
+ $posArray = [ 'pos' => $posArray ];
+
+ return array_merge( $parentArray, $posArray );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Elements/Location.php b/www/wiki/extensions/Maps/src/Elements/Location.php
new file mode 100644
index 00000000..6ea8c4be
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Elements/Location.php
@@ -0,0 +1,157 @@
+<?php
+
+namespace Maps\Elements;
+
+use DataValues\Geo\Values\LatLongValue;
+
+/**
+ * Class describing a single location (geographical point).
+ *
+ * TODO: rethink the design of this class after deciding on what actual role it has
+ *
+ * @since 3.0
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author Daniel Werner
+ */
+class Location extends BaseElement {
+
+ /**
+ * @var LatLongValue
+ */
+ private $coordinates;
+
+ /**
+ * @var string
+ */
+ private $address;
+
+ /**
+ * @var string
+ */
+ private $icon = '';
+
+ /**
+ * @var string
+ */
+ private $group = '';
+
+ /**
+ * @var string
+ */
+ private $inlineLabel = '';
+
+ /**
+ * @var string
+ */
+ private $visitedIcon = '';
+
+ public function __construct( LatLongValue $coordinates, string $title = '', string $text = '' ) {
+ $this->coordinates = $coordinates;
+ $this->setTitle( $title );
+ $this->setText( $text );
+ }
+
+ public static function newFromLatLon( float $lat, float $lon ): self {
+ return new self( new LatLongValue( $lat, $lon ) );
+ }
+
+ public function getCoordinates(): LatLongValue {
+ return $this->coordinates;
+ }
+
+ public function getJSONObject( string $defText = '', string $defTitle = '', string $defIconUrl = '',
+ string $defGroup = '', string $defInlineLabel = '', string $defVisitedIcon = '' ): array {
+
+ $parentArray = parent::getJSONObject( $defText, $defTitle );
+
+ $array = [
+ 'lat' => $this->coordinates->getLatitude(),
+ 'lon' => $this->coordinates->getLongitude(),
+ 'icon' => $this->hasIcon() ? \Maps\MapsFunctions::getFileUrl( $this->getIcon() ) : $defIconUrl,
+ ];
+ $val = $this->getAddress();
+ if ( $val !== '' ) {
+ $array['address'] = $val;
+ }
+ $val = $this->hasGroup() ? $this->getGroup() : $defGroup;
+ if ( !empty( $val ) ) {
+ $array['group'] = $val;
+ }
+ $val = $this->hasInlineLabel() ? $this->getInlineLabel() : $defInlineLabel;
+ if ( !empty( $val ) ) {
+ $array['inlineLabel'] = $val;
+ }
+ $val = $this->hasVisitedIcon() ? $this->getVisitedIcon() : $defVisitedIcon;
+ if ( !empty( $val ) ) {
+ $array['visitedicon'] = $val;
+ }
+
+ return array_merge( $parentArray, $array );
+ }
+
+ public function hasIcon(): bool {
+ return $this->icon !== '';
+ }
+
+ public function getIcon(): string {
+ return $this->icon;
+ }
+
+ public function setIcon( string $icon ) {
+ $this->icon = $icon;
+ }
+
+ /**
+ * Returns the address corresponding to this location.
+ * If there is none, and empty sting is returned.
+ */
+ public function getAddress(): string {
+ if ( is_null( $this->address ) ) {
+ $this->address = '';
+ }
+
+ return $this->address;
+ }
+
+ /**
+ * Returns whether Location is assigned to a group.
+ */
+ public function hasGroup(): bool {
+ return $this->group !== '';
+ }
+
+ public function getGroup(): string {
+ return $this->group;
+ }
+
+ public function setGroup( string $group ) {
+ $this->group = trim( $group );
+ }
+
+ public function hasInlineLabel(): bool {
+ return $this->inlineLabel !== '';
+ }
+
+ public function getInlineLabel(): string {
+ return $this->inlineLabel;
+ }
+
+ public function setInlineLabel( string $label ) {
+ $this->inlineLabel = $label;
+ }
+
+ public function hasVisitedIcon(): bool {
+ return $this->visitedIcon !== '';
+ }
+
+ public function getVisitedIcon(): string {
+ return $this->visitedIcon;
+ }
+
+ public function setVisitedIcon( string $visitedIcon ) {
+ $this->visitedIcon = $visitedIcon;
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Elements/Polygon.php b/www/wiki/extensions/Maps/src/Elements/Polygon.php
new file mode 100644
index 00000000..bd7289ec
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Elements/Polygon.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Maps\Elements;
+
+/**
+ * @since 3.0
+ *
+ * @licence GNU GPL v2+
+ * @author Kim Eik < kim@heldig.org >
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class Polygon extends Line {
+
+ private $onlyVisibleOnHover = false;
+ private $fillOpacity = '0.5';
+ private $fillColor = '#FF0000';
+
+ public function isOnlyVisibleOnHover(): bool {
+ return $this->onlyVisibleOnHover;
+ }
+
+ public function setOnlyVisibleOnHover( bool $visible ) {
+ $this->onlyVisibleOnHover = $visible;
+ }
+
+ public function setFillOpacity( string $fillOpacity ) {
+ $this->fillOpacity = $fillOpacity;
+ }
+
+ public function setFillColor( string $fillColor ) {
+ $this->fillColor = $fillColor;
+ }
+
+ public function getJSONObject( string $defText = '', string $defTitle = '' ): array {
+ $json = parent::getJSONObject( $defText, $defTitle );
+
+ $json['onlyVisibleOnHover'] = $this->onlyVisibleOnHover;
+ $json['fillColor'] = $this->fillColor;
+ $json['fillOpacity'] = $this->fillOpacity;
+
+ return $json;
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Elements/Rectangle.php b/www/wiki/extensions/Maps/src/Elements/Rectangle.php
new file mode 100644
index 00000000..d8392f70
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Elements/Rectangle.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Maps\Elements;
+
+use DataValues\Geo\Values\LatLongValue;
+use InvalidArgumentException;
+
+/**
+ * @since 3.0
+ *
+ *
+ * @licence GNU GPL v2+
+ * @author Kim Eik < kim@heldig.org >
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class Rectangle extends \Maps\Elements\BaseFillableElement {
+
+ /**
+ * @since 3.0
+ * @var LatLongValue
+ */
+ protected $rectangleNorthEast;
+
+ /**
+ * @since 3.0
+ * @var LatLongValue
+ */
+ protected $rectangleSouthWest;
+
+ /**
+ * @since 3.0
+ *
+ * @param LatLongValue $rectangleNorthEast
+ * @param LatLongValue $rectangleSouthWest
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct( LatLongValue $rectangleNorthEast, LatLongValue $rectangleSouthWest ) {
+ if ( $rectangleNorthEast->equals( $rectangleSouthWest ) ) {
+ throw new InvalidArgumentException( '$rectangleNorthEast cannot be equal to $rectangleSouthWest' );
+ }
+
+ // TODO: validate bounds are correct, if not, flip
+ $this->setRectangleNorthEast( $rectangleNorthEast );
+ $this->setRectangleSouthWest( $rectangleSouthWest );
+ }
+
+ public function getJSONObject( string $defText = '', string $defTitle = '' ): array {
+ $parentArray = parent::getJSONObject( $defText, $defTitle );
+ $array = [
+ 'ne' => [
+ 'lon' => $this->getRectangleNorthEast()->getLongitude(),
+ 'lat' => $this->getRectangleNorthEast()->getLatitude()
+ ],
+ 'sw' => [
+ 'lon' => $this->getRectangleSouthWest()->getLongitude(),
+ 'lat' => $this->getRectangleSouthWest()->getLatitude()
+ ],
+ ];
+
+ return array_merge( $parentArray, $array );
+ }
+
+ public function getRectangleNorthEast(): LatLongValue {
+ return $this->rectangleNorthEast;
+ }
+
+ public function setRectangleNorthEast( LatLongValue $rectangleNorthEast ) {
+ $this->rectangleNorthEast = $rectangleNorthEast;
+ }
+
+ public function getRectangleSouthWest(): LatLongValue {
+ return $this->rectangleSouthWest;
+ }
+
+ public function setRectangleSouthWest( LatLongValue $rectangleSouthWest ) {
+ $this->rectangleSouthWest = $rectangleSouthWest;
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Elements/WmsOverlay.php b/www/wiki/extensions/Maps/src/Elements/WmsOverlay.php
new file mode 100644
index 00000000..fa3c95f9
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Elements/WmsOverlay.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace Maps\Elements;
+
+/**
+ * Class that holds metadata on WMS overlay layers on map
+ *
+ * @since 3.0
+ *
+ * @licence GNU GPL v2+
+ * @author Mathias Lidal < mathiaslidal@gmail.com >
+ */
+class WmsOverlay extends BaseElement {
+
+ /**
+ * @var String Base url to WMS server
+ */
+ private $wmsServerUrl;
+
+ /**
+ * @var String WMS Layer name
+ */
+ private $wmsLayerName;
+
+ /**
+ * @var String WMS Style name (default value: 'default')
+ */
+ private $wmsStyleName;
+
+ public function __construct( string $wmsServerUrl, string $wmsLayerName, string $wmsStyleName = "default" ) {
+ $this->setWmsServerUrl( $wmsServerUrl );
+ $this->setWmsLayerName( $wmsLayerName );
+ $this->setWmsStyleName( $wmsStyleName );
+ }
+
+ public function getJSONObject( string $defText = '', string $defTitle = '' ): array {
+ $parentArray = parent::getJSONObject( $defText, $defTitle );
+
+ $array = [
+ 'wmsServerUrl' => $this->getWmsServerUrl(),
+ 'wmsLayerName' => $this->getWmsLayerName(),
+ 'wmsStyleName' => $this->getWmsStyleName()
+ ];
+ return array_merge( $parentArray, $array );
+ }
+
+ public function getWmsServerUrl(): string {
+ return $this->wmsServerUrl;
+ }
+
+ public function setWmsServerUrl( string $wmsServerUrl ) {
+ $this->wmsServerUrl = $wmsServerUrl;
+ }
+
+ public function getWmsLayerName(): string {
+ return $this->wmsLayerName;
+ }
+
+ public function setWmsLayerName( string $wmsLayerName ) {
+ $this->wmsLayerName = $wmsLayerName;
+ }
+
+ public function getWmsStyleName(): string {
+ return $this->wmsStyleName;
+ }
+
+ public function setWmsStyleName( string $wmsStyleName ) {
+ $this->wmsStyleName = $wmsStyleName;
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/FileUrlFinder.php b/www/wiki/extensions/Maps/src/FileUrlFinder.php
new file mode 100644
index 00000000..c4894d53
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/FileUrlFinder.php
@@ -0,0 +1,18 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Maps;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+interface FileUrlFinder {
+
+ /**
+ * Resolves the url of images provided as wiki page; leaves others alone.
+ */
+ public function getUrlForFileName( string $fileName ): string;
+
+}
diff --git a/www/wiki/extensions/Maps/src/GeoFunctions.php b/www/wiki/extensions/Maps/src/GeoFunctions.php
new file mode 100644
index 00000000..75a92791
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/GeoFunctions.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Maps;
+
+use DataValues\Geo\Values\LatLongValue;
+
+/**
+ * Static class containing geographical functions.
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author Pnelnik
+ * @author Matěj Grabovský
+ */
+final class GeoFunctions {
+
+ // The approximate radius of the earth in meters, according to http://en.wikipedia.org/wiki/Earth_radius.
+ private const EARTH_RADIUS = 6371000;
+
+ /**
+ * Returns the geographical distance between two coordinates.
+ * See http://en.wikipedia.org/wiki/Geographical_distance
+ *
+ * @since 2.0
+ *
+ * @param LatLongValue $start
+ * @param LatLongValue $end
+ *
+ * @return float Distance in m.
+ */
+ public static function calculateDistance( LatLongValue $start, LatLongValue $end ) {
+ $northRad1 = deg2rad( $start->getLatitude() );
+ $eastRad1 = deg2rad( $start->getLongitude() );
+
+ $cosNorth1 = cos( $northRad1 );
+ $cosEast1 = cos( $eastRad1 );
+
+ $sinNorth1 = sin( $northRad1 );
+ $sinEast1 = sin( $eastRad1 );
+
+ $northRad2 = deg2rad( $end->getLatitude() );
+ $eastRad2 = deg2rad( $end->getLongitude() );
+
+ $cosNorth2 = cos( $northRad2 );
+ $cosEast2 = cos( $eastRad2 );
+
+ $sinNorth2 = sin( $northRad2 );
+ $sinEast2 = sin( $eastRad2 );
+
+ $term1 = $cosNorth1 * $sinEast1 - $cosNorth2 * $sinEast2;
+ $term2 = $cosNorth1 * $cosEast1 - $cosNorth2 * $cosEast2;
+ $term3 = $sinNorth1 - $sinNorth2;
+
+ $distThruSquared = $term1 * $term1 + $term2 * $term2 + $term3 * $term3;
+
+ $distance = 2 * self::EARTH_RADIUS * asin( sqrt( $distThruSquared ) / 2 );
+
+ assert( $distance >= 0 );
+
+ return $distance;
+ }
+
+ /**
+ * Finds a destination given a starting location, bearing and distance.
+ *
+ * @since 2.0
+ *
+ * @param LatLongValue $startingCoordinates
+ * @param float $bearing The initial bearing in degrees.
+ * @param float $distance The distance to travel in km.
+ *
+ * @return array The destination coordinates, as non-directional floats in an array with lat and lon keys.
+ */
+ public static function findDestination( LatLongValue $startingCoordinates, $bearing, $distance ) {
+ $startingCoordinates = [
+ 'lat' => deg2rad( $startingCoordinates->getLatitude() ),
+ 'lon' => deg2rad( $startingCoordinates->getLongitude() ),
+ ];
+
+ $radBearing = deg2rad( (float)$bearing );
+ $angularDistance = $distance / self::EARTH_RADIUS;
+
+ $lat = asin(
+ sin( $startingCoordinates['lat'] ) * cos( $angularDistance ) + cos( $startingCoordinates['lat'] ) * sin(
+ $angularDistance
+ ) * cos( $radBearing )
+ );
+ $lon = $startingCoordinates['lon'] + atan2(
+ sin( $radBearing ) * sin( $angularDistance ) * cos( $startingCoordinates['lat'] ),
+ cos( $angularDistance ) - sin( $startingCoordinates['lat'] ) * sin( $lat )
+ );
+
+ return [
+ 'lat' => rad2deg( $lat ),
+ 'lon' => rad2deg( $lon )
+ ];
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/src/GoogleMapsService.php b/www/wiki/extensions/Maps/src/GoogleMapsService.php
new file mode 100644
index 00000000..9c1b91b9
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/GoogleMapsService.php
@@ -0,0 +1,314 @@
+<?php
+
+namespace Maps;
+
+use Html;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author Peter Grassberger < petertheone@gmail.com >
+ */
+class GoogleMapsService implements MappingService {
+
+ /**
+ * Maps user input map types to the Google Maps names for the map types.
+ */
+ private const MAP_TYPES = [
+ 'normal' => 'ROADMAP',
+ 'roadmap' => 'ROADMAP',
+ 'satellite' => 'SATELLITE',
+ 'hybrid' => 'HYBRID',
+ 'terrain' => 'TERRAIN',
+ 'physical' => 'TERRAIN',
+ 'earth' => 'earth'
+ ];
+
+ private const TYPE_CONTROL_STYLES = [
+ 'default' => 'DEFAULT',
+ 'horizontal' => 'HORIZONTAL_BAR',
+ 'dropdown' => 'DROPDOWN_MENU'
+ ];
+
+ private $addedDependencies = [];
+
+ public function getName(): string {
+ return 'googlemaps3';
+ }
+
+ public function getAliases(): array {
+ return [ 'googlemaps', 'google' ];
+ }
+
+ public function hasAlias( string $alias ): bool {
+ return in_array( $alias, [ 'googlemaps', 'google' ] );
+ }
+
+ public function getParameterInfo(): array {
+ global $egMapsGMaps3Type, $egMapsGMaps3Types, $egMapsGMaps3Controls, $egMapsGMaps3Layers;
+ global $egMapsGMaps3DefTypeStyle, $egMapsGMaps3DefZoomStyle, $egMapsGMaps3AutoInfoWindows;
+ global $egMapsResizableByDefault;
+
+ $params = [];
+
+ $params['zoom'] = [
+ 'type' => 'integer',
+ 'range' => [ 0, 20 ],
+ 'default' => $GLOBALS['egMapsGMaps3Zoom'],
+ 'message' => 'maps-par-zoom',
+ ];
+
+ $params['type'] = [
+ 'default' => $egMapsGMaps3Type,
+ 'values' => self::getTypeNames(),
+ 'message' => 'maps-googlemaps3-par-type',
+ 'post-format' => function ( $value ) {
+ return GoogleMapsService::MAP_TYPES[strtolower( $value )];
+ },
+ ];
+
+ $params['types'] = [
+ 'dependencies' => 'type',
+ 'default' => $egMapsGMaps3Types,
+ 'values' => self::getTypeNames(),
+ 'message' => 'maps-googlemaps3-par-types',
+ 'islist' => true,
+ 'post-format' => function ( array $value ) {
+ foreach ( $value as &$part ) {
+ $part = self::MAP_TYPES[strtolower( $part )];
+ }
+
+ return $value;
+ },
+ ];
+
+ $params['layers'] = [
+ 'default' => $egMapsGMaps3Layers,
+ 'values' => [
+ 'traffic',
+ 'bicycling',
+ 'transit'
+ ],
+ 'message' => 'maps-googlemaps3-par-layers',
+ 'islist' => true,
+ ];
+
+ $params['controls'] = [
+ 'default' => $egMapsGMaps3Controls,
+ 'values' => [
+ 'pan',
+ 'zoom',
+ 'type',
+ 'scale',
+ 'streetview',
+ 'rotate'
+ ],
+ 'message' => 'maps-googlemaps3-par-controls',
+ 'islist' => true,
+ 'post-format' => function ( $value ) {
+ return array_map( 'strtolower', $value );
+ },
+ ];
+
+ $params['zoomstyle'] = [
+ 'default' => $egMapsGMaps3DefZoomStyle,
+ 'values' => [ 'default', 'small', 'large' ],
+ 'message' => 'maps-googlemaps3-par-zoomstyle',
+ 'post-format' => 'strtoupper',
+ ];
+
+ $params['typestyle'] = [
+ 'default' => $egMapsGMaps3DefTypeStyle,
+ 'values' => array_keys( self::TYPE_CONTROL_STYLES ),
+ 'message' => 'maps-googlemaps3-par-typestyle',
+ 'post-format' => function ( $value ) {
+ return self::TYPE_CONTROL_STYLES[strtolower( $value )];
+ },
+ ];
+
+ $params['autoinfowindows'] = [
+ 'type' => 'boolean',
+ 'default' => $egMapsGMaps3AutoInfoWindows,
+ 'message' => 'maps-googlemaps3-par-autoinfowindows',
+ ];
+
+ $params['resizable'] = [
+ 'type' => 'boolean',
+ 'default' => $egMapsResizableByDefault,
+ 'message' => 'maps-par-resizable',
+ ];
+
+ $params['kmlrezoom'] = [
+ 'type' => 'boolean',
+ 'default' => $GLOBALS['egMapsRezoomForKML'],
+ 'message' => 'maps-googlemaps3-par-kmlrezoom',
+ ];
+
+ $params['poi'] = [
+ 'type' => 'boolean',
+ 'default' => $GLOBALS['egMapsShowPOI'],
+ 'message' => 'maps-googlemaps3-par-poi',
+ ];
+
+ $params['markercluster'] = [
+ 'type' => 'boolean',
+ 'default' => false,
+ 'message' => 'maps-par-markercluster',
+ ];
+
+ $params['clustergridsize'] = [
+ 'type' => 'integer',
+ 'default' => 60,
+ 'message' => 'maps-googlemaps3-par-clustergridsize',
+ ];
+
+ $params['clustermaxzoom'] = [
+ 'type' => 'integer',
+ 'default' => 20,
+ 'message' => 'maps-par-clustermaxzoom',
+ ];
+
+ $params['clusterzoomonclick'] = [
+ 'type' => 'boolean',
+ 'default' => true,
+ 'message' => 'maps-par-clusterzoomonclick',
+ ];
+
+ $params['clusteraveragecenter'] = [
+ 'type' => 'boolean',
+ 'default' => true,
+ 'message' => 'maps-googlemaps3-par-clusteraveragecenter',
+ ];
+
+ $params['clusterminsize'] = [
+ 'type' => 'integer',
+ 'default' => 2,
+ 'message' => 'maps-googlemaps3-par-clusterminsize',
+ ];
+
+ $params['imageoverlays'] = [
+ 'type' => 'mapsimageoverlay',
+ 'default' => [],
+ 'delimiter' => ';',
+ 'islist' => true,
+ 'message' => 'maps-googlemaps3-par-imageoverlays',
+ ];
+
+ $params['kml'] = [
+ 'default' => [],
+ 'message' => 'maps-par-kml',
+ 'islist' => true,
+ 'post-format' => function( array $kmlFileNames ) {
+ return array_map(
+ function( string $fileName ) {
+ return wfExpandUrl( MapsFunctions::getFileUrl( $fileName ) );
+ },
+ $kmlFileNames
+ );
+ }
+ ];
+
+ $params['gkml'] = [
+ 'default' => [],
+ 'message' => 'maps-googlemaps3-par-gkml',
+ 'islist' => true,
+ ];
+
+ $params['searchmarkers'] = [
+ 'default' => '',
+ 'message' => 'maps-par-searchmarkers',
+ // new CriterionSearchMarkers() FIXME
+ ];
+
+ $params['enablefullscreen'] = [
+ 'type' => 'boolean',
+ 'default' => false,
+ 'message' => 'maps-par-enable-fullscreen',
+ ];
+
+ $params['scrollwheelzoom'] = [
+ 'type' => 'boolean',
+ 'default' => false,
+ 'message' => 'maps-par-scrollwheelzoom',
+ ];
+
+ return $params;
+ }
+
+ /**
+ * Returns the names of all supported map types.
+ */
+ private function getTypeNames(): array {
+ return array_keys( self::MAP_TYPES );
+ }
+
+ public function newMapId(): string {
+ static $mapsOnThisPage = 0;
+
+ $mapsOnThisPage++;
+
+ return 'map_google3_' . $mapsOnThisPage;
+ }
+
+ public function getResourceModules(): array {
+ return [ 'ext.maps.googlemaps3', 'ext.sm.googlemaps3ajax' ];
+ }
+
+ public static function getApiScript( $langCode, array $urlArgs = [] ) {
+ $urlArgs = array_merge(
+ [
+ 'language' => self::getMappedLanguageCode( $langCode )
+ ],
+ $urlArgs
+ );
+ if ( $GLOBALS['egMapsGMaps3ApiKey'] !== '' ) {
+ $urlArgs['key'] = $GLOBALS['egMapsGMaps3ApiKey'];
+ }
+ if ( $GLOBALS['egMapsGMaps3ApiVersion'] !== '' ) {
+ $urlArgs['v'] = $GLOBALS['egMapsGMaps3ApiVersion'];
+ }
+
+ return Html::linkedScript( '//maps.googleapis.com/maps/api/js?' . wfArrayToCgi( $urlArgs ) );
+ }
+
+ /**
+ * Maps language codes to Google Maps API v3 compatible values.
+ */
+ private static function getMappedLanguageCode( string $code ): string {
+ $mappings = [
+ 'en_gb' => 'en-gb',// v3 supports en_gb - but wants us to call it en-gb
+ 'he' => 'iw', // iw is googlish for hebrew
+ 'fj' => 'fil', // google does not support Fijian - use Filipino as close(?) supported relative
+ ];
+
+ if ( array_key_exists( $code, $mappings ) ) {
+ return $mappings[$code];
+ }
+
+ return $code;
+ }
+
+ public function getDependencyHtml( array $params ): string {
+ $dependencies = [];
+
+ // Only add dependencies that have not yet been added.
+ foreach ( $this->getDependencies() as $dependency ) {
+ if ( !in_array( $dependency, $this->addedDependencies ) ) {
+ $dependencies[] = $dependency;
+ $this->addedDependencies[] = $dependency;
+ }
+ }
+
+ // If there are dependencies, put them all together in a string, otherwise return false.
+ return $dependencies !== [] ? implode( '', $dependencies ) : false;
+ }
+
+ private function getDependencies(): array {
+ return [
+ self::getApiScript(
+ is_string( $GLOBALS['egMapsGMaps3Language'] ) ?
+ $GLOBALS['egMapsGMaps3Language'] : $GLOBALS['egMapsGMaps3Language']->getCode()
+ )
+ ];
+ }
+}
diff --git a/www/wiki/extensions/Maps/src/LeafletService.php b/www/wiki/extensions/Maps/src/LeafletService.php
new file mode 100644
index 00000000..0e41d670
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/LeafletService.php
@@ -0,0 +1,184 @@
+<?php
+
+namespace Maps;
+
+use Html;
+
+/**
+ * @licence GNU GPL v2+
+ */
+class LeafletService implements MappingService {
+
+ private $addedDependencies = [];
+
+ public function getName(): string {
+ return 'leaflet';
+ }
+
+ public function getAliases(): array {
+ return [ 'leafletmaps', 'leaflet' ]; // TODO: main name should not be in here?
+ }
+
+ public function hasAlias( string $alias ): bool {
+ return in_array( $alias, [ 'leafletmaps', 'leaflet' ] );
+ }
+
+ public function getParameterInfo(): array {
+ global $GLOBALS;
+
+ $params = [];
+
+ $params['zoom'] = [
+ 'type' => 'integer',
+ 'range' => [ 0, 20 ],
+ 'default' => false,
+ 'message' => 'maps-par-zoom'
+ ];
+
+ $params['defzoom'] = [
+ 'type' => 'integer',
+ 'range' => [ 0, 20 ],
+ 'default' => self::getDefaultZoom(),
+ 'message' => 'maps-leaflet-par-defzoom'
+ ];
+
+ $params['layers'] = [
+ 'aliases' => 'layer',
+ 'type' => 'string',
+ 'values' => array_keys( $GLOBALS['egMapsLeafletAvailableLayers'], true, true ),
+ 'default' => $GLOBALS['egMapsLeafletLayers'],
+ 'message' => 'maps-leaflet-par-layers',
+ 'islist' => true,
+ ];
+
+ $params['overlaylayers'] = [
+ 'type' => 'string',
+ 'values' => array_keys( $GLOBALS['egMapsLeafletAvailableOverlayLayers'], true, true ),
+ 'default' => $GLOBALS['egMapsLeafletOverlayLayers'],
+ 'message' => 'maps-leaflet-par-overlaylayers',
+ 'islist' => true,
+ ];
+
+ $params['resizable'] = [
+ 'type' => 'boolean',
+ 'default' => $GLOBALS['egMapsResizableByDefault'],
+ 'message' => 'maps-par-resizable'
+ ];
+
+ $params['enablefullscreen'] = [
+ 'type' => 'boolean',
+ 'default' => false,
+ 'message' => 'maps-par-enable-fullscreen',
+ ];
+
+ $params['scrollwheelzoom'] = [
+ 'type' => 'boolean',
+ 'default' => true,
+ 'message' => 'maps-par-scrollwheelzoom',
+ ];
+
+ $params['markercluster'] = [
+ 'type' => 'boolean',
+ 'default' => false,
+ 'message' => 'maps-par-markercluster',
+ ];
+
+ $params['clustermaxzoom'] = [
+ 'type' => 'integer',
+ 'default' => 20,
+ 'message' => 'maps-par-clustermaxzoom',
+ ];
+
+ $params['clusterzoomonclick'] = [
+ 'type' => 'boolean',
+ 'default' => true,
+ 'message' => 'maps-par-clusterzoomonclick',
+ ];
+
+ $params['clustermaxradius'] = [
+ 'type' => 'integer',
+ 'default' => 80,
+ 'message' => 'maps-par-maxclusterradius',
+ ];
+
+ $params['clusterspiderfy'] = [
+ 'type' => 'boolean',
+ 'default' => true,
+ 'message' => 'maps-leaflet-par-clusterspiderfy',
+ ];
+
+ $params['geojson'] = [
+ 'type' => 'jsonfile',
+ 'default' => '',
+ 'message' => 'maps-displaymap-par-geojson',
+ ];
+
+ return $params;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function getDefaultZoom() {
+ return $GLOBALS['egMapsLeafletZoom'];
+ }
+
+ public function newMapId(): string {
+ static $mapsOnThisPage = 0;
+
+ $mapsOnThisPage++;
+
+ return 'map_leaflet_' . $mapsOnThisPage;
+ }
+
+ public function getResourceModules(): array {
+ return [ 'ext.maps.leaflet', 'ext.sm.leafletajax' ];
+ }
+
+ public function getDependencyHtml( array $params ): string {
+ $dependencies = [];
+
+ // Only add dependencies that have not yet been added.
+ foreach ( $this->getDependencies( $params ) as $dependency ) {
+ if ( !in_array( $dependency, $this->addedDependencies ) ) {
+ $dependencies[] = $dependency;
+ $this->addedDependencies[] = $dependency;
+ }
+ }
+
+ // If there are dependencies, put them all together in a string, otherwise return false.
+ return $dependencies !== [] ? implode( '', $dependencies ) : false;
+ }
+
+ private function getDependencies( array $params ): array {
+ $leafletPath = $GLOBALS['wgScriptPath'] . '/extensions/Maps/resources/leaflet/leaflet';
+
+ return array_merge(
+ [
+ Html::linkedStyle( "$leafletPath/leaflet.css" ),
+ Html::linkedScript( "$leafletPath/leaflet.js" ),
+ ],
+ $this->getLayerDependencies( $params )
+ );
+ }
+
+ private function getLayerDependencies( array $params ) {
+ global $egMapsLeafletLayerDependencies, $egMapsLeafletAvailableLayers,
+ $egMapsLeafletLayersApiKeys;
+
+ $layerDependencies = [];
+
+ foreach ( $params['layers'] as $layerName ) {
+ if ( array_key_exists( $layerName, $egMapsLeafletAvailableLayers )
+ && $egMapsLeafletAvailableLayers[$layerName]
+ && array_key_exists( $layerName, $egMapsLeafletLayersApiKeys )
+ && array_key_exists( $layerName, $egMapsLeafletLayerDependencies ) ) {
+ $layerDependencies[] = '<script src="' . $egMapsLeafletLayerDependencies[$layerName] .
+ $egMapsLeafletLayersApiKeys[$layerName] . '"></script>';
+ }
+ }
+
+ return array_unique( $layerDependencies );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/MappingService.php b/www/wiki/extensions/Maps/src/MappingService.php
new file mode 100644
index 00000000..184ef712
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MappingService.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Maps;
+
+use ParamProcessor\ParamDefinition;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+interface MappingService {
+
+ public function getName(): string;
+
+ public function getAliases(): array;
+
+ public function hasAlias( string $alias ): bool;
+
+ /**
+ * @return array[]|ParamDefinition[]
+ */
+ public function getParameterInfo(): array;
+
+ public function getDependencyHtml( array $params ): string;
+
+ /**
+ * Returns the resource modules that need to be loaded to use this mapping service.
+ */
+ public function getResourceModules(): array;
+
+ public function newMapId();
+
+}
diff --git a/www/wiki/extensions/Maps/src/MappingServices.php b/www/wiki/extensions/Maps/src/MappingServices.php
new file mode 100644
index 00000000..295a7299
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MappingServices.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Maps;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+final class MappingServices {
+
+ /**
+ * @var MappingService[]
+ */
+ private $nameToServiceMap = [];
+
+ /**
+ * @var string Name of the default service, which is used as fallback
+ */
+ private $defaultService;
+
+ /**
+ * @param string[] $availableServices
+ * @param string $defaultService
+ * @param MappingService ...$services
+ * @throws \InvalidArgumentException
+ */
+ public function __construct( array $availableServices, string $defaultService, MappingService ...$services ) {
+ $this->defaultService = $defaultService;
+
+ foreach ( $services as $service ) {
+ if ( in_array( $service->getName(), $availableServices ) ) {
+ $this->nameToServiceMap[$service->getName()] = $service;
+
+ foreach ( $service->getAliases() as $alias ) {
+ $this->nameToServiceMap[$alias] = $service;
+ }
+ }
+ }
+
+ if ( !$this->nameIsKnown( $defaultService ) ) {
+ throw new \InvalidArgumentException( 'The default mapping service needs to be available' );
+ }
+ }
+
+ /**
+ * @param string $name Name or alias of a service
+ * @return bool
+ */
+ public function nameIsKnown( string $name ): bool {
+ return array_key_exists( $name, $this->nameToServiceMap );
+ }
+
+ /**
+ * @param string $name Name or alias of a service
+ * @return MappingService
+ * @throws \OutOfBoundsException
+ */
+ public function getService( string $name ): MappingService {
+ if ( !$this->nameIsKnown( $name ) ) {
+ throw new \OutOfBoundsException();
+ }
+
+ return $this->nameToServiceMap[$name];
+ }
+
+ /**
+ * @param string $name Name or alias of a service
+ * @return MappingService
+ */
+ public function getServiceOrDefault( string $name ): MappingService {
+ if ( $this->nameIsKnown( $name ) ) {
+ return $this->nameToServiceMap[$name];
+ }
+
+ return $this->nameToServiceMap[$this->defaultService];
+ }
+
+ public function getAllNames(): array {
+ return array_keys( $this->nameToServiceMap );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/MapsFactory.php b/www/wiki/extensions/Maps/src/MapsFactory.php
new file mode 100644
index 00000000..6ab23d5d
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MapsFactory.php
@@ -0,0 +1,168 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Maps;
+
+use FileFetcher\Cache\Factory as CacheFactory;
+use FileFetcher\FileFetcher;
+use Jeroen\SimpleGeocoder\Geocoder;
+use Jeroen\SimpleGeocoder\Geocoders\Decorators\CoordinateFriendlyGeocoder;
+use Jeroen\SimpleGeocoder\Geocoders\FileFetchers\GeoNamesGeocoder;
+use Jeroen\SimpleGeocoder\Geocoders\FileFetchers\GoogleGeocoder;
+use Jeroen\SimpleGeocoder\Geocoders\FileFetchers\NominatimGeocoder;
+use Jeroen\SimpleGeocoder\Geocoders\NullGeocoder;
+use Maps\DataAccess\CachingGeocoder;
+use Maps\DataAccess\MapsFileFetcher;
+use Maps\DataAccess\MediaWikiFileUrlFinder;
+use Maps\DataAccess\PageContentFetcher;
+use Maps\MediaWiki\ParserHooks\DisplayMapFunction;
+use Maps\Presentation\CoordinateFormatter;
+use Maps\Presentation\WikitextParsers\LocationParser;
+use MediaWiki\MediaWikiServices;
+use SimpleCache\Cache\Cache;
+use SimpleCache\Cache\MediaWikiCache;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class MapsFactory {
+
+ private $settings;
+ private $mediaWikiServices;
+
+ private function __construct( array $settings, MediaWikiServices $mediaWikiServices ) {
+ $this->settings = $settings;
+ $this->mediaWikiServices = $mediaWikiServices;
+ }
+
+ public static function newDefault(): self {
+ return new self( $GLOBALS, MediaWikiServices::getInstance() );
+ }
+
+ /**
+ * Only for legacy code where dependency injection is not possible
+ */
+ public static function globalInstance(): self {
+ static $instance = null;
+
+ if ( $instance === null ) {
+ $instance = self::newDefault();
+ }
+
+ return $instance;
+ }
+
+ public function newLocationParser(): LocationParser {
+ return LocationParser::newInstance(
+ $this->getGeocoder(),
+ $this->getFileUrlFinder()
+ );
+ }
+
+ public function getGeocoder(): Geocoder {
+ $geocoder = new CoordinateFriendlyGeocoder( $this->newCoreGeocoder() );
+
+ if ( $this->settings['egMapsEnableGeoCache'] ) {
+ return new CachingGeocoder(
+ $geocoder,
+ $this->getMediaWikiCache(),
+ $this->settings['egMapsGeoCacheTtl']
+ );
+ }
+
+ return $geocoder;
+ }
+
+ private function newCoreGeocoder(): Geocoder {
+ switch ( $this->settings['egMapsDefaultGeoService'] ) {
+ case 'geonames':
+ if ( $this->settings['egMapsGeoNamesUser'] === '' ) {
+ return $this->newGoogleGeocoder();
+ }
+
+ return new GeoNamesGeocoder(
+ $this->newFileFetcher(),
+ $this->settings['egMapsGeoNamesUser']
+ );
+ case 'google':
+ return $this->newGoogleGeocoder();
+ case 'nominatim':
+ return new NominatimGeocoder(
+ $this->newFileFetcher()
+ );
+ default:
+ return new NullGeocoder();
+ }
+ }
+
+ private function newGoogleGeocoder(): Geocoder {
+ return new GoogleGeocoder(
+ $this->newFileFetcher(),
+ $this->settings['egMapsGMaps3ApiKey'],
+ $this->settings['egMapsGMaps3ApiVersion']
+ );
+ }
+
+ public function getFileFetcher(): FileFetcher {
+ return $this->newFileFetcher();
+ }
+
+ private function newFileFetcher(): FileFetcher {
+ return new MapsFileFetcher();
+ }
+
+ public function getGeoJsonFileFetcher(): FileFetcher {
+ if ( $this->settings['egMapsGeoJsonCacheTtl'] === 0 ) {
+ return $this->getFileFetcher();
+ }
+
+ return ( new CacheFactory() )->newJeroenSimpleCacheFetcher(
+ $this->getFileFetcher(),
+ $this->getMediaWikiSimpleCache( $this->settings['egMapsGeoJsonCacheTtl'] )
+ );
+ }
+
+ private function getMediaWikiSimpleCache( int $ttlInSeconds ): Cache {
+ return new MediaWikiCache(
+ $this->getMediaWikiCache(),
+ $ttlInSeconds
+ );
+ }
+
+ private function getMediaWikiCache(): \BagOStuff {
+ return wfGetCache( CACHE_ANYTHING );
+ }
+
+ public function getPageContentFetcher(): PageContentFetcher {
+ return new PageContentFetcher(
+ $this->mediaWikiServices->getTitleParser(),
+ $this->mediaWikiServices->getRevisionLookup()
+ );
+ }
+
+ public function getCoordinateFormatter(): CoordinateFormatter {
+ return new CoordinateFormatter();
+ }
+
+ public function getFileUrlFinder(): FileUrlFinder {
+ return new MediaWikiFileUrlFinder();
+ }
+
+ public function getMappingServices(): MappingServices {
+ return new MappingServices(
+ $this->settings['egMapsAvailableServices'],
+ $this->settings['egMapsDefaultService'],
+ new GoogleMapsService(),
+ new LeafletService()
+ );
+ }
+
+ public function getDisplayMapFunction(): DisplayMapFunction {
+ return new DisplayMapFunction(
+ $this->getMappingServices()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/MapsFunctions.php b/www/wiki/extensions/Maps/src/MapsFunctions.php
new file mode 100644
index 00000000..47cd1358
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MapsFunctions.php
@@ -0,0 +1,218 @@
+<?php
+
+namespace Maps;
+
+use Xml;
+
+/**
+ * A class that holds static helper functions for generic mapping-related functions.
+ *
+ * @deprecated
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+final class MapsFunctions {
+
+ /**
+ * Encode a variable of unknown type to JavaScript.
+ * Arrays are converted to JS arrays, objects are converted to JS associative
+ * arrays (objects). So cast your PHP associative arrays to objects before
+ * passing them to here.
+ *
+ * This is a copy of
+ *
+ * @see Xml::encodeJsVar
+ * which fixes incorrect behaviour with floats.
+ *
+ * @since 0.7.1
+ *
+ * @param mixed $value
+ *
+ * @return string
+ */
+ public static function encodeJsVar( $value ) {
+ if ( is_bool( $value ) ) {
+ $s = $value ? 'true' : 'false';
+ } elseif ( is_null( $value ) ) {
+ $s = 'null';
+ } elseif ( is_int( $value ) || is_float( $value ) ) {
+ $s = $value;
+ } elseif ( is_array( $value ) && // Make sure it's not associative.
+ array_keys( $value ) === range( 0, count( $value ) - 1 ) ||
+ count( $value ) == 0
+ ) {
+ $s = '[';
+ foreach ( $value as $elt ) {
+ if ( $s != '[' ) {
+ $s .= ', ';
+ }
+ $s .= self::encodeJsVar( $elt );
+ }
+ $s .= ']';
+ } elseif ( is_object( $value ) || is_array( $value ) ) {
+ // Objects and associative arrays
+ $s = '{';
+ foreach ( (array)$value as $name => $elt ) {
+ if ( $s != '{' ) {
+ $s .= ', ';
+ }
+ $s .= '"' . Xml::encodeJsVar( $name ) . '": ' .
+ self::encodeJsVar( $elt );
+ }
+ $s .= '}';
+ } else {
+ $s = '"' . Xml::encodeJsVar( $value ) . '"';
+ }
+ return $s;
+ }
+
+ /**
+ * This function returns the definitions for the parameters used by every map feature.
+ *
+ * @return array
+ */
+ public static function getCommonParameters() {
+ $params = [];
+
+ $params['mappingservice'] = [
+ 'type' => 'string',
+ 'aliases' => 'service',
+ 'default' => $GLOBALS['egMapsDefaultService'],
+ 'values' => MapsFactory::globalInstance()->getMappingServices()->getAllNames(),
+ ];
+
+ $params['width'] = [
+ 'type' => 'dimension',
+ 'allowauto' => true,
+ 'units' => [ 'px', 'ex', 'em', '%', '' ],
+ 'default' => $GLOBALS['egMapsMapWidth'],
+ ];
+
+ $params['height'] = [
+ 'type' => 'dimension',
+ 'units' => [ 'px', 'ex', 'em', '' ],
+ 'default' => $GLOBALS['egMapsMapHeight'],
+ ];
+
+ $params['centre'] = [
+ 'type' => 'string',
+ 'aliases' => [ 'center' ],
+ 'default' => false,
+ 'manipulatedefault' => false,
+ ];
+
+ // Give grep a chance to find the usages:
+ // maps-par-mappingservice, maps-par-geoservice, maps-par-width,
+ // maps-par-height, maps-par-centre
+ foreach ( $params as $name => &$data ) {
+ $data['name'] = $name;
+ $data['message'] = 'maps-par-' . $name;
+ }
+
+ $params['title'] = [
+ 'name' => 'title',
+ 'default' => $GLOBALS['egMapsDefaultTitle'],
+ ];
+
+ $params['label'] = [
+ 'default' => $GLOBALS['egMapsDefaultLabel'],
+ 'aliases' => 'text',
+ ];
+
+ $params['icon'] = [
+ 'default' => '',
+ ];
+
+ $params['visitedicon'] = [
+ 'default' => '',
+ ];
+
+ $params['lines'] = [
+ 'type' => 'mapsline',
+ 'default' => [],
+ 'delimiter' => ';',
+ 'islist' => true,
+ ];
+
+ $params['polygons'] = [
+ 'type' => 'mapspolygon',
+ 'default' => [],
+ 'delimiter' => ';',
+ 'islist' => true,
+ ];
+
+ $params['circles'] = [
+ 'type' => 'mapscircle',
+ 'default' => [],
+ 'delimiter' => ';',
+ 'islist' => true,
+ ];
+
+ $params['rectangles'] = [
+ 'type' => 'mapsrectangle',
+ 'default' => [],
+ 'delimiter' => ';',
+ 'islist' => true,
+ ];
+
+ $params['wmsoverlay'] = [
+ 'type' => 'wmsoverlay',
+ 'default' => false,
+ 'delimiter' => ' ',
+ ];
+
+ $params['maxzoom'] = [
+ 'type' => 'integer',
+ 'default' => false,
+ 'manipulatedefault' => false,
+ 'dependencies' => 'minzoom',
+ ];
+
+ $params['minzoom'] = [
+ 'type' => 'integer',
+ 'default' => false,
+ 'manipulatedefault' => false,
+ 'lowerbound' => 0,
+ ];
+
+ $params['copycoords'] = [
+ 'type' => 'boolean',
+ 'default' => false,
+ ];
+
+ $params['static'] = [
+ 'type' => 'boolean',
+ 'default' => false,
+ ];
+
+ // Give grep a chance to find the usages:
+ // maps-displaymap-par-title, maps-displaymap-par-label, maps-displaymap-par-icon,
+ // maps-displaymap-par-visitedicon, aps-displaymap-par-lines, maps-displaymap-par-polygons,
+ // maps-displaymap-par-circles, maps-displaymap-par-rectangles, maps-displaymap-par-wmsoverlay,
+ // maps-displaymap-par-maxzoom, maps-displaymap-par-minzoom, maps-displaymap-par-copycoords,
+ // maps-displaymap-par-static
+ foreach ( $params as $name => &$param ) {
+ if ( !array_key_exists( 'message', $param ) ) {
+ $param['message'] = 'maps-displaymap-par-' . $name;
+ }
+ }
+
+ return $params;
+ }
+
+ /**
+ * Resolves the url of images provided as wiki page; leaves others alone.
+ *
+ * @since 1.0
+ * @deprecated
+ *
+ * @param string $file
+ *
+ * @return string
+ */
+ public static function getFileUrl( $file ): string {
+ return MapsFactory::globalInstance()->getFileUrlFinder()->getUrlForFileName( $file );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/MapsSetup.php b/www/wiki/extensions/Maps/src/MapsSetup.php
new file mode 100644
index 00000000..245d3915
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MapsSetup.php
@@ -0,0 +1,206 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Maps;
+
+use DataValues\Geo\Parsers\LatLongParser;
+use Maps\DataAccess\JsonFileParser;
+use Maps\MediaWiki\Content\GeoJsonContent;
+use Maps\MediaWiki\Content\GeoJsonContentHandler;
+use Maps\MediaWiki\ParserHooks\CoordinatesFunction;
+use Maps\MediaWiki\ParserHooks\DisplayMapFunction;
+use Maps\MediaWiki\ParserHooks\DistanceFunction;
+use Maps\MediaWiki\ParserHooks\FindDestinationFunction;
+use Maps\MediaWiki\ParserHooks\GeocodeFunction;
+use Maps\MediaWiki\ParserHooks\GeoDistanceFunction;
+use Maps\MediaWiki\ParserHooks\MapsDocFunction;
+use Maps\Presentation\WikitextParsers\CircleParser;
+use Maps\Presentation\WikitextParsers\DistanceParser;
+use Maps\Presentation\WikitextParsers\ImageOverlayParser;
+use Maps\Presentation\WikitextParsers\LineParser;
+use Maps\Presentation\WikitextParsers\LocationParser;
+use Maps\Presentation\WikitextParsers\PolygonParser;
+use Maps\Presentation\WikitextParsers\RectangleParser;
+use Maps\Presentation\WikitextParsers\WmsOverlayParser;
+use Parser;
+use PPFrame;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class MapsSetup {
+
+ private $mwGlobals;
+
+ public function __construct( array &$mwGlobals ) {
+ $this->mwGlobals = $mwGlobals;
+ }
+
+ public function setup() {
+ $this->defaultSettings();
+ $this->registerAllTheThings();
+
+ if ( !$this->mwGlobals['egMapsDisableSmwIntegration'] && defined( 'SMW_VERSION' ) ) {
+ SemanticMaps::newFromMediaWikiGlobals( $this->mwGlobals )->initExtension();
+ }
+ }
+
+ private function registerAllTheThings() {
+ $this->registerParserHooks();
+ $this->registerPermissions();
+ $this->registerParameterTypes();
+ $this->registerHooks();
+
+ $this->mwGlobals['wgContentHandlers'][GeoJsonContent::CONTENT_MODEL_ID] = GeoJsonContentHandler::class;
+ }
+
+ private function defaultSettings() {
+ if ( $this->mwGlobals['egMapsGMaps3Language'] === '' ) {
+ $this->mwGlobals['egMapsGMaps3Language'] = $this->mwGlobals['wgLang'];
+ }
+
+ if ( in_array( 'googlemaps3', $this->mwGlobals['egMapsAvailableServices'] ) ) {
+ $this->mwGlobals['wgSpecialPages']['MapEditor'] = 'Maps\MediaWiki\Specials\SpecialMapEditor';
+ $this->mwGlobals['wgSpecialPageGroups']['MapEditor'] = 'maps';
+ }
+
+ if ( $this->mwGlobals['egMapsGMaps3ApiKey'] === '' && array_key_exists(
+ 'egGoogleJsApiKey',
+ $this->mwGlobals
+ ) ) {
+ $this->mwGlobals['egMapsGMaps3ApiKey'] = $this->mwGlobals['egGoogleJsApiKey'];
+ }
+ }
+
+ private function registerParserHooks() {
+ if ( $this->mwGlobals['egMapsEnableCoordinateFunction'] ) {
+ $this->mwGlobals['wgHooks']['ParserFirstCallInit'][] = function ( Parser &$parser ) {
+ return ( new CoordinatesFunction() )->init( $parser );
+ };
+ }
+
+ $this->mwGlobals['wgHooks']['ParserFirstCallInit'][] = function ( Parser &$parser ) {
+ foreach ( [ 'display_map', 'display_point', 'display_points', 'display_line' ] as $hookName ) {
+ $parser->setFunctionHook(
+ $hookName,
+ function ( Parser $parser, PPFrame $frame, array $arguments ) {
+ $mapHtml = MapsFactory::newDefault()->getDisplayMapFunction()->getMapHtmlForKeyValueStrings(
+ $parser,
+ array_map(
+ function ( $argument ) use ( $frame ) {
+ return $frame->expand( $argument );
+ },
+ $arguments
+ )
+ );
+
+ return [
+ $mapHtml,
+ 'noparse' => true,
+ 'isHTML' => true,
+ ];
+ },
+ Parser::SFH_OBJECT_ARGS
+ );
+
+ $parser->setHook(
+ $hookName,
+ function ( $text, array $arguments, Parser $parser ) {
+ if ( $text !== null ) {
+ $defaultParameters = DisplayMapFunction::getHookDefinition( "\n" )->getDefaultParameters();
+ $defaultParam = array_shift( $defaultParameters );
+
+ // If there is a first default parameter, set the tag contents as its value.
+ if ( $defaultParam !== null ) {
+ $arguments[$defaultParam] = $text;
+ }
+ }
+
+ return MapsFactory::newDefault()->getDisplayMapFunction()->getMapHtmlForParameterList( $parser, $arguments );
+ }
+ );
+ }
+ };
+
+ $this->mwGlobals['wgHooks']['ParserFirstCallInit'][] = function ( Parser &$parser ) {
+ return ( new DistanceFunction() )->init( $parser );
+ };
+
+ $this->mwGlobals['wgHooks']['ParserFirstCallInit'][] = function ( Parser &$parser ) {
+ return ( new FindDestinationFunction() )->init( $parser );
+ };
+
+ $this->mwGlobals['wgHooks']['ParserFirstCallInit'][] = function ( Parser &$parser ) {
+ return ( new GeocodeFunction() )->init( $parser );
+ };
+
+ $this->mwGlobals['wgHooks']['ParserFirstCallInit'][] = function ( Parser &$parser ) {
+ return ( new GeoDistanceFunction() )->init( $parser );
+ };
+
+ $this->mwGlobals['wgHooks']['ParserFirstCallInit'][] = function ( Parser &$parser ) {
+ return ( new MapsDocFunction() )->init( $parser );
+ };
+ }
+
+ private function registerPermissions() {
+ $this->mwGlobals['wgAvailableRights'][] = 'geocode';
+
+ // Users that can geocode. By default the same as those that can edit.
+ foreach ( $this->mwGlobals['wgGroupPermissions'] as $group => $rights ) {
+ if ( array_key_exists( 'edit', $rights ) ) {
+ $this->mwGlobals['wgGroupPermissions'][$group]['geocode'] = $this->mwGlobals['wgGroupPermissions'][$group]['edit'];
+ }
+ }
+ }
+
+ private function registerParameterTypes() {
+ $this->mwGlobals['wgParamDefinitions']['coordinate'] = [
+ 'string-parser' => LatLongParser::class,
+ ];
+
+ $this->mwGlobals['wgParamDefinitions']['mapslocation'] = [
+ 'string-parser' => LocationParser::class,
+ ];
+
+ $this->mwGlobals['wgParamDefinitions']['mapsline'] = [
+ 'string-parser' => LineParser::class,
+ ];
+
+ $this->mwGlobals['wgParamDefinitions']['mapscircle'] = [
+ 'string-parser' => CircleParser::class,
+ ];
+
+ $this->mwGlobals['wgParamDefinitions']['mapsrectangle'] = [
+ 'string-parser' => RectangleParser::class,
+ ];
+
+ $this->mwGlobals['wgParamDefinitions']['mapspolygon'] = [
+ 'string-parser' => PolygonParser::class,
+ ];
+
+ $this->mwGlobals['wgParamDefinitions']['distance'] = [
+ 'string-parser' => DistanceParser::class,
+ ];
+
+ $this->mwGlobals['wgParamDefinitions']['wmsoverlay'] = [
+ 'string-parser' => WmsOverlayParser::class,
+ ];
+
+ $this->mwGlobals['wgParamDefinitions']['mapsimageoverlay'] = [
+ 'string-parser' => ImageOverlayParser::class,
+ ];
+
+ $this->mwGlobals['wgParamDefinitions']['jsonfile'] = [
+ 'string-parser' => JsonFileParser::class,
+ ];
+ }
+
+ private function registerHooks() {
+ $this->mwGlobals['wgHooks']['AdminLinks'][] = 'Maps\MediaWiki\MapsHooks::addToAdminLinks';
+ $this->mwGlobals['wgHooks']['MakeGlobalVariablesScript'][] = 'Maps\MediaWiki\MapsHooks::onMakeGlobalVariablesScript';
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/MediaWiki/Content/GeoJsonContent.php b/www/wiki/extensions/Maps/src/MediaWiki/Content/GeoJsonContent.php
new file mode 100644
index 00000000..72a89b04
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MediaWiki/Content/GeoJsonContent.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace Maps\MediaWiki\Content;
+
+use Html;
+use ParserOptions;
+use ParserOutput;
+use Title;
+
+class GeoJsonContent extends \JsonContent {
+
+ public const CONTENT_MODEL_ID = 'GeoJSON';
+
+ public function __construct( string $text, string $modelId = self::CONTENT_MODEL_ID ) {
+ parent::__construct( $text, $modelId );
+ }
+
+ protected function fillParserOutput( Title $title, $revId, ParserOptions $options,
+ $generateHtml, ParserOutput &$output ) {
+
+ if ( $generateHtml && $this->isValid() ) {
+ $output->setText( $this->getMapHtml( $this->beautifyJSON() ) );
+ $output->addModules( 'ext.maps.leaflet.editor' );
+ } else {
+ $output->setText( '' );
+ }
+ }
+
+ private function getMapHtml( string $jsonString ): string {
+ return
+ Html::element(
+ 'div',
+ [
+ 'id' => 'GeoJsonMap',
+ 'class' => 'GeoJsonMap',
+ ]
+ )
+ . '<style>'
+ . '.GeoJsonMap {width: "100%"; height: 600px; display: "inline-block"}'
+ . '</style>'
+ .
+ Html::element(
+ 'script',
+ [],
+ 'var GeoJson =' . $jsonString . ';'
+ );
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/src/MediaWiki/Content/GeoJsonContentHandler.php b/www/wiki/extensions/Maps/src/MediaWiki/Content/GeoJsonContentHandler.php
new file mode 100644
index 00000000..b192a9c3
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MediaWiki/Content/GeoJsonContentHandler.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Maps\MediaWiki\Content;
+
+class GeoJsonContentHandler extends \JsonContentHandler {
+
+ public function __construct( $modelId = GeoJsonContent::CONTENT_MODEL_ID ) {
+ parent::__construct( $modelId );
+ }
+
+ protected function getContentClass() {
+ return GeoJsonContent::class;
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/src/MediaWiki/MapsHooks.php b/www/wiki/extensions/Maps/src/MediaWiki/MapsHooks.php
new file mode 100644
index 00000000..e2a8ad95
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MediaWiki/MapsHooks.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Maps\MediaWiki;
+
+use AlItem;
+use ALTree;
+
+/**
+ * Static class for hooks handled by the Maps extension.
+ *
+ * @since 0.7
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+final class MapsHooks {
+
+ /**
+ * Adds a link to Admin Links page.
+ *
+ * @since 0.7
+ *
+ * @param ALTree $admin_links_tree
+ *
+ * @return boolean
+ */
+ public static function addToAdminLinks( ALTree &$admin_links_tree ) {
+ $displaying_data_section = $admin_links_tree->getSection(
+ wfMessage( 'smw_adminlinks_displayingdata' )->text()
+ );
+
+ // Escape if SMW hasn't added links.
+ if ( is_null( $displaying_data_section ) ) {
+ return true;
+ }
+
+ $smw_docu_row = $displaying_data_section->getRow( 'smw' );
+
+ $maps_docu_label = wfMessage( 'adminlinks_documentation', 'Maps' )->text();
+ $smw_docu_row->addItem(
+ AlItem::newFromExternalLink( 'https://www.semantic-mediawiki.org/wiki/Extension:Maps', $maps_docu_label )
+ );
+
+ return true;
+ }
+
+ /**
+ * Adds global JavaScript variables.
+ *
+ * @since 1.0
+ * @see http://www.mediawiki.org/wiki/Manual:Hooks/MakeGlobalVariablesScript
+ *
+ * @param array &$vars Variables to be added into the output
+ *
+ * @return boolean true in all cases
+ */
+ public static function onMakeGlobalVariablesScript( array &$vars ) {
+ $vars['egMapsScriptPath'] = $GLOBALS['wgScriptPath'] . '/extensions/Maps/'; // TODO: wgExtensionDirectory?
+ $vars['egMapsDebugJS'] = $GLOBALS['egMapsDebugJS'];
+ $vars['egMapsAvailableServices'] = $GLOBALS['egMapsAvailableServices'];
+ $vars['egMapsLeafletLayersApiKeys'] = $GLOBALS['egMapsLeafletLayersApiKeys'];
+
+ $vars += $GLOBALS['egMapsGlobalJSVars'];
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/CoordinatesFunction.php b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/CoordinatesFunction.php
new file mode 100644
index 00000000..94051571
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/CoordinatesFunction.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Maps\MediaWiki\ParserHooks;
+
+use Maps\MapsFactory;
+use ParserHook;
+
+/**
+ * Class for the 'coordinates' parser hooks,
+ * which can transform the notation of a set of coordinates.
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class CoordinatesFunction extends ParserHook {
+
+ /**
+ * Renders and returns the output.
+ *
+ * @see ParserHook::render
+ *
+ * @param array $parameters
+ *
+ * @return string
+ */
+ public function render( array $parameters ) {
+ return MapsFactory::globalInstance()->getCoordinateFormatter()->format(
+ $parameters['location'],
+ $parameters['format'],
+ $parameters['directional']
+ );
+ }
+
+ /**
+ * @see ParserHook::getMessage()
+ */
+ public function getMessage() {
+ return 'maps-coordinates-description';
+ }
+
+ /**
+ * Gets the name of the parser hook.
+ *
+ * @see ParserHook::getName
+ *
+ * @return string
+ */
+ protected function getName() {
+ return 'coordinates';
+ }
+
+ /**
+ * Returns an array containing the parameter info.
+ *
+ * @see ParserHook::getParameterInfo
+ *
+ * @return array
+ */
+ protected function getParameterInfo( $type ) {
+ global $egMapsAvailableCoordNotations;
+ global $egMapsCoordinateNotation;
+ global $egMapsCoordinateDirectional;
+
+ $params = [];
+
+ $params['location'] = [
+ 'type' => 'coordinate',
+ ];
+
+ $params['format'] = [
+ 'default' => $egMapsCoordinateNotation,
+ 'values' => $egMapsAvailableCoordNotations,
+ 'aliases' => 'notation',
+ 'tolower' => true,
+ ];
+
+ $params['directional'] = [
+ 'type' => 'boolean',
+ 'default' => $egMapsCoordinateDirectional,
+ ];
+
+ // Give grep a chance to find the usages:
+ // maps-coordinates-par-location, maps-coordinates-par-format, maps-coordinates-par-directional
+ foreach ( $params as $name => &$param ) {
+ $param['message'] = 'maps-coordinates-par-' . $name;
+ }
+
+ return $params;
+ }
+
+ /**
+ * Returns the list of default parameters.
+ *
+ * @see ParserHook::getDefaultParameters
+ *
+ * @return array
+ */
+ protected function getDefaultParameters( $type ) {
+ return [ 'location', 'format', 'directional' ];
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DisplayMapFunction.php b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DisplayMapFunction.php
new file mode 100644
index 00000000..bad0d842
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DisplayMapFunction.php
@@ -0,0 +1,171 @@
+<?php
+
+namespace Maps\MediaWiki\ParserHooks;
+
+use Maps;
+use Maps\MapsFunctions;
+use Maps\MappingServices;
+use Maps\Presentation\ParameterExtractor;
+use MWException;
+use ParamProcessor;
+use ParamProcessor\ProcessedParam;
+use Parser;
+
+/**
+ * Class for the 'display_map' parser hooks.
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class DisplayMapFunction {
+
+ private $services;
+
+ private $renderer;
+
+ public function __construct( MappingServices $services ) {
+ $this->services = $services;
+
+ $this->renderer = new DisplayMapRenderer();
+ }
+
+ /**
+ * @param Parser $parser
+ * @param string[] $parameters Values of the array can be named parameters ("key=value") or unnamed.
+ * They are not normalized, so can be "key = value "
+ *
+ * @return string
+ * @throws MWException
+ */
+ public function getMapHtmlForKeyValueStrings( Parser $parser, array $parameters ): string {
+ $processor = new \ParamProcessor\Processor( new \ParamProcessor\Options() );
+
+ $service = $this->services->getServiceOrDefault(
+ $this->extractServiceName(
+ Maps\Presentation\ParameterExtractor::extractFromKeyValueStrings( $parameters )
+ )
+ );
+
+ $this->renderer->service = $service;
+
+ $processor->setFunctionParams(
+ $parameters,
+ array_merge(
+ self::getHookDefinition( ';' )->getParameters(),
+ $service->getParameterInfo()
+ ),
+ self::getHookDefinition( ';' )->getDefaultParameters()
+ );
+
+ return $this->getMapHtmlFromProcessor( $parser, $processor );
+ }
+
+ /**
+ * @param Parser $parser
+ * @param string[] $parameters Key value list of parameters. Unnamed parameters have numeric keys.
+ * Both keys and values have not been normalized.
+ *
+ * @return string
+ * @throws MWException
+ */
+ public function getMapHtmlForParameterList( Parser $parser, array $parameters ) {
+ $processor = new \ParamProcessor\Processor( new \ParamProcessor\Options() );
+
+ $service = $this->services->getServiceOrDefault( $this->extractServiceName( $parameters ) );
+
+ $this->renderer->service = $service;
+
+ $processor->setParameters(
+ $parameters,
+ array_merge(
+ self::getHookDefinition( "\n" )->getParameters(),
+ $service->getParameterInfo()
+ )
+ );
+
+ return $this->getMapHtmlFromProcessor( $parser, $processor );
+ }
+
+ private function getMapHtmlFromProcessor( Parser $parser, ParamProcessor\Processor $processor ) {
+ $params = $processor->processParameters()->getParameters();
+
+ $this->defaultMapZoom( $params );
+
+ $this->trackMap( $parser );
+
+ return $this->renderer->renderMap(
+ $this->processedParametersToKeyValueArray( $params ),
+ $parser
+ );
+ }
+
+ private function extractServiceName( array $parameters ): string {
+ $service = ( new ParameterExtractor() )->extract(
+ [ 'mappingservice', 'service' ],
+ $parameters
+ );
+
+ return $service ?? '';
+ }
+
+ private function processedParametersToKeyValueArray( array $params ): array {
+ $parameters = [];
+
+ foreach ( $params as $parameter ) {
+ $parameters[$parameter->getName()] = $parameter->getValue();
+ }
+
+ return $parameters;
+ }
+
+ public static function getHookDefinition( string $locationDelimiter ): \ParserHooks\HookDefinition {
+ return new \ParserHooks\HookDefinition(
+ [ 'display_map', 'display_point', 'display_points', 'display_line' ],
+ self::getParameterDefinitions( $locationDelimiter ),
+ [ 'coordinates' ]
+ );
+ }
+
+ private static function getParameterDefinitions( $locationDelimiter ): array {
+ $params = MapsFunctions::getCommonParameters();
+
+ $params['coordinates'] = [
+ 'type' => 'string',
+ 'aliases' => [ 'coords', 'location', 'address', 'addresses', 'locations', 'points' ],
+ 'default' => [],
+ 'islist' => true,
+ 'delimiter' => $locationDelimiter,
+ 'message' => 'maps-displaymap-par-coordinates',
+ ];
+
+ return $params;
+ }
+
+ /**
+ * @param ProcessedParam[] $parameters
+ */
+ private function defaultMapZoom( array &$parameters ) {
+ if ( array_key_exists( 'zoom', $parameters ) && $parameters['zoom']->wasSetToDefault() && count(
+ $parameters['coordinates']->getValue()
+ ) > 1 ) {
+ $parameters['zoom'] = $this->getParameterWithValue( $parameters['zoom'], false );
+ }
+ }
+
+ private function getParameterWithValue( ProcessedParam $param, $value ) {
+ return new ProcessedParam(
+ $param->getName(),
+ $value,
+ $param->wasSetToDefault(),
+ $param->getOriginalName(),
+ $param->getOriginalValue()
+ );
+ }
+
+ private function trackMap( Parser $parser ) {
+ if ( $GLOBALS['egMapsEnableCategory'] ) {
+ $parser->addTrackingCategory( 'maps-tracking-category' );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DisplayMapRenderer.php b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DisplayMapRenderer.php
new file mode 100644
index 00000000..8c757acd
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DisplayMapRenderer.php
@@ -0,0 +1,182 @@
+<?php
+
+namespace Maps\MediaWiki\ParserHooks;
+
+use FormatJson;
+use Html;
+use Maps\DataAccess\MediaWikiFileUrlFinder;
+use Maps\Elements\Location;
+use Maps\MappingService;
+use Maps\Presentation\ElementJsonSerializer;
+use Maps\Presentation\MapHtmlBuilder;
+use Maps\Presentation\WikitextParser;
+use Maps\Presentation\WikitextParsers\LocationParser;
+use Parser;
+
+/**
+ * Class handling the #display_map rendering.
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author Kim Eik
+ */
+class DisplayMapRenderer {
+
+ public $service;
+
+ /**
+ * @var LocationParser
+ */
+ private $locationParser;
+
+ /**
+ * @var MediaWikiFileUrlFinder
+ */
+ private $fileUrlFinder;
+
+ /**
+ * @var WikitextParser
+ */
+ private $wikitextParser;
+ /**
+ * @var ElementJsonSerializer
+ */
+ private $elementSerializer;
+
+ public function __construct( MappingService $service = null ) {
+ $this->service = $service;
+ }
+
+ /**
+ * Handles the request from the parser hook by doing the work that's common for all
+ * mapping services, calling the specific methods and finally returning the resulting output.
+ *
+ * @param array $params
+ * @param Parser $parser
+ *
+ * @return string
+ */
+ public final function renderMap( array $params, Parser $parser ) {
+ $factory = \Maps\MapsFactory::newDefault();
+
+ $this->locationParser = $factory->newLocationParser();
+ $this->fileUrlFinder = $factory->getFileUrlFinder();
+
+ $this->wikitextParser = new WikitextParser( clone $parser );
+ $this->elementSerializer = new ElementJsonSerializer( $this->wikitextParser );
+
+ $this->handleMarkerData( $params );
+
+ $output = ( new MapHtmlBuilder() )->getMapHTML(
+ $params,
+ $this->service->newMapId(),
+ $this->service->getName()
+ );
+
+ $dependencies = $this->service->getDependencyHtml( $params );
+
+ // Only add a head item when there are dependencies.
+ if ( $dependencies ) {
+ $parser->getOutput()->addHeadItem( $dependencies );
+ }
+
+ $parser->getOutput()->addModules( $this->service->getResourceModules() );
+
+ return $output;
+ }
+
+ /**
+ * Converts the data in the coordinates parameter to JSON-ready objects.
+ * These get stored in the locations parameter, and the coordinates on gets deleted.
+ */
+ private function handleMarkerData( array &$params ) {
+ $params['centre'] = $this->getCenter( $params['centre'] );
+
+ if ( is_object( $params['wmsoverlay'] ) ) {
+ $params['wmsoverlay'] = $params['wmsoverlay']->getJSONObject();
+ }
+
+ $params['locations'] = $this->getLocationJson( $params );
+
+ unset( $params['coordinates'] );
+
+ $this->handleShapeData( $params );
+ }
+
+ private function getCenter( $coordinatesOrAddress ) {
+ if ( $coordinatesOrAddress === false ) {
+ return false;
+ }
+
+ try {
+ // FIXME: a Location makes no sense here, since the non-coordinate data is not used
+ $location = $this->locationParser->parse( $coordinatesOrAddress );
+ }
+ catch ( \Exception $ex ) {
+ // TODO: somehow report this to the user
+ return false;
+ }
+
+ return $location->getJSONObject();
+ }
+
+ private function getLocationJson( array $params ) {
+ $iconUrl = $this->fileUrlFinder->getUrlForFileName( $params['icon'] );
+ $visitedIconUrl = $this->fileUrlFinder->getUrlForFileName( $params['visitedicon'] );
+
+ $locationJsonObjects = [];
+
+ foreach ( $params['coordinates'] as $coordinatesOrAddress ) {
+ try {
+ $location = $this->locationParser->parse( $coordinatesOrAddress );
+ }
+ catch ( \Exception $ex ) {
+ // TODO: somehow report this to the user
+ continue;
+ }
+
+ $locationJsonObjects[] = $this->getLocationJsonObject(
+ $location,
+ $params,
+ $iconUrl,
+ $visitedIconUrl
+ );
+ }
+
+ return $locationJsonObjects;
+ }
+
+ private function getLocationJsonObject( Location $location, array $params, $iconUrl, $visitedIconUrl ) {
+ $jsonObj = $location->getJSONObject( $params['title'], $params['label'], $iconUrl, '', '', $visitedIconUrl );
+
+ $this->elementSerializer->titleAndText( $jsonObj );
+
+ if ( isset( $jsonObj['inlineLabel'] ) ) {
+ $jsonObj['inlineLabel'] = strip_tags(
+ $this->wikitextParser->wikitextToHtml( $jsonObj['inlineLabel'] ),
+ '<a><img>'
+ );
+ }
+
+ return $jsonObj;
+ }
+
+ private function handleShapeData( array &$params ) {
+ $textContainers = [
+ &$params['lines'],
+ &$params['polygons'],
+ &$params['circles'],
+ &$params['rectangles'],
+ &$params['imageoverlays'], // FIXME: this is Google Maps specific!!
+ ];
+
+ foreach ( $textContainers as &$textContainer ) {
+ if ( is_array( $textContainer ) ) {
+ foreach ( $textContainer as &$obj ) {
+ $obj = $this->elementSerializer->elementToJson( $obj );
+ }
+ }
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DistanceFunction.php b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DistanceFunction.php
new file mode 100644
index 00000000..84b11c2b
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DistanceFunction.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace Maps\MediaWiki\ParserHooks;
+
+use Maps\Presentation\MapsDistanceParser;
+use ParserHook;
+
+/**
+ * Class for the 'distance' parser hooks,
+ * which can transform the notation of a distance.
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class DistanceFunction extends ParserHook {
+
+ /**
+ * Renders and returns the output.
+ *
+ * @see ParserHook::render
+ *
+ * @param array $parameters
+ *
+ * @return string
+ */
+ public function render( array $parameters ) {
+ return MapsDistanceParser::formatDistance(
+ $parameters['distance'],
+ $parameters['unit'],
+ $parameters['decimals']
+ );
+ }
+
+ /**
+ * @see ParserHook::getMessage()
+ */
+ public function getMessage() {
+ return 'maps-distance-description';
+ }
+
+ /**
+ * Gets the name of the parser hook.
+ *
+ * @see ParserHook::getName
+ *
+ * @return string
+ */
+ protected function getName() {
+ return 'distance';
+ }
+
+ /**
+ * Returns an array containing the parameter info.
+ *
+ * @see ParserHook::getParameterInfo
+ *
+ * @return array
+ */
+ protected function getParameterInfo( $type ) {
+ global $egMapsDistanceUnit, $egMapsDistanceDecimals;
+
+ $params = [];
+
+ $params['distance'] = [
+ 'type' => 'distance',
+ ];
+
+ $params['unit'] = [
+ 'default' => $egMapsDistanceUnit,
+ 'values' => MapsDistanceParser::getUnits(),
+ ];
+
+ $params['decimals'] = [
+ 'type' => 'integer',
+ 'default' => $egMapsDistanceDecimals,
+ ];
+
+ // Give grep a chance to find the usages:
+ // maps-distance-par-distance, maps-distance-par-unit, maps-distance-par-decimals
+ foreach ( $params as $name => &$param ) {
+ $param['message'] = 'maps-distance-par-' . $name;
+ }
+
+ return $params;
+ }
+
+ /**
+ * Returns the list of default parameters.
+ *
+ * @see ParserHook::getDefaultParameters
+ *
+ * @param $type
+ *
+ * @return array
+ */
+ protected function getDefaultParameters( $type ) {
+ return [ 'distance', 'unit', 'decimals' ];
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/FindDestinationFunction.php b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/FindDestinationFunction.php
new file mode 100644
index 00000000..e7cb319c
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/FindDestinationFunction.php
@@ -0,0 +1,120 @@
+<?php
+
+namespace Maps\MediaWiki\ParserHooks;
+
+use DataValues\Geo\Values\LatLongValue;
+use Maps\MapsFactory;
+use Maps\GeoFunctions;
+use ParserHook;
+
+/**
+ * Class for the 'finddestination' parser hooks, which can find a
+ * destination given a starting point, an initial bearing and a distance.
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class FindDestinationFunction extends ParserHook {
+
+ /**
+ * Renders and returns the output.
+ *
+ * @see ParserHook::render
+ *
+ * @param array $parameters
+ *
+ * @return string
+ */
+ public function render( array $parameters ) {
+ $destination = GeoFunctions::findDestination(
+ $parameters['location']->getCoordinates(),
+ $parameters['bearing'],
+ $parameters['distance']
+ );
+
+ return MapsFactory::globalInstance()->getCoordinateFormatter()->format(
+ new LatLongValue( $destination['lat'], $destination['lon'] ),
+ $parameters['format'],
+ $parameters['directional']
+ );
+ }
+
+ /**
+ * @see ParserHook::getMessage()
+ */
+ public function getMessage() {
+ return 'maps-finddestination-description';
+ }
+
+ /**
+ * Gets the name of the parser hook.
+ *
+ * @see ParserHook::getName
+ *
+ * @return string
+ */
+ protected function getName() {
+ return 'finddestination';
+ }
+
+ /**
+ * Returns an array containing the parameter info.
+ *
+ * @see ParserHook::getParameterInfo
+ *
+ * @return array
+ */
+ protected function getParameterInfo( $type ) {
+ global $egMapsAvailableCoordNotations;
+ global $egMapsCoordinateNotation, $egMapsCoordinateDirectional;
+
+ $params = [];
+
+ $params['location'] = [
+ 'type' => 'mapslocation',
+ ];
+
+ $params['format'] = [
+ 'default' => $egMapsCoordinateNotation,
+ 'values' => $egMapsAvailableCoordNotations,
+ 'aliases' => 'notation',
+ 'tolower' => true,
+ ];
+
+ $params['directional'] = [
+ 'type' => 'boolean',
+ 'default' => $egMapsCoordinateDirectional,
+ ];
+
+ $params['bearing'] = [
+ 'type' => 'float',
+ ];
+
+ $params['distance'] = [
+ 'type' => 'distance',
+ ];
+
+ // Give grep a chance to find the usages:
+ // maps-finddestination-par-location, maps-finddestination-par-format,
+ // maps-finddestination-par-directional, maps-finddestination-par-bearing,
+ // maps-finddestination-par-distance, maps-finddestination-par-mappingservice,
+ // maps-finddestination-par-geoservice, maps-finddestination-par-allowcoordinates
+ foreach ( $params as $name => &$param ) {
+ $param['message'] = 'maps-finddestination-par-' . $name;
+ }
+
+ return $params;
+ }
+
+ /**
+ * Returns the list of default parameters.
+ *
+ * @see ParserHook::getDefaultParameters
+ *
+ * @return array
+ */
+ protected function getDefaultParameters( $type ) {
+ return [ 'location', 'bearing', 'distance' ];
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/GeoDistanceFunction.php b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/GeoDistanceFunction.php
new file mode 100644
index 00000000..f7afc393
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/GeoDistanceFunction.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace Maps\MediaWiki\ParserHooks;
+
+use Maps\GeoFunctions;
+use Maps\Presentation\MapsDistanceParser;
+use MWException;
+use ParserHook;
+
+/**
+ * Class for the 'geodistance' parser hooks, which can
+ * calculate the geographical distance between two points.
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class GeoDistanceFunction extends ParserHook {
+
+ /**
+ * Renders and returns the output.
+ *
+ * @see ParserHook::render
+ *
+ * @param array $parameters
+ *
+ * @return string
+ * @throws MWException
+ */
+ public function render( array $parameters ) {
+ /**
+ * @var \DataValues\Geo\Values\LatLongValue $coordinates1
+ * @var \DataValues\Geo\Values\LatLongValue $coordinates2
+ */
+ $coordinates1 = $parameters['location1']->getCoordinates();
+ $coordinates2 = $parameters['location2']->getCoordinates();
+
+ $distance = GeoFunctions::calculateDistance( $coordinates1, $coordinates2 );
+ $output = MapsDistanceParser::formatDistance( $distance, $parameters['unit'], $parameters['decimals'] );
+
+ return $output;
+ }
+
+ /**
+ * @see ParserHook::getMessage
+ */
+ public function getMessage() {
+ return 'maps-geodistance-description';
+ }
+
+ /**
+ * Gets the name of the parser hook.
+ *
+ * @see ParserHook::getName
+ *
+ * @return string
+ */
+ protected function getName() {
+ return 'geodistance';
+ }
+
+ /**
+ * Returns an array containing the parameter info.
+ *
+ * @see ParserHook::getParameterInfo
+ *
+ * @return array
+ */
+ protected function getParameterInfo( $type ) {
+ global $egMapsDistanceUnit, $egMapsDistanceDecimals;
+
+ $params = [];
+
+ $params['unit'] = [
+ 'default' => $egMapsDistanceUnit,
+ 'values' => MapsDistanceParser::getUnits(),
+ ];
+
+ $params['decimals'] = [
+ 'type' => 'integer',
+ 'default' => $egMapsDistanceDecimals,
+ ];
+
+ $params['location1'] = [
+ 'type' => 'mapslocation',
+ 'aliases' => 'from',
+ ];
+
+ $params['location2'] = [
+ 'type' => 'mapslocation',
+ 'aliases' => 'to',
+ ];
+
+ // Give grep a chance to find the usages:
+ // maps-geodistance-par-mappingservice, maps-geodistance-par-geoservice,
+ // maps-geodistance-par-unit, maps-geodistance-par-decimals,
+ // maps-geodistance-par-location1, maps-geodistance-par-location2
+ foreach ( $params as $name => &$param ) {
+ $param['message'] = 'maps-geodistance-par-' . $name;
+ }
+
+ return $params;
+ }
+
+ /**
+ * Returns the list of default parameters.
+ *
+ * @see ParserHook::getDefaultParameters
+ *
+ * @param $type
+ *
+ * @return array
+ */
+ protected function getDefaultParameters( $type ) {
+ return [ 'location1', 'location2', 'unit', 'decimals' ];
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/GeocodeFunction.php b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/GeocodeFunction.php
new file mode 100644
index 00000000..5f70ef24
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/GeocodeFunction.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace Maps\MediaWiki\ParserHooks;
+
+use Jeroen\SimpleGeocoder\Geocoder;
+use Maps\MapsFactory;
+use ParserHook;
+
+/**
+ * Class for the 'geocode' parser hooks, which can turn
+ * human readable locations into sets of coordinates.
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class GeocodeFunction extends ParserHook {
+
+ private $geocoder;
+
+ public function __construct( Geocoder $geocoder = null ) {
+ $this->geocoder = $geocoder ?? \Maps\MapsFactory::newDefault()->getGeocoder();
+ parent::__construct();
+ }
+
+ /**
+ * Renders and returns the output.
+ *
+ * @see ParserHook::render
+ *
+ * @param array $parameters
+ *
+ * @return string
+ */
+ public function render( array $parameters ) {
+ $coordinates = $this->geocoder->geocode( $parameters['location'] );
+
+ if ( $coordinates === null ) {
+ return 'Geocoding failed'; // TODO: i18n
+ }
+
+ return MapsFactory::globalInstance()->getCoordinateFormatter()->format(
+ $coordinates,
+ $parameters['format'],
+ $parameters['directional']
+ );
+ }
+
+ /**
+ * @see ParserHook::getMessage()
+ */
+ public function getMessage() {
+ return 'maps-geocode-description';
+ }
+
+ /**
+ * Gets the name of the parser hook.
+ *
+ * @see ParserHook::getName
+ *
+ * @return string
+ */
+ protected function getName() {
+ return 'geocode';
+ }
+
+ /**
+ * Returns an array containing the parameter info.
+ *
+ * @see ParserHook::getParameterInfo
+ *
+ * @return array
+ */
+ protected function getParameterInfo( $type ) {
+ global $egMapsAvailableCoordNotations;
+ global $egMapsCoordinateNotation;
+ global $egMapsCoordinateDirectional;
+
+ $params = [];
+
+ $params['location'] = [
+ 'type' => 'string',
+ 'message' => 'maps-geocode-par-location',
+ ];
+
+ $params['format'] = [
+ 'default' => $egMapsCoordinateNotation,
+ 'values' => $egMapsAvailableCoordNotations,
+ 'aliases' => 'notation',
+ 'tolower' => true,
+ 'message' => 'maps-geocode-par-format',
+ ];
+
+ $params['directional'] = [
+ 'type' => 'boolean',
+ 'default' => $egMapsCoordinateDirectional,
+ 'message' => 'maps-geocode-par-directional',
+ ];
+
+ return $params;
+ }
+
+ /**
+ * Returns the list of default parameters.
+ *
+ * @see ParserHook::getDefaultParameters
+ *
+ * @return array
+ */
+ protected function getDefaultParameters( $type ) {
+ return [ 'location' ];
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/MapsDocFunction.php b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/MapsDocFunction.php
new file mode 100644
index 00000000..6a365378
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/MapsDocFunction.php
@@ -0,0 +1,200 @@
+<?php
+
+namespace Maps\MediaWiki\ParserHooks;
+
+use Maps\MappingServices;
+use Maps\MapsFactory;
+use ParamProcessor\ParamDefinition;
+use ParserHook;
+
+/**
+ * Class for the 'mapsdoc' parser hooks,
+ * which displays documentation for a specified mapping service.
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class MapsDocFunction extends ParserHook {
+
+ /**
+ * Field to store the value of the language parameter.
+ *
+ * @var string
+ */
+ protected $language;
+
+ /**
+ * Renders and returns the output.
+ *
+ * @see ParserHook::render
+ *
+ * @param array $parameters
+ *
+ * @return string
+ */
+ public function render( array $parameters ) {
+ $this->language = $parameters['language'];
+
+ $params = $this->getServiceParameters( $parameters['service'] );
+
+ return $this->getParameterTable( $params );
+ }
+
+ private function getServiceParameters( $service ) {
+ return array_merge(
+ [
+ 'zoom' => [
+ 'type' => 'integer',
+ 'message' => 'maps-par-zoom',
+ ]
+ ],
+ MapsFactory::globalInstance()->getMappingServices()->getService( $service )->getParameterInfo()
+ );
+ }
+
+ /**
+ * Returns the wikitext for a table listing the provided parameters.
+ *
+ * @param array $parameters
+ *
+ * @return string
+ */
+ private function getParameterTable( array $parameters ) {
+ $tableRows = [];
+
+ $parameters = ParamDefinition::getCleanDefinitions( $parameters );
+
+ foreach ( $parameters as $parameter ) {
+ $tableRows[] = $this->getDescriptionRow( $parameter );
+ }
+
+ $table = '';
+
+ if ( count( $tableRows ) > 0 ) {
+ $tableRows = array_merge(
+ [
+ '!' . $this->msg( 'validator-describe-header-parameter' ) . "\n" .
+ //'!' . $this->msg( 'validator-describe-header-aliases' ) ."\n" .
+ '!' . $this->msg( 'validator-describe-header-type' ) . "\n" .
+ '!' . $this->msg( 'validator-describe-header-default' ) . "\n" .
+ '!' . $this->msg( 'validator-describe-header-description' )
+ ],
+ $tableRows
+ );
+
+ $table = implode( "\n|-\n", $tableRows );
+
+ $table =
+ '{| class="wikitable sortable"' . "\n" .
+ $table .
+ "\n|}";
+ }
+
+ return $table;
+ }
+
+ /**
+ * Returns the wikitext for a table row describing a single parameter.
+ *
+ * @param ParamDefinition $parameter
+ *
+ * @return string
+ */
+ private function getDescriptionRow( ParamDefinition $parameter ) {
+ $description = $this->msg( $parameter->getMessage() );
+
+ $type = $this->msg( $parameter->getTypeMessage() );
+
+ $default = $parameter->isRequired() ? "''" . $this->msg(
+ 'validator-describe-required'
+ ) . "''" : $parameter->getDefault();
+ if ( is_array( $default ) ) {
+ $default = implode( ', ', $default );
+ } elseif ( is_bool( $default ) ) {
+ $default = $default ? 'yes' : 'no';
+ }
+
+ if ( $default === '' ) {
+ $default = "''" . $this->msg( 'validator-describe-empty' ) . "''";
+ }
+
+ return <<<EOT
+| {$parameter->getName()}
+| {$type}
+| {$default}
+| {$description}
+EOT;
+ }
+
+ /**
+ * Message function that takes into account the language parameter.
+ *
+ * @param string $key
+ * @param ... $args
+ *
+ * @return string
+ */
+ private function msg() {
+ $args = func_get_args();
+ $key = array_shift( $args );
+ return wfMessage( $key, $args )->inLanguage( $this->language )->text();
+ }
+
+ /**
+ * @see ParserHook::getDescription()
+ */
+ public function getMessage() {
+ return 'maps-mapsdoc-description';
+ }
+
+ /**
+ * Gets the name of the parser hook.
+ *
+ * @see ParserHook::getName
+ *
+ * @return string
+ */
+ protected function getName() {
+ return 'mapsdoc';
+ }
+
+ /**
+ * Returns an array containing the parameter info.
+ *
+ * @see ParserHook::getParameterInfo
+ *
+ * @return array
+ */
+ protected function getParameterInfo( $type ) {
+ $params = [];
+
+ $params['service'] = [
+ 'values' => $GLOBALS['egMapsAvailableServices'],
+ 'tolower' => true,
+ ];
+
+ $params['language'] = [
+ 'default' => $GLOBALS['wgLanguageCode'],
+ ];
+
+ // Give grep a chance to find the usages:
+ // maps-geocode-par-service, maps-geocode-par-language
+ foreach ( $params as $name => &$param ) {
+ $param['message'] = 'maps-geocode-par-' . $name;
+ }
+
+ return $params;
+ }
+
+ /**
+ * Returns the list of default parameters.
+ *
+ * @see ParserHook::getDefaultParameters
+ *
+ * @return array
+ */
+ protected function getDefaultParameters( $type ) {
+ return [ 'service', 'language' ];
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/MediaWiki/SemanticMapsHooks.php b/www/wiki/extensions/Maps/src/MediaWiki/SemanticMapsHooks.php
new file mode 100644
index 00000000..4c929b42
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MediaWiki/SemanticMapsHooks.php
@@ -0,0 +1,115 @@
+<?php
+
+namespace Maps\MediaWiki;
+
+use AlItem;
+use ALTree;
+use Maps\SemanticMW\DataValues\CoordinateValue;
+use Maps\SemanticMW\DataValues\GeoPolygonValue;
+use SMW\DataTypeRegistry;
+use SMWDataItem;
+use SMWPrintRequest;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+final class SemanticMapsHooks {
+
+ /**
+ * Adds a link to Admin Links page.
+ *
+ * @since 0.7
+ *
+ * @param ALTree $admin_links_tree
+ *
+ * @return boolean
+ */
+ public static function addToAdminLinks( ALTree &$admin_links_tree ) {
+ $displaying_data_section = $admin_links_tree->getSection(
+ wfMessage( 'smw_adminlinks_displayingdata' )->text()
+ );
+
+ // Escape if SMW hasn't added links.
+ if ( is_null( $displaying_data_section ) ) {
+ return true;
+ }
+
+ $smw_docu_row = $displaying_data_section->getRow( 'smw' );
+
+ $sm_docu_label = wfMessage( 'adminlinks_documentation', 'Semantic Maps' )->text();
+ $smw_docu_row->addItem(
+ AlItem::newFromExternalLink( 'https://www.semantic-mediawiki.org/wiki/Semantic_Maps', $sm_docu_label )
+ );
+
+ return true;
+ }
+
+ /**
+ * Adds support for the geographical coordinates and shapes data type to Semantic MediaWiki.
+ *
+ * @since 2.0
+ *
+ * @return boolean
+ */
+ public static function initGeoDataTypes() {
+ DataTypeRegistry::getInstance()->registerDatatype(
+ '_geo',
+ CoordinateValue::class,
+ SMWDataItem::TYPE_GEO
+ );
+
+ return true;
+ }
+
+ /**
+ * Set the default format to 'map' when the requested properties are
+ * of type geographic coordinates.
+ *
+ * TODO: have a setting to turn this off and have it off by default for #show
+ *
+ * @since 1.0
+ *
+ * @param $format Mixed: The format (string), or false when not set yet
+ * @param SMWPrintRequest[] $printRequests The print requests made
+ *
+ * @return boolean
+ */
+ public static function addGeoCoordsDefaultFormat( &$format, array $printRequests ) {
+ // Only set the format when not set yet. This allows other extensions to override the Maps behavior.
+ if ( $format === false ) {
+ // Only apply when there is more then one print request.
+ // This way requests comming from #show are ignored.
+ if ( count( $printRequests ) > 1 ) {
+ $allValid = true;
+ $hasCoords = false;
+
+ // Loop through the print requests to determine their types.
+ foreach ( $printRequests as $printRequest ) {
+ // Skip the first request, as it's the object.
+ if ( $printRequest->getMode() == SMWPrintRequest::PRINT_THIS ) {
+ continue;
+ }
+
+ $typeId = $printRequest->getTypeID();
+
+ if ( $typeId == '_geo' ) {
+ $hasCoords = true;
+ } else {
+ $allValid = false;
+ break;
+ }
+ }
+
+ // If they are all coordinates, set the result format to 'map'.
+ if ( $allValid && $hasCoords ) {
+ $format = 'map';
+ }
+ }
+
+ }
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/MediaWiki/Specials/MapEditorHTML.php b/www/wiki/extensions/Maps/src/MediaWiki/Specials/MapEditorHTML.php
new file mode 100644
index 00000000..60409041
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MediaWiki/Specials/MapEditorHTML.php
@@ -0,0 +1,221 @@
+<?php
+
+namespace Maps\MediaWiki\Specials;
+
+use ContextSource;
+use Html;
+
+/**
+ * Class to Handle HTML generation for Special:MapEditor
+ *
+ * @since 2.1
+ *
+ * @licence GNU GPL v2+
+ * @author Nischayn22
+ */
+class MapEditorHtml extends ContextSource {
+
+ /**
+ * Array holding the additional attributes for the canvas div.
+ *
+ * @var array()
+ * @since 2.1
+ */
+ protected $attribs;
+
+ public function __construct( $attribs = [] ) {
+ $this->attribs = $attribs;
+ }
+
+ /**
+ * Returns the HTML for the MapEditor.
+ *
+ * @return string
+ * @since 2.1
+ */
+ public function getEditorHTML() {
+
+ $output = <<<EOT
+ {$this->getCanvasDiv()}<div style="display: none;">
+ <div id="code-output-container" title="%1\$s">
+ <textarea id="code-output" rows="15" readonly></textarea>
+ </div>
+ <div id="code-input-container" title="%2\$s" >
+ <p>%3\$s</p>
+ <textarea id="code-input" rows="15"></textarea>
+ </div>
+ <div id="marker-form" class="mapeditor-dialog" title="%4\$s">
+ <div class="link-title-switcher">
+ <input type="radio" name="switch" value="text" /> %5\$s
+ <input type="radio" name="switch" value="link" /> %6\$s
+ </div>
+ <form class="mapeditor-dialog-form">
+ <fieldset>
+ <label for="m-title">%7\$s</label>
+ <input type="text" name="title" id="m-title" class="text ui-widget-content ui-corner-all"/>
+ <label for="m-text">%8\$s</label>
+ <input type="text" name="text" id="m-text" class="text ui-widget-content ui-corner-all"/>
+ <label for="m-link">%9\$s</label>
+ <input type="text" name="link" id="m-link" class="text ui-widget-content ui-corner-all"/>
+ <label for="m-icon">%10\$s</label>
+ <input type="text" name="icon" id="m-icon" class="text ui-widget-content ui-corner-all"/>
+ <label for="m-group">%11\$s</label>
+ <input type="text" name="group" id="m-group" class="text ui-widget-content ui-corner-all"/>
+ <label for="m-inlinelabel">%12\$s</label>
+ <input type="text" name="inlinelabel" id="m-inlinelabel" class="text ui-widget-content ui-corner-all"/>
+ <label for="m-visitedicon">%23\$s</label>
+ <input type="text" name="visitedicon" id="m-visitedicon" class="text ui-widget-content ui-corner-all"/>
+ </fieldset>
+ </form>
+ </div>
+
+ <div id="strokable-form" class="mapeditor-dialog" title="%4\$s">
+ <div class="link-title-switcher">
+ <input type="radio" name="switch" value="text" /> %5\$s
+ <input type="radio" name="switch" value="link" /> %6\$s
+ </div>
+ <form class="mapeditor-dialog-form">
+ <fieldset>
+ <label for="s-title">%7\$s</label>
+ <input type="text" name="title" id="s-title" class="text ui-widget-content ui-corner-all"/>
+ <label for="s-text">%8\$s</label>
+ <input type="text" name="text" id="s-text" value="" class="text ui-widget-content ui-corner-all"/>
+ <label for="s-link">%9\$s</label>
+ <input type="text" name="link" id="s-link" class="text ui-widget-content ui-corner-all"/>
+ <label for="s-strokecolor">%13\$s</label>
+ <input type="text" name="strokeColor" id="s-strokecolor" class="text ui-widget-content ui-corner-all"/>
+ <label for="s-strokeopacity">%14\$s</label>
+ <input type="hidden" name="strokeOpacity" id="s-strokeopacity" class="text ui-widget-content ui-corner-all"/>
+ <label for="s-strokeweight">%15\$s</label>
+ <input type="text" name="strokeWeight" id="s-strokeweight" class="text ui-widget-content ui-corner-all"/>
+ </fieldset>
+ </form>
+ </div>
+
+ <div id="fillable-form" class="mapeditor-dialog" title="%4\$s">
+ <div class="link-title-switcher">
+ <input type="radio" name="switch" value="text" /> %5\$s
+ <input type="radio" name="switch" value="link" /> %6\$s
+ </div>
+ <form class="mapeditor-dialog-form">
+ <fieldset>
+ <label for="f-title">%7\$s</label>
+ <input type="text" name="title" id="f-title" class="text ui-widget-content ui-corner-all"/>
+ <label for="f-text">%8\$s</label>
+ <input type="text" name="text" id="f-text" value="" class="text ui-widget-content ui-corner-all"/>
+ <label for="f-link">%9\$s</label>
+ <input type="text" name="link" id="f-link" class="text ui-widget-content ui-corner-all"/>
+ <label for="f-strokecolor">%13\$s</label>
+ <input type="text" name="strokeColor" id="f-strokecolor" class="text ui-widget-content ui-corner-all"/>
+ <label for="f-strokeopacity">%14\$s</label>
+ <input type="hidden" name="strokeOpacity" id="f-strokeopacity" class="text ui-widget-content ui-corner-all"/>
+ <label for="f-strokeweight">%15\$s</label>
+ <input type="text" name="strokeWeight" id="f-strokeweight" class="text ui-widget-content ui-corner-all"/>
+ <label for="f-fillcolor">%16\$s</label>
+ <input type="text" name="fillColor" id="f-fillcolor" class="text ui-widget-content ui-corner-all"/>
+ <label for="f-fillopacity">%17\$s</label>
+ <input type="hidden" name="fillOpacity" id="f-fillopacity" class="text ui-widget-content ui-corner-all"/>
+ </fieldset>
+ </form>
+ </div>
+
+ <div id="polygon-form" class="mapeditor-dialog" title="%4\$s">
+ <div class="link-title-switcher">
+ <input type="radio" name="switch" value="text" /> %5\$s
+ <input type="radio" name="switch" value="link" /> %6\$s
+ </div>
+ <form class="mapeditor-dialog-form">
+ <fieldset>
+ <label for="p-title">%7\$s</label>
+ <input type="text" name="title" id="p-title" class="text ui-widget-content ui-corner-all"/>
+ <label for="p-text">%8\$s</label>
+ <input type="text" name="text" id="p-text" value="" class="text ui-widget-content ui-corner-all"/>
+ <label for="p-link">%9\$s</label>
+ <input type="text" name="link" id="p-link" class="text ui-widget-content ui-corner-all"/>
+ <label for="p-strokecolor">%13\$s</label>
+ <input type="text" name="strokeColor" id="p-strokecolor" class="text ui-widget-content ui-corner-all"/>
+ <label for="p-strokeopacity">%14\$s</label>
+ <input type="hidden" name="strokeOpacity" id="p-strokeopacity" class="text ui-widget-content ui-corner-all"/>
+ <label for="p-strokeweight">%15\$s</label>
+ <input type="text" name="strokeWeight" id="p-strokeweight" class="text ui-widget-content ui-corner-all"/>
+ <label for="p-fillcolor">%16\$s</label>
+ <input type="text" name="fillColor" id="p-fillcolor" class="text ui-widget-content ui-corner-all"/>
+ <label for="p-fillopacity">%17\$s</label>
+ <input type="hidden" name="fillOpacity" id="p-fillopacity" class="text ui-widget-content ui-corner-all"/>
+ <label for="p-showonhover">%18\$s</label>
+ <input type="checkbox" name="showOnHover" id="p-showonhover" class="text ui-widget-content ui-corner-all"/>
+ </fieldset>
+ </form>
+ </div>
+ <div id="map-parameter-form" class="mapeditor-dialog" title="%19\$s">
+ <form class="mapeditor-dialog-form">
+ <div>
+ <select name="key">
+ <option value="">%20\$s</option>
+ </select>
+ </div>
+ </form>
+ </div>
+ <div id="imageoverlay-form" title="%22\$s">
+ <div class="link-title-switcher">
+ <input type="radio" name="switch" value="text" /> %5\$s
+ <input type="radio" name="switch" value="link" /> %6\$s
+ </div>
+ <form class="mapeditor-dialog-form">
+ <fieldset>
+ <label for="i-title">%7\$s</label>
+ <input type="text" name="title" id="i-title" class="text ui-widget-content ui-corner-all"/>
+ <label for="i-text">%8\$s</label>
+ <input type="text" name="text" id="i-text" class="text ui-widget-content ui-corner-all"/>
+ <label for="i-link">%9\$s</label>
+ <input type="text" name="link" id="i-link" class="text ui-widget-content ui-corner-all"/>
+ <label for="i-image">%21\$s</label>
+ <input type="text" name="image" id="i-image" class="text ui-widget-content ui-corner-all"/>
+ </fieldset>
+ </form>
+ </div>
+</div>
+EOT;
+
+ $html = sprintf(
+ $output,
+ $this->msg( 'mapeditor-code-title' ),
+ $this->msg( 'mapeditor-import-title' ),
+ $this->msg( 'mapeditor-import-note' ),
+ $this->msg( 'mapeditor-form-title' ),
+ $this->msg( 'mapeditor-link-title-switcher-popup-text' ),
+ $this->msg( 'mapeditor-link-title-switcher-link-text' ),
+ $this->msg( 'mapeditor-form-field-title' ),
+ $this->msg( 'mapeditor-form-field-text' ),
+ $this->msg( 'mapeditor-form-field-link' ),
+ $this->msg( 'mapeditor-form-field-icon' ),
+ $this->msg( 'mapeditor-form-field-group' ),
+ $this->msg( 'mapeditor-form-field-inlinelabel' ),
+ $this->msg( 'mapeditor-form-field-strokecolor' ),
+ $this->msg( 'mapeditor-form-field-strokeopacity' ),
+ $this->msg( 'mapeditor-form-field-strokeweight' ),
+ $this->msg( 'mapeditor-form-field-fillcolor' ),
+ $this->msg( 'mapeditor-form-field-fillopcaity' ),
+ $this->msg( 'mapeditor-form-field-showonhover' ),
+ $this->msg( 'mapeditor-mapparam-title' ),
+ $this->msg( 'mapeditor-mapparam-defoption' ),
+ $this->msg( 'mapeditor-form-field-image' ),
+ $this->msg( 'mapeditor-imageoverlay-title' ),
+ $this->msg( 'mapeditor-form-field-visitedicon' )
+ );
+
+ return $html;
+ }
+
+ /**
+ * @return string
+ * @since 2.1
+ */
+ public function getCanvasDiv() {
+ return Html::element(
+ 'div',
+ $this->attribs
+ );
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/src/MediaWiki/Specials/SpecialMapEditor.php b/www/wiki/extensions/Maps/src/MediaWiki/Specials/SpecialMapEditor.php
new file mode 100644
index 00000000..76b5dad5
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/MediaWiki/Specials/SpecialMapEditor.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Maps\MediaWiki\Specials;
+
+use Maps\GoogleMapsService;
+use Maps\MediaWiki\Specials\MapEditorHtml;
+use SpecialPage;
+
+/**
+ * Special page with map editor interface using Google Maps.
+ *
+ * @since 2.0
+ *
+ * @licence GNU GPL v2+
+ * @author Kim Eik
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class SpecialMapEditor extends SpecialPage {
+
+ /**
+ * @see SpecialPage::__construct
+ *
+ * @since 2.0
+ */
+ public function __construct() {
+ parent::__construct( 'MapEditor' );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ *
+ * @since 2.0
+ *
+ * @param null|string $subPage
+ */
+ public function execute( $subPage ) {
+ $this->setHeaders();
+
+ $outputPage = $this->getOutput();
+
+ $outputPage->addHtml(
+ GoogleMapsService::getApiScript(
+ $this->getLanguage()->getCode(),
+ [ 'libraries' => 'drawing' ]
+ )
+ );
+
+ $outputPage->addModules( 'mapeditor' );
+ $editorHtml = new MapEditorHtml( $this->getAttribs() );
+ $html = $editorHtml->getEditorHtml();
+ $outputPage->addHTML( $html );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return array
+ */
+ protected function getAttribs() {
+ return [
+ 'id' => 'map-canvas',
+ 'context' => 'Maps\MediaWiki\Specials\SpecialMapEditor'
+ ];
+ }
+
+ protected function getGroupName() {
+ return 'maps';
+ }
+}
diff --git a/www/wiki/extensions/Maps/src/Presentation/CoordinateFormatter.php b/www/wiki/extensions/Maps/src/Presentation/CoordinateFormatter.php
new file mode 100644
index 00000000..6b5d413d
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Presentation/CoordinateFormatter.php
@@ -0,0 +1,36 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Maps\Presentation;
+
+use DataValues\Geo\Formatters\LatLongFormatter;
+use DataValues\Geo\Values\LatLongValue;
+use ValueFormatters\FormatterOptions;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class CoordinateFormatter {
+
+ private const PRECISION_MAP = [
+ 'dms' => 1 / 360000,
+ 'dm' => 1 / 600000,
+ 'dd' => 1 / 1000000,
+ 'float' => 1 / 1000000,
+ ];
+
+ public function format( LatLongValue $latLong, string $format, bool $directional ) {
+ $formatter = new LatLongFormatter( new FormatterOptions(
+ [
+ LatLongFormatter::OPT_FORMAT => $format,
+ LatLongFormatter::OPT_DIRECTIONAL => $directional,
+ LatLongFormatter::OPT_PRECISION => self::PRECISION_MAP[$format]
+ ]
+ ) );
+
+ return $formatter->format( $latLong );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Presentation/ElementJsonSerializer.php b/www/wiki/extensions/Maps/src/Presentation/ElementJsonSerializer.php
new file mode 100644
index 00000000..6c4e33c5
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Presentation/ElementJsonSerializer.php
@@ -0,0 +1,35 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Maps\Presentation;
+
+
+use Maps\Elements\BaseElement;
+
+class ElementJsonSerializer {
+
+ private $parser;
+
+ public function __construct( WikitextParser $parser ) {
+ $this->parser = $parser;
+ }
+
+ public function elementToJson( BaseElement $element ): array {
+ $json = $element->getArrayValue();
+
+ $this->titleAndText( $json );
+
+ return $json;
+ }
+
+ public function titleAndText( array &$elementJson ) {
+ $elementJson['title'] = $this->parser->wikitextToHtml( $elementJson['title'] );
+ $elementJson['text'] = $this->parser->wikitextToHtml( $elementJson['text'] );
+
+ $hasTitleAndText = $elementJson['title'] !== '' && $elementJson['text'] !== '';
+ $elementJson['text'] = ( $hasTitleAndText ? '<b>' . $elementJson['title'] . '</b>' : $elementJson['title'] ) . $elementJson['text'];
+ $elementJson['title'] = strip_tags( $elementJson['title'] );
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/src/Presentation/KmlFormatter.php b/www/wiki/extensions/Maps/src/Presentation/KmlFormatter.php
new file mode 100644
index 00000000..90df6d1e
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Presentation/KmlFormatter.php
@@ -0,0 +1,78 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Maps\Presentation;
+
+use Maps\Elements\Location;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class KmlFormatter {
+
+ /**
+ * Builds and returns KML representing the set geographical objects.
+ */
+ public function formatLocationsAsKml( Location ...$locations ): string {
+ $elements = $this->getKmlForLocations( $locations );
+
+ // http://earth.google.com/kml/2.2
+ return <<<EOT
+<?xml version="1.0" encoding="UTF-8"?>
+<kml xmlns="http://www.opengis.net/kml/2.2">
+ <Document>
+$elements
+ </Document>
+</kml>
+EOT;
+ }
+
+ private function getKmlForLocations( array $locations ): string {
+ return implode(
+ "\n",
+ array_map(
+ function( Location $location ) {
+ return $this->locationToKmlPlacemark( $location );
+ },
+ $locations
+ )
+ );
+ }
+
+
+ private function locationToKmlPlacemark( Location $location ): string {
+ // TODO: escaping?
+ $name = '<name><![CDATA[' . $location->getTitle() . ']]></name>';
+
+ // TODO: escaping?
+ $description = '<description><![CDATA[' . $location->getText() . ']]></description>';
+
+ $coordinates = '<coordinates>'
+ . $this->escapeValue( $this->getCoordinateString( $location ) )
+ . '</coordinates>';
+
+ return <<<EOT
+ <Placemark>
+ $name
+ $description
+ <Point>
+ $coordinates
+ </Point>
+ </Placemark>
+EOT;
+ }
+
+ private function getCoordinateString( Location $location ): string {
+ // lon,lat[,alt]
+ return $location->getCoordinates()->getLongitude()
+ . ',' . $location->getCoordinates()->getLatitude()
+ . ',0';
+ }
+
+ private function escapeValue( string $value ): string {
+ return htmlspecialchars( $value, ENT_NOQUOTES );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Presentation/MapHtmlBuilder.php b/www/wiki/extensions/Maps/src/Presentation/MapHtmlBuilder.php
new file mode 100644
index 00000000..aa47e558
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Presentation/MapHtmlBuilder.php
@@ -0,0 +1,37 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Maps\Presentation;
+
+use FormatJson;
+use Html;
+
+class MapHtmlBuilder {
+
+ public function getMapHTML( array $params, string $mapName, string $serviceName ): string {
+ if ( is_int( $params['height'] ) ) {
+ $params['height'] = (string)$params['height'] . 'px';
+ }
+
+ if ( is_int( $params['width'] ) ) {
+ $params['width'] = (string)$params['width'] . 'px';
+ }
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'id' => $mapName,
+ 'style' => "width: {$params['width']}; height: {$params['height']}; background-color: #cccccc; overflow: hidden;",
+ 'class' => 'maps-map maps-' . $serviceName
+ ],
+ wfMessage( 'maps-loading-map' )->inContentLanguage()->escaped() .
+ Html::element(
+ 'div',
+ [ 'style' => 'display:none', 'class' => 'mapdata' ],
+ FormatJson::encode( $params )
+ )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Presentation/MapsDistanceParser.php b/www/wiki/extensions/Maps/src/Presentation/MapsDistanceParser.php
new file mode 100644
index 00000000..c8fb0ef1
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Presentation/MapsDistanceParser.php
@@ -0,0 +1,133 @@
+<?php
+
+namespace Maps\Presentation;
+
+/**
+ * Static class for distance validation and parsing. Internal representations are in meters.
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class MapsDistanceParser {
+
+ private static $validatedDistanceUnit = false;
+
+ private static $unitRegex = false;
+
+ public static function parseAndFormat( string $distance, string $unit = null, int $decimals = 2 ): string {
+ return self::formatDistance( self::parseDistance( $distance ), $unit, $decimals );
+ }
+
+ /**
+ * Formats a given distance in meters to a distance in an optionally specified notation.
+ */
+ public static function formatDistance( float $meters, string $unit = null, int $decimals = 2 ): string {
+ global $wgContLang;
+ $meters = $wgContLang->formatNum( round( $meters / self::getUnitRatio( $unit ), $decimals ) );
+ return "$meters $unit";
+ }
+
+ /**
+ * Returns the unit to meter ratio in a safe way, by first resolving the unit.
+ */
+ public static function getUnitRatio( string $unit = null ): float {
+ global $egMapsDistanceUnits;
+ return $egMapsDistanceUnits[self::getValidUnit( $unit )];
+ }
+
+ /**
+ * Returns a valid unit. If the provided one is invalid, the default will be used.
+ */
+ public static function getValidUnit( string $unit = null ): string {
+ global $egMapsDistanceUnit, $egMapsDistanceUnits;
+
+ // This ensures the value for $egMapsDistanceUnit is correct, and caches the result.
+ if ( self::$validatedDistanceUnit === false ) {
+ if ( !array_key_exists( $egMapsDistanceUnit, $egMapsDistanceUnits ) ) {
+ $units = array_keys( $egMapsDistanceUnits );
+ $egMapsDistanceUnit = $units[0];
+ }
+
+ self::$validatedDistanceUnit = true;
+ }
+
+ if ( $unit == null || !array_key_exists( $unit, $egMapsDistanceUnits ) ) {
+ $unit = $egMapsDistanceUnit;
+ }
+
+ return $unit;
+ }
+
+ /**
+ * Parses a distance optionally containing a unit to a float value in meters.
+ *
+ * @param string $distance
+ *
+ * @return float|false The distance in meters or false on failure
+ */
+ public static function parseDistance( string $distance ) {
+ if ( !self::isDistance( $distance ) ) {
+ return false;
+ }
+
+ $distance = self::normalizeDistance( $distance );
+
+ self::initUnitRegex();
+
+ $matches = [];
+ preg_match( '/^\d+(\.\d+)?\s?(' . self::$unitRegex . ')?$/', $distance, $matches );
+
+ $value = (float)( $matches[0] . $matches[1] );
+ $value *= self::getUnitRatio( $matches[2] );
+
+ return $value;
+ }
+
+ public static function isDistance( string $distance ): bool {
+ $distance = self::normalizeDistance( $distance );
+
+ self::initUnitRegex();
+
+ return (bool)preg_match( '/^\d+(\.\d+)?\s?(' . self::$unitRegex . ')?$/', $distance );
+ }
+
+ /**
+ * Normalizes a potential distance by removing spaces and turning comma's into dots.
+ */
+ protected static function normalizeDistance( string $distance ): string {
+ $distance = trim( (string)$distance );
+ $strlen = strlen( $distance );
+
+ for ( $i = 0; $i < $strlen; $i++ ) {
+ if ( !ctype_digit( $distance{$i} ) && !in_array( $distance{$i}, [ ',', '.' ] ) ) {
+ $value = substr( $distance, 0, $i );
+ $unit = substr( $distance, $i );
+ break;
+ }
+ }
+
+ $value = str_replace( ',', '.', isset( $value ) ? $value : $distance );
+
+ if ( isset( $unit ) ) {
+ $value .= ' ' . str_replace( [ ' ', "\t" ], '', $unit );
+ }
+
+ return $value;
+ }
+
+ private static function initUnitRegex() {
+ if ( self::$unitRegex === false ) {
+ global $egMapsDistanceUnits;
+ self::$unitRegex = implode( '|', array_keys( $egMapsDistanceUnits ) ) . '|';
+ }
+ }
+
+ /**
+ * Returns a list of all supported units.
+ */
+ public static function getUnits(): array {
+ global $egMapsDistanceUnits;
+ return array_keys( $egMapsDistanceUnits );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Presentation/ParameterExtractor.php b/www/wiki/extensions/Maps/src/Presentation/ParameterExtractor.php
new file mode 100644
index 00000000..33a93658
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Presentation/ParameterExtractor.php
@@ -0,0 +1,47 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Maps\Presentation;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class ParameterExtractor {
+
+ /**
+ * Extracts the value of a parameter from a parameter list.
+ *
+ * @param string[] $parameterNames Name and aliases of the parameter. First match gets used
+ * @param string[] $rawParameters Parameters that did not get processed further than being put in a key-value map
+ *
+ * @return string|null
+ */
+ public function extract( array $parameterNames, array $rawParameters ) {
+ foreach( $parameterNames as $parameterName ) {
+ foreach ( $rawParameters as $rawName => $rawValue ) {
+ if ( trim( strtolower( $rawName ) ) === $parameterName ) {
+ return trim( $rawValue );
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public static function extractFromKeyValueStrings( array $keyValueStrings ) {
+ $rawParameters = [];
+
+ foreach ( $keyValueStrings as $keyValueString ) {
+ $parts = explode( '=', $keyValueString, 2 );
+
+ if ( count( $parts ) === 2 ) {
+ $rawParameters[$parts[0]] = $parts[1];
+ }
+ }
+
+ return $rawParameters;
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Presentation/WikitextParser.php b/www/wiki/extensions/Maps/src/Presentation/WikitextParser.php
new file mode 100644
index 00000000..e0c2601a
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Presentation/WikitextParser.php
@@ -0,0 +1,30 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Maps\Presentation;
+
+use Parser;
+use ParserOptions;
+
+class WikitextParser {
+
+ private $parser;
+
+ public function __construct( Parser $parser ) {
+ $this->parser = $parser;
+ }
+
+ public function wikitextToHtml( string $text ): string {
+ if ( trim( $text ) === '' ) {
+ return '';
+ }
+
+ return $this->parser->parse(
+ $text,
+ $this->parser->getTitle(),
+ new ParserOptions()
+ )->getText();
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/CircleParser.php b/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/CircleParser.php
new file mode 100644
index 00000000..110064c0
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/CircleParser.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Maps\Presentation\WikitextParsers;
+
+use DataValues\Geo\Values\LatLongValue;
+use Jeroen\SimpleGeocoder\Geocoder;
+use Maps\Elements\Circle;
+use Maps\MapsFactory;
+use ValueParsers\ParseException;
+use ValueParsers\StringValueParser;
+use ValueParsers\ValueParser;
+
+/**
+ * @since 3.0
+ *
+ * @licence GNU GPL v2+
+ * @author Kim Eik
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class CircleParser implements ValueParser {
+
+ private $metaDataSeparator = '~';
+
+ private $geocoder;
+
+ public function __construct( $geocoder = null ) {
+ $this->geocoder = $geocoder instanceof Geocoder ? $geocoder : MapsFactory::newDefault()->getGeocoder();
+ }
+
+ /**
+ * @see StringValueParser::stringParse
+ *
+ * @since 3.0
+ *
+ * @param string $value
+ *
+ * @return Circle
+ */
+ public function parse( $value ) {
+ $metaData = explode( $this->metaDataSeparator, $value );
+ $circleData = explode( ':', array_shift( $metaData ) );
+
+ $circle = new Circle( $this->stringToLatLongValue( $circleData[0] ), (float)$circleData[1] );
+
+ if ( $metaData !== [] ) {
+ $circle->setTitle( array_shift( $metaData ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $circle->setText( array_shift( $metaData ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $circle->setStrokeColor( array_shift( $metaData ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $circle->setStrokeOpacity( array_shift( $metaData ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $circle->setStrokeWeight( array_shift( $metaData ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $circle->setFillColor( array_shift( $metaData ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $circle->setFillOpacity( array_shift( $metaData ) );
+ }
+
+ return $circle;
+ }
+
+ private function stringToLatLongValue( string $location ): LatLongValue {
+ $latLong = $this->geocoder->geocode( $location );
+
+ if ( $latLong === null ) {
+ throw new ParseException( 'Failed to parse or geocode' );
+ }
+
+ return $latLong;
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/DistanceParser.php b/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/DistanceParser.php
new file mode 100644
index 00000000..2f90e9fc
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/DistanceParser.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Maps\Presentation\WikitextParsers;
+
+use ValueParsers\ParseException;
+use ValueParsers\StringValueParser;
+
+/**
+ * ValueParser that parses the string representation of a distance.
+ *
+ * @since 3.0
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class DistanceParser extends StringValueParser {
+
+ /**
+ * @see StringValueParser::stringParse
+ *
+ * @since 3.0
+ *
+ * @param string $value
+ *
+ * @return float
+ * @throws ParseException
+ */
+ public function stringParse( $value ) {
+ $distance = \Maps\Presentation\MapsDistanceParser::parseDistance( $value );
+
+ if ( is_float( $distance ) ) {
+ return $distance;
+ }
+
+ throw new ParseException( 'Not a distance' );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/ImageOverlayParser.php b/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/ImageOverlayParser.php
new file mode 100644
index 00000000..c2d81591
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/ImageOverlayParser.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Maps\Presentation\WikitextParsers;
+
+use DataValues\Geo\Values\LatLongValue;
+use Jeroen\SimpleGeocoder\Geocoder;
+use Maps\Elements\ImageOverlay;
+use Maps\MapsFactory;
+use ValueParsers\ParseException;
+use ValueParsers\ValueParser;
+
+/**
+ * @since 3.1
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class ImageOverlayParser implements ValueParser {
+
+ private $geocoder;
+
+ public function __construct( $geocoder = null ) {
+ $this->geocoder = $geocoder instanceof Geocoder ? $geocoder : MapsFactory::newDefault()->getGeocoder();
+ }
+
+ /**
+ * @since 3.1
+ *
+ * @param string $value
+ *
+ * @return ImageOverlay
+ * @throws ParseException
+ */
+ public function parse( $value ) {
+ $metaData = explode( '~', $value );
+ $imageParameters = explode( ':', array_shift( $metaData ), 3 );
+
+ if ( count( $imageParameters ) !== 3 ) {
+ throw new ParseException( 'Need 3 parameters for an image overlay' );
+ }
+
+ $boundsNorthEast = $this->stringToLatLongValue( $imageParameters[0] );
+ $boundsSouthWest = $this->stringToLatLongValue( $imageParameters[1] );
+ $imageUrl = \Maps\MapsFunctions::getFileUrl( $imageParameters[2] );
+
+ $overlay = new ImageOverlay( $boundsNorthEast, $boundsSouthWest, $imageUrl );
+
+ if ( $metaData !== [] ) {
+ $overlay->setTitle( array_shift( $metaData ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $overlay->setText( array_shift( $metaData ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $overlay->setLink( $this->getUrlFromLinkString( array_shift( $metaData ) ) );
+ }
+
+ return $overlay;
+ }
+
+ private function getUrlFromLinkString( string $linkString ): string {
+ $linkPrefix = 'link:';
+
+ if ( substr( $linkString, 0, strlen( $linkPrefix ) ) === $linkPrefix ) {
+ return substr( $linkString, strlen( $linkPrefix ) );
+ }
+
+ return $linkString;
+ }
+
+ private function stringToLatLongValue( string $location ): LatLongValue {
+ $latLong = $this->geocoder->geocode( $location );
+
+ if ( $latLong === null ) {
+ throw new ParseException( 'Failed to parse or geocode' );
+ }
+
+ return $latLong;
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/LineParser.php b/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/LineParser.php
new file mode 100644
index 00000000..4e25a1f7
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/LineParser.php
@@ -0,0 +1,163 @@
+<?php
+
+namespace Maps\Presentation\WikitextParsers;
+
+use DataValues\Geo\Values\LatLongValue;
+use Jeroen\SimpleGeocoder\Geocoder;
+use Maps\Elements\Line;
+use Maps\MapsFactory;
+use ValueParsers\StringValueParser;
+use ValueParsers\ValueParser;
+
+/**
+ * ValueParser that parses the string representation of a line.
+ *
+ * @since 3.0
+ *
+ * @licence GNU GPL v2+
+ * @author Kim Eik
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class LineParser implements ValueParser {
+
+ private $metaDataSeparator = '~';
+
+ private $geocoder = null;
+
+ public function setGeocoder( Geocoder $geocoder ) {
+ $this->geocoder = $geocoder;
+ }
+
+ private function getGeocoder(): Geocoder {
+ if ( $this->geocoder == null ) {
+ $this->geocoder = MapsFactory::newDefault()->getGeocoder();
+ }
+
+ return $this->geocoder;
+ }
+
+ /**
+ * @see StringValueParser::stringParse
+ *
+ * @since 3.0
+ *
+ * @param string $value
+ *
+ * @return Line
+ */
+ public function parse( $value ) {
+ $parts = explode( $this->metaDataSeparator, $value );
+
+ $line = $this->constructShapeFromLatLongValues(
+ $this->parseCoordinates(
+ explode( ':', array_shift( $parts ) )
+ )
+ );
+
+ $this->handleCommonParams( $parts, $line );
+
+ return $line;
+ }
+
+ protected function constructShapeFromLatLongValues( array $locations ) {
+ return new Line( $locations );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string[] $coordinateStrings
+ *
+ * @return LatLongValue[]
+ */
+ protected function parseCoordinates( array $coordinateStrings ): array {
+ $coordinates = [];
+
+ foreach ( $coordinateStrings as $coordinateString ) {
+ $coordinate = $this->getGeocoder()->geocode( $coordinateString );
+
+ if ( $coordinate === null ) {
+ // TODO: good if the user knows something has been omitted
+ } else {
+ $coordinates[] = $coordinate;
+ }
+ }
+
+ return $coordinates;
+ }
+
+ /**
+ * This method requires that parameters are positionally correct,
+ * 1. Link (one parameter) or bubble data (two parameters)
+ * 2. Stroke data (three parameters)
+ * 3. Fill data (two parameters)
+ * e.g ...title~text~strokeColor~strokeOpacity~strokeWeight~fillColor~fillOpacity
+ *
+ * @since 3.0
+ *
+ * @param array $params
+ * @param Line $line
+ */
+ protected function handleCommonParams( array &$params, Line &$line ) {
+ //Handle bubble and link parameters
+
+ //create link data
+ $linkOrTitle = array_shift( $params );
+ if ( $link = $this->isLinkParameter( $linkOrTitle ) ) {
+ $this->setLinkFromParameter( $line, $link );
+ } else {
+ //create bubble data
+ $this->setBubbleDataFromParameter( $line, $params, $linkOrTitle );
+ }
+
+ //handle stroke parameters
+ if ( $color = array_shift( $params ) ) {
+ $line->setStrokeColor( $color );
+ }
+
+ if ( $opacity = array_shift( $params ) ) {
+ $line->setStrokeOpacity( $opacity );
+ }
+
+ if ( $weight = array_shift( $params ) ) {
+ $line->setStrokeWeight( $weight );
+ }
+ }
+
+ /**
+ * Checks if a string is prefixed with link:
+ *
+ * @static
+ *
+ * @param $link
+ *
+ * @return bool|string
+ * @since 2.0
+ */
+ private function isLinkParameter( $link ) {
+ if ( strpos( $link, 'link:' ) === 0 ) {
+ return substr( $link, 5 );
+ }
+
+ return false;
+ }
+
+ protected function setLinkFromParameter( Line &$line, $link ) {
+ if ( filter_var( $link, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED ) ) {
+ $line->setLink( $link );
+ } else {
+ $title = \Title::newFromText( $link );
+ $line->setLink( $title->getFullURL() );
+ }
+ }
+
+ protected function setBubbleDataFromParameter( Line &$line, &$params, $title ) {
+ if ( $title ) {
+ $line->setTitle( $title );
+ }
+ if ( $text = array_shift( $params ) ) {
+ $line->setText( $text );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/LocationParser.php b/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/LocationParser.php
new file mode 100644
index 00000000..26af76ef
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/LocationParser.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace Maps\Presentation\WikitextParsers;
+
+use DataValues\Geo\Parsers\LatLongParser;
+use Jeroen\SimpleGeocoder\Geocoder;
+use Maps\Elements\Location;
+use Maps\FileUrlFinder;
+use Maps\MapsFactory;
+use Title;
+use ValueParsers\ParseException;
+use ValueParsers\StringValueParser;
+use ValueParsers\ValueParser;
+
+/**
+ * ValueParser that parses the string representation of a location.
+ *
+ * @since 3.0
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class LocationParser implements ValueParser {
+
+ private $geocoder;
+ private $fileUrlFinder;
+ private $useAddressAsTitle;
+
+ /**
+ * @deprecated Use newInstance instead
+ */
+ public function __construct( $enableLegacyCrud = true ) {
+ if ( $enableLegacyCrud ) {
+ $this->geocoder = MapsFactory::globalInstance()->getGeocoder();
+ $this->fileUrlFinder = MapsFactory::globalInstance()->getFileUrlFinder();
+ $this->useAddressAsTitle = false;
+ }
+ }
+
+ public static function newInstance( Geocoder $geocoder, FileUrlFinder $fileUrlFinder, bool $useAddressAsTitle = false ): self {
+ $instance = new self( false );
+ $instance->geocoder = $geocoder;
+ $instance->fileUrlFinder = $fileUrlFinder;
+ $instance->useAddressAsTitle = $useAddressAsTitle;
+ return $instance;
+ }
+
+ /**
+ * @see StringValueParser::stringParse
+ *
+ * @since 3.0
+ *
+ * @param string $value
+ *
+ * @return Location
+ * @throws ParseException
+ */
+ public function parse( $value ) {
+ $separator = '~';
+
+ $metaData = explode( $separator, $value );
+
+ $coordinatesOrAddress = array_shift( $metaData );
+ $coordinates = $this->geocoder->geocode( $coordinatesOrAddress );
+
+ if ( $coordinates === null ) {
+ throw new ParseException( 'Location is not a parsable coordinate and not a geocodable address' );
+ }
+
+ $location = new Location( $coordinates );
+
+ if ( $metaData !== [] ) {
+ $this->setTitleOrLink( $location, array_shift( $metaData ) );
+ } else {
+ if ( $this->useAddressAsTitle && $this->isAddress( $coordinatesOrAddress ) ) {
+ $location->setTitle( $coordinatesOrAddress );
+ }
+ }
+
+ if ( $metaData !== [] ) {
+ $location->setText( array_shift( $metaData ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $location->setIcon( $this->fileUrlFinder->getUrlForFileName( array_shift( $metaData ) ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $location->setGroup( array_shift( $metaData ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $location->setInlineLabel( array_shift( $metaData ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $location->setVisitedIcon( $this->fileUrlFinder->getUrlForFileName( array_shift( $metaData ) ) ) ;
+ }
+
+ return $location;
+ }
+
+ private function setTitleOrLink( Location $location, $titleOrLink ) {
+ if ( $this->isLink( $titleOrLink ) ) {
+ $this->setLink( $location, $titleOrLink );
+ } else {
+ $location->setTitle( $titleOrLink );
+ }
+ }
+
+ private function isLink( $value ) {
+ return strpos( $value, 'link:' ) === 0;
+ }
+
+ private function setLink( Location $location, $link ) {
+ $link = substr( $link, 5 );
+ $location->setLink( $this->getExpandedLink( $link ) );
+ }
+
+ private function getExpandedLink( $link ) {
+ if ( filter_var( $link, FILTER_VALIDATE_URL ) ) {
+ return $link;
+ }
+
+ $title = Title::newFromText( $link );
+
+ if ( $title === null ) {
+ return '';
+ }
+
+ return $title->getFullURL();
+ }
+
+ private function isAddress( string $coordsOrAddress ): bool {
+ $coordinateParser = new LatLongParser();
+
+ try {
+ $coordinateParser->parse( $coordsOrAddress );
+ }
+ catch ( ParseException $parseException ) {
+ return true;
+ }
+
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/PolygonParser.php b/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/PolygonParser.php
new file mode 100644
index 00000000..1bf14a7f
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/PolygonParser.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Maps\Presentation\WikitextParsers;
+
+use Maps\Elements\Line;
+use Maps\Elements\Polygon;
+
+/**
+ * ValueParser that parses the string representation of a polygon.
+ *
+ * @since 3.0
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class PolygonParser extends LineParser {
+
+ protected function constructShapeFromLatLongValues( array $locations ) {
+ return new Polygon( $locations );
+ }
+
+ protected function handleCommonParams( array &$params, Line &$line ) {
+ parent::handleCommonParams( $params, $line );
+ $this->handlePolygonParams( $params, $line );
+ }
+
+ protected function handlePolygonParams( array &$params, Polygon &$polygon ) {
+ if ( $fillColor = array_shift( $params ) ) {
+ $polygon->setFillColor( $fillColor );
+ }
+
+ if ( $fillOpacity = array_shift( $params ) ) {
+ $polygon->setFillOpacity( $fillOpacity );
+ }
+
+ if ( $showOnlyOnHover = array_shift( $params ) ) {
+ $polygon->setOnlyVisibleOnHover( strtolower( trim( $showOnlyOnHover ) ) === 'on' );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/RectangleParser.php b/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/RectangleParser.php
new file mode 100644
index 00000000..1b694a27
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/RectangleParser.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Maps\Presentation\WikitextParsers;
+
+use DataValues\Geo\Values\LatLongValue;
+use Jeroen\SimpleGeocoder\Geocoder;
+use Maps\Elements\Rectangle;
+use Maps\MapsFactory;
+use ValueParsers\ParseException;
+use ValueParsers\StringValueParser;
+use ValueParsers\ValueParser;
+
+/**
+ * @since 3.0
+ *
+ * @licence GNU GPL v2+
+ * @author Kim Eik
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class RectangleParser implements ValueParser {
+
+ private $metaDataSeparator = '~';
+
+ private $geocoder;
+
+ public function __construct( $geocoder = null ) {
+ $this->geocoder = $geocoder instanceof Geocoder ? $geocoder : MapsFactory::newDefault()->getGeocoder();
+ }
+
+ /**
+ * @see StringValueParser::stringParse
+ *
+ * @since 3.0
+ *
+ * @param string $value
+ *
+ * @return Rectangle
+ */
+ public function parse( $value ) {
+ $metaData = explode( $this->metaDataSeparator, $value );
+ $rectangleData = explode( ':', array_shift( $metaData ) );
+
+ $rectangle = new Rectangle(
+ $this->stringToLatLongValue( $rectangleData[0] ),
+ $this->stringToLatLongValue( $rectangleData[1] )
+ );
+
+ if ( $metaData !== [] ) {
+ $rectangle->setTitle( array_shift( $metaData ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $rectangle->setText( array_shift( $metaData ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $rectangle->setStrokeColor( array_shift( $metaData ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $rectangle->setStrokeOpacity( array_shift( $metaData ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $rectangle->setStrokeWeight( array_shift( $metaData ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $rectangle->setFillColor( array_shift( $metaData ) );
+ }
+
+ if ( $metaData !== [] ) {
+ $rectangle->setFillOpacity( array_shift( $metaData ) );
+ }
+
+ return $rectangle;
+ }
+
+ private function stringToLatLongValue( string $location ): LatLongValue {
+ $latLong = $this->geocoder->geocode( $location );
+
+ if ( $latLong === null ) {
+ throw new ParseException( 'Failed to parse or geocode' );
+ }
+
+ return $latLong;
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/WmsOverlayParser.php b/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/WmsOverlayParser.php
new file mode 100644
index 00000000..b2b784b9
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/Presentation/WikitextParsers/WmsOverlayParser.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace Maps\Presentation\WikitextParsers;
+
+use Maps\Elements\WmsOverlay;
+use ValueParsers\ParseException;
+use ValueParsers\ValueParser;
+
+/**
+ * ValueParser that parses the string representation of a WMS layer
+ *
+ * @since 3.0
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class WmsOverlayParser implements ValueParser {
+
+ /**
+ * Parses the provided string and returns the result.
+ *
+ * @since 3.0
+ *
+ * @param string $value
+ *
+ * @return WmsOverlay
+ * @throws ParseException
+ */
+ public function parse( $value ) {
+ if ( !is_string( $value ) ) {
+ throw new ParseException( 'Not a string' );
+ }
+
+ $separator = " ";
+ $metaData = explode( $separator, $value );
+
+ if ( count( $metaData ) >= 2 ) {
+ $wmsOverlay = new WmsOverlay( $metaData[0], $metaData[1] );
+ if ( count( $metaData ) == 3 ) {
+ $wmsOverlay->setWmsStyleName( $metaData[2] );
+ }
+
+ return $wmsOverlay;
+ }
+
+ throw new ParseException( 'Need at least two parameters, url to WMS server and map layer name' );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/SemanticMW/DataValues/CoordinateValue.php b/www/wiki/extensions/Maps/src/SemanticMW/DataValues/CoordinateValue.php
new file mode 100644
index 00000000..47de2632
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/SemanticMW/DataValues/CoordinateValue.php
@@ -0,0 +1,266 @@
+<?php
+
+namespace Maps\SemanticMW\DataValues;
+
+use DataValues\Geo\Parsers\LatLongParser;
+use DataValues\Geo\Values\LatLongValue;
+use InvalidArgumentException;
+use Maps\MapsFactory;
+use Maps\Presentation\MapsDistanceParser;
+use SMW\Query\Language\Description;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\QueryComparator;
+use SMWDataItem;
+use SMWDataValue;
+use SMWDIGeoCoord;
+use SMWOutputs;
+use ValueParsers\ParseException;
+
+/**
+ * @property SMWDIGeoCoord m_dataitem
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author Markus Krötzsch
+ */
+class CoordinateValue extends SMWDataValue {
+
+ private $wikiValue;
+
+ /**
+ * Overwrite SMWDataValue::getQueryDescription() to be able to process
+ * comparators between all values.
+ *
+ * @param string $value
+ *
+ * @return Description
+ * @throws InvalidArgumentException
+ */
+ public function getQueryDescription( $value ) {
+ if ( !is_string( $value ) ) {
+ throw new InvalidArgumentException( '$value needs to be a string' );
+ }
+
+ list( $distance, $comparator ) = $this->parseUserValue( $value );
+ $distance = $this->parserDistance( $distance );
+
+ $this->setUserValue( $value );
+
+ switch ( true ) {
+ case !$this->isValid():
+ return new ThingDescription();
+ case $distance !== false:
+ return new \Maps\SemanticMW\ValueDescriptions\AreaDescription(
+ $this->getDataItem(),
+ $comparator,
+ $distance
+ );
+ default:
+ return new \Maps\SemanticMW\ValueDescriptions\CoordinateDescription(
+ $this->getDataItem(),
+ null,
+ $comparator
+ );
+ }
+ }
+
+ /**
+ * @see SMWDataValue::parseUserValue
+ */
+ protected function parseUserValue( $value ) {
+ if ( !is_string( $value ) ) {
+ throw new InvalidArgumentException( '$value needs to be a string' );
+ }
+
+ $this->wikiValue = $value;
+
+ $comparator = SMW_CMP_EQ;
+ $distance = false;
+
+ if ( $value === '' ) {
+ $this->addError( wfMessage( 'smw_novalues' )->text() );
+ } else {
+ $comparator = QueryComparator::getInstance()->extractComparatorFromString( $value );
+
+ list( $coordinates, $distance ) = $this->findValueParts( $value );
+
+ $this->tryParseAndSetDataItem( $coordinates );
+ }
+
+ return [ $distance, $comparator ];
+ }
+
+ private function findValueParts( string $value ): array {
+ $parts = explode( '(', $value );
+
+ $coordinates = trim( array_shift( $parts ) );
+ $distance = count( $parts ) > 0 ? trim( array_shift( $parts ) ) : false;
+
+ return [ $coordinates, $distance ];
+ }
+
+ private function tryParseAndSetDataItem( string $coordinates ) {
+ $parser = new LatLongParser();
+
+ try {
+ $value = $parser->parse( $coordinates );
+ $this->m_dataitem = new SMWDIGeoCoord( $value->getLatitude(), $value->getLongitude() );
+ }
+ catch ( ParseException $parseException ) {
+ $this->addError( wfMessage( 'maps_unrecognized_coords', $coordinates, 1 )->text() );
+
+ // Make sure this is always set
+ // TODO: Why is this needed?!
+ $this->m_dataitem = new SMWDIGeoCoord( [ 'lat' => 0, 'lon' => 0 ] );
+ }
+ }
+
+ private function parserDistance( $distance ) {
+ if ( $distance !== false ) {
+ $distance = substr( trim( $distance ), 0, -1 );
+
+ if ( !MapsDistanceParser::isDistance( $distance ) ) {
+ $this->addError( wfMessage( 'semanticmaps-unrecognizeddistance', $distance )->text() );
+ $distance = false;
+ }
+ }
+
+ return $distance;
+ }
+
+ /**
+ * @see SMWDataValue::getShortHTMLText
+ *
+ * @since 0.6
+ */
+ public function getShortHTMLText( $linker = null ) {
+ return $this->getShortWikiText( $linker );
+ }
+
+ /**
+ * @see SMWDataValue::getShortWikiText
+ */
+ public function getShortWikiText( $linked = null ) {
+ if ( $this->isValid() ) {
+ if ( $this->m_caption === false ) {
+ return $this->getFormattedCoord( $this->m_dataitem );
+ }
+
+ return $this->m_caption;
+ }
+
+ return $this->getErrorText();
+ }
+
+ /**
+ * @param SMWDIGeoCoord $dataItem
+ * @param string|null $format
+ *
+ * @return string|null
+ */
+ private function getFormattedCoord( SMWDIGeoCoord $dataItem, string $format = null ) {
+ return MapsFactory::globalInstance()->getCoordinateFormatter()->format(
+ new LatLongValue(
+ $dataItem->getLatitude(),
+ $dataItem->getLongitude()
+ ),
+ $format ?? $GLOBALS['smgQPCoodFormat'],
+ $GLOBALS['smgQPCoodDirectional']
+ );
+ }
+
+ /**
+ * @see SMWDataValue::getLongHTMLText
+ */
+ public function getLongHTMLText( $linker = null ) {
+ return $this->getLongWikiText( $linker );
+ }
+
+ /**
+ * @see SMWDataValue::getLongWikiText
+ *
+ * @since 0.6
+ */
+ public function getLongWikiText( $linked = null ) {
+ if ( $this->isValid() ) {
+ SMWOutputs::requireHeadItem( SMW_HEADER_TOOLTIP );
+
+ // TODO: fix lang keys so they include the space and coordinates.
+ $coordinateSet = $this->m_dataitem->getCoordinateSet();
+
+ $text = $this->getFormattedCoord( $this->m_dataitem );
+
+ $lines = [
+ wfMessage( 'semanticmaps-latitude', $coordinateSet['lat'] )->inContentLanguage()->escaped(),
+ wfMessage( 'semanticmaps-longitude', $coordinateSet['lon'] )->inContentLanguage()->escaped(),
+ ];
+
+ if ( array_key_exists( 'alt', $coordinateSet ) ) {
+ $lines[] = wfMessage( 'semanticmaps-altitude', $coordinateSet['alt'] )->inContentLanguage()->escaped();
+ }
+
+ return '<span class="smwttinline">' . htmlspecialchars( $text ) . '<span class="smwttcontent">' .
+ implode( '<br />', $lines ) .
+ '</span></span>';
+ } else {
+ return $this->getErrorText();
+ }
+ }
+
+ /**
+ * @see SMWDataValue::getWikiValue
+ */
+ public function getWikiValue() {
+ return $this->wikiValue;
+ }
+
+ /**
+ * @see SMWDataValue::setDataItem
+ *
+ * @param SMWDataItem $dataItem
+ *
+ * @return boolean
+ */
+ protected function loadDataItem( SMWDataItem $dataItem ) {
+ if ( $dataItem instanceof SMWDIGeoCoord ) {
+ $formattedValue = $this->getFormattedCoord( $dataItem );
+
+ if ( $formattedValue !== null ) {
+ $this->wikiValue = $formattedValue;
+ $this->m_dataitem = $dataItem;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Create links to mapping services based on a wiki-editable message. The parameters
+ * available to the message are:
+ *
+ * $1: The location in non-directional float notation.
+ * $2: The location in directional DMS notation.
+ * $3: The latitude in non-directional float notation.
+ * $4 The longitude in non-directional float notation.
+ *
+ * @return array
+ */
+ protected function getServiceLinkParams() {
+ $coordinateSet = $this->m_dataitem->getCoordinateSet();
+ return [
+ $this->getFormattedCoord( $this->m_dataitem, 'float' ), // TODO
+ $this->getFormattedCoord( $this->m_dataitem, 'dms' ), // TODO
+ $coordinateSet['lat'],
+ $coordinateSet['lon']
+ ];
+ }
+
+ /**
+ * @return SMWDIGeoCoord|\SMWDIError
+ */
+ public function getDataItem() {
+ return parent::getDataItem();
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/SemanticMW/ResultPrinters/KmlPrinter.php b/www/wiki/extensions/Maps/src/SemanticMW/ResultPrinters/KmlPrinter.php
new file mode 100644
index 00000000..1bda31c6
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/SemanticMW/ResultPrinters/KmlPrinter.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace Maps\SemanticMW\ResultPrinters;
+
+use Maps\Presentation\KmlFormatter;
+use ParamProcessor\ParamDefinition;
+use SMW\Query\ResultPrinters\FileExportPrinter;
+use SMWQueryResult;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class KmlPrinter extends FileExportPrinter {
+
+ /**
+ * @param SMWQueryResult $res
+ * @param int $outputMode
+ *
+ * @return string
+ */
+ public function getResultText( SMWQueryResult $res, $outputMode ) {
+ if ( $outputMode == SMW_OUTPUT_FILE ) {
+ return $this->getKML( $res, $outputMode );
+ }
+
+ return $this->getKMLLink( $res, $outputMode );
+ }
+
+ private function getKML( SMWQueryResult $res, int $outputMode ): string {
+ $queryHandler = new QueryHandler( $res, $outputMode, $this->params['linkabsolute'] );
+ $queryHandler->setText( $this->params['text'] );
+ $queryHandler->setTitle( $this->params['title'] );
+ $queryHandler->setSubjectSeparator( '' );
+
+ $formatter = new KmlFormatter();
+ return $formatter->formatLocationsAsKml( ...$queryHandler->getLocations() );
+ }
+
+ /**
+ * Returns a link (HTML) pointing to a query that returns the actual KML file.
+ */
+ private function getKMLLink( SMWQueryResult $res, int $outputMode ): string {
+ $searchLabel = $this->getSearchLabel( $outputMode );
+ $link = $res->getQueryLink(
+ $searchLabel ? $searchLabel : wfMessage( 'semanticmaps-kml-link' )->inContentLanguage()->text()
+ );
+ $link->setParameter( 'kml', 'format' );
+ $link->setParameter( $this->params['linkabsolute'] ? 'yes' : 'no', 'linkabsolute' );
+
+ if ( $this->params['title'] !== '' ) {
+ $link->setParameter( $this->params['title'], 'title' );
+ }
+
+ if ( $this->params['text'] !== '' ) {
+ $link->setParameter( $this->params['text'], 'text' );
+ }
+
+ // Fix for offset-error in getQueryLink()
+ // (getQueryLink by default sets offset to point to the next
+ // result set, fix by setting it to 0 if now explicitly set)
+ if ( array_key_exists( 'offset', $this->params ) ) {
+ $link->setParameter( $this->params['offset'], 'offset' );
+ } else {
+ $link->setParameter( 0, 'offset' );
+ }
+
+ if ( array_key_exists( 'limit', $this->params ) ) {
+ $link->setParameter( $this->params['limit'], 'limit' );
+ } else { // Use a reasonable default limit.
+ $link->setParameter( 20, 'limit' );
+ }
+
+ $this->isHTML = ( $outputMode == SMW_OUTPUT_HTML );
+
+ return $link->getText( $outputMode, $this->mLinker );
+ }
+
+ /**
+ * @see SMWResultPrinter::getParamDefinitions
+ *
+ * @param ParamDefinition[] $definitions
+ *
+ * @return array
+ */
+ public function getParamDefinitions( array $definitions ) {
+ global $egMapsDefaultLabel, $egMapsDefaultTitle;
+
+ $definitions['text'] = [
+ 'message' => 'semanticmaps-kml-text',
+ 'default' => $egMapsDefaultLabel,
+ ];
+
+ $definitions['title'] = [
+ 'message' => 'semanticmaps-kml-title',
+ 'default' => $egMapsDefaultTitle,
+ ];
+
+ $definitions['linkabsolute'] = [
+ 'message' => 'semanticmaps-kml-linkabsolute',
+ 'type' => 'boolean',
+ 'default' => true,
+ ];
+
+ return $definitions;
+ }
+
+ /**
+ * @see SMWIExportPrinter::getMimeType
+ *
+ * @param SMWQueryResult $queryResult
+ *
+ * @return string
+ */
+ public function getMimeType( SMWQueryResult $queryResult ) {
+ return 'application/vnd.google-earth.kml+xml';
+ }
+
+ /**
+ * @see SMWIExportPrinter::getFileName
+ *
+ * @param SMWQueryResult $queryResult
+ *
+ * @return string|boolean
+ */
+ public function getFileName( SMWQueryResult $queryResult ) {
+ return 'kml.kml';
+ }
+
+ /**
+ * @see SMWResultPrinter::getName()
+ */
+ public final function getName() {
+ return wfMessage( 'semanticmaps-kml' )->text();
+ }
+
+ /**
+ * @see SMWResultPrinter::handleParameters
+ *
+ * @param array $params
+ * @param $outputMode
+ */
+ protected function handleParameters( array $params, $outputMode ) {
+ $this->params = $params;
+ }
+}
diff --git a/www/wiki/extensions/Maps/src/SemanticMW/ResultPrinters/MapPrinter.php b/www/wiki/extensions/Maps/src/SemanticMW/ResultPrinters/MapPrinter.php
new file mode 100644
index 00000000..6ab945f6
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/SemanticMW/ResultPrinters/MapPrinter.php
@@ -0,0 +1,403 @@
+<?php
+
+namespace Maps\SemanticMW\ResultPrinters;
+
+use FormatJson;
+use Html;
+use Linker;
+use Maps\Elements\BaseElement;
+use Maps\Elements\Location;
+use Maps\FileUrlFinder;
+use Maps\MappingService;
+use Maps\MapsFunctions;
+use Maps\Presentation\ElementJsonSerializer;
+use Maps\Presentation\MapHtmlBuilder;
+use Maps\Presentation\WikitextParser;
+use Maps\Presentation\WikitextParsers\LocationParser;
+use ParamProcessor\ParamDefinition;
+use Parser;
+use SMW\Query\ResultPrinters\ResultPrinter;
+use SMWOutputs;
+use SMWQueryResult;
+use Title;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author Peter Grassberger < petertheone@gmail.com >
+ */
+class MapPrinter extends ResultPrinter {
+
+ private static $services = [];
+
+ /**
+ * @var LocationParser
+ */
+ private $locationParser;
+
+ /**
+ * @var FileUrlFinder
+ */
+ private $fileUrlFinder;
+
+ /**
+ * @var MappingService
+ */
+ private $service;
+
+ /**
+ * @var WikitextParser
+ */
+ private $wikitextParser;
+
+ /**
+ * @var ElementJsonSerializer
+ */
+ private $elementSerializer;
+
+ /**
+ * @var string|boolean
+ */
+ private $fatalErrorMsg = false;
+
+ /**
+ * @param string $format
+ * @param bool $inline
+ */
+ public function __construct( $format, $inline = true ) {
+ $this->service = self::$services[$format];
+
+ parent::__construct( $format, $inline );
+ }
+
+ /**
+ * @since 3.4
+ * FIXME: this is a temporary hack that should be replaced when SMW allows for dependency
+ * injection in query printers.
+ *
+ * @param MappingService $service
+ */
+ public static function registerService( MappingService $service ) {
+ self::$services[$service->getName()] = $service;
+ }
+
+ public static function registerDefaultService( $serviceName ) {
+ self::$services['map'] = self::$services[$serviceName];
+ }
+
+ private function getParser(): Parser {
+ $parser = $GLOBALS['wgParser'];
+
+ if ( $parser instanceof \StubObject ) {
+ return $parser->_newObject();
+ }
+
+ return $parser;
+ }
+
+ private function getParserClone(): Parser {
+ $parser = $this->getParser();
+ return clone $parser;
+ }
+
+ /**
+ * Builds up and returns the HTML for the map, with the queried coordinate data on it.
+ *
+ * @param SMWQueryResult $res
+ * @param int $outputMode
+ *
+ * @return string
+ */
+ public final function getResultText( SMWQueryResult $res, $outputMode ) {
+ if ( $this->fatalErrorMsg !== false ) {
+ return $this->fatalErrorMsg;
+ }
+
+ $factory = \Maps\MapsFactory::newDefault();
+ $this->locationParser = $factory->newLocationParser();
+ $this->fileUrlFinder = $factory->getFileUrlFinder();
+
+ $this->wikitextParser = new WikitextParser( $this->getParserClone() );
+ $this->elementSerializer = new ElementJsonSerializer( $this->wikitextParser );
+
+ $this->addTrackingCategoryIfNeeded();
+
+ $params = $this->params;
+
+ $queryHandler = new QueryHandler( $res, $outputMode );
+ $queryHandler->setLinkStyle( $params['link'] );
+ $queryHandler->setHeaderStyle( $params['headers'] );
+ $queryHandler->setShowSubject( $params['showtitle'] );
+ $queryHandler->setTemplate( $params['template'] );
+ $queryHandler->setUserParam( $params['userparam'] );
+ $queryHandler->setHideNamespace( $params['hidenamespace'] );
+ $queryHandler->setActiveIcon( $params['activeicon'] );
+
+ $this->handleMarkerData( $params, $queryHandler );
+
+ $params['lines'] = $this->elementsToJson( $params['lines'] );
+ $params['polygons'] = $this->elementsToJson( $params['polygons'] );
+ $params['circles'] = $this->elementsToJson( $params['circles'] );
+ $params['rectangles'] = $this->elementsToJson( $params['rectangles'] );
+
+ $params['ajaxquery'] = urlencode( $params['ajaxquery'] );
+
+ if ( $params['locations'] === [] ) {
+ return $params['default'];
+ }
+
+ // We can only take care of the zoom defaulting here,
+ // as not all locations are available in whats passed to Validator.
+ if ( $this->fullParams['zoom']->wasSetToDefault() && count( $params['locations'] ) > 1 ) {
+ $params['zoom'] = false;
+ }
+
+ $mapId = $this->service->newMapId();
+
+ SMWOutputs::requireHeadItem(
+ $mapId,
+ $this->service->getDependencyHtml( $params )
+ );
+
+ foreach ( $this->service->getResourceModules() as $resourceModule ) {
+ SMWOutputs::requireResource( $resourceModule );
+ }
+
+ if ( array_key_exists( 'source', $params ) ) {
+ unset( $params['source'] );
+ }
+
+ return ( new MapHtmlBuilder() )->getMapHTML(
+ $params,
+ $mapId,
+ $this->service->getName()
+ );
+ }
+
+ private function elementsToJson( array $elements ) {
+ return array_map(
+ function( BaseElement $element ) {
+ return $this->elementSerializer->elementToJson( $element );
+ },
+ $elements
+ );
+ }
+
+ private function addTrackingCategoryIfNeeded() {
+ /**
+ * @var Parser $wgParser
+ */
+ global $wgParser;
+
+ if ( $GLOBALS['egMapsEnableCategory'] && $wgParser->getOutput() !== null ) {
+ $wgParser->addTrackingCategory( 'maps-tracking-category' );
+ }
+ }
+
+ /**
+ * Converts the data in the coordinates parameter to JSON-ready objects.
+ * These get stored in the locations parameter, and the coordinates on gets deleted.
+ *
+ * @param array &$params
+ * @param QueryHandler $queryHandler
+ */
+ private function handleMarkerData( array &$params, QueryHandler $queryHandler ) {
+ $params['centre'] = $this->getCenter( $params['centre'] );
+
+ $iconUrl = $this->fileUrlFinder->getUrlForFileName( $params['icon'] );
+ $visitedIconUrl = $this->fileUrlFinder->getUrlForFileName( $params['visitedicon'] );
+
+ $params['locations'] = $this->getJsonForStaticLocations(
+ $params['staticlocations'],
+ $params,
+ $iconUrl,
+ $visitedIconUrl
+ );
+
+ unset( $params['staticlocations'] );
+
+ $params['locations'] = array_merge(
+ $params['locations'],
+ $this->getJsonForLocations(
+ $queryHandler->getLocations(),
+ $params,
+ $iconUrl,
+ $visitedIconUrl
+ )
+ );
+ }
+
+ private function getCenter( $coordinatesOrAddress ) {
+ if ( $coordinatesOrAddress === false ) {
+ return false;
+ }
+
+ try {
+ // FIXME: a Location makes no sense here, since the non-coordinate data is not used
+ $location = $this->locationParser->parse( $coordinatesOrAddress );
+ }
+ catch ( \Exception $ex ) {
+ // TODO: somehow report this to the user
+ return false;
+ }
+
+ return $location->getJSONObject();
+ }
+
+ private function getJsonForStaticLocations( array $staticLocations, array $params, $iconUrl, $visitedIconUrl ) {
+ $locationsJson = [];
+
+ foreach ( $staticLocations as $location ) {
+ $locationsJson[] = $this->getJsonForStaticLocation(
+ $location,
+ $params,
+ $iconUrl,
+ $visitedIconUrl
+ );
+ }
+
+ return $locationsJson;
+ }
+
+ private function getJsonForStaticLocation( Location $location, array $params, $iconUrl, $visitedIconUrl ) {
+ $jsonObj = $location->getJSONObject( $params['title'], $params['label'], $iconUrl, '', '', $visitedIconUrl );
+
+ $this->elementSerializer->titleAndText( $jsonObj );
+
+ if ( $params['pagelabel'] ) {
+ $jsonObj['inlineLabel'] = Linker::link( Title::newFromText( $jsonObj['title'] ) );
+ }
+
+ return $jsonObj;
+ }
+
+ /**
+ * @param Location[] $locations
+ * @param array $params
+ * @param string $iconUrl
+ * @param string $visitedIconUrl
+ *
+ * @return array
+ */
+ private function getJsonForLocations( iterable $locations, array $params, string $iconUrl, string $visitedIconUrl ): array {
+ $locationsJson = [];
+
+ foreach ( $locations as $location ) {
+ $jsonObj = $location->getJSONObject(
+ $params['title'],
+ $params['label'],
+ $iconUrl,
+ '',
+ '',
+ $visitedIconUrl
+ );
+
+ $jsonObj['title'] = strip_tags( $jsonObj['title'] );
+
+ $locationsJson[] = $jsonObj;
+ }
+
+ return $locationsJson;
+ }
+
+ /**
+ * Returns the internationalized name of the mapping service.
+ *
+ * @return string
+ */
+ public final function getName() {
+ return wfMessage( 'maps_' . $this->service->getName() )->text();
+ }
+
+ /**
+ * Returns a list of parameter information, for usage by Special:Ask and others.
+ *
+ * @return array
+ */
+ public function getParameters() {
+ $params = parent::getParameters();
+ $paramInfo = $this->getParameterInfo();
+
+ // Do not display this as an option, as the format already determines it
+ // TODO: this can probably be done cleaner with some changes in Maps
+ unset( $paramInfo['mappingservice'] );
+
+ $params = array_merge( $params, $paramInfo );
+
+ return $params;
+ }
+
+ /**
+ * Returns an array containing the parameter info.
+ *
+ * @return array
+ */
+ private function getParameterInfo() {
+ global $smgQPShowTitle, $smgQPTemplate, $smgQPHideNamespace;
+
+ $params = array_merge(
+ ParamDefinition::getCleanDefinitions( MapsFunctions::getCommonParameters() ),
+ $this->service->getParameterInfo()
+ );
+
+ $params['staticlocations'] = [
+ 'type' => 'mapslocation',
+ 'aliases' => [ 'locations', 'points' ],
+ 'default' => [],
+ 'islist' => true,
+ 'delimiter' => ';',
+ 'message' => 'semanticmaps-par-staticlocations',
+ ];
+
+ $params['showtitle'] = [
+ 'type' => 'boolean',
+ 'aliases' => 'show title',
+ 'default' => $smgQPShowTitle,
+ ];
+
+ $params['hidenamespace'] = [
+ 'type' => 'boolean',
+ 'aliases' => 'hide namespace',
+ 'default' => $smgQPHideNamespace,
+ ];
+
+ $params['template'] = [
+ 'default' => $smgQPTemplate,
+ ];
+
+ $params['userparam'] = [
+ 'default' => '',
+ ];
+
+ $params['activeicon'] = [
+ 'type' => 'string',
+ 'default' => '',
+ ];
+
+ $params['pagelabel'] = [
+ 'type' => 'boolean',
+ 'default' => false,
+ ];
+
+ $params['ajaxcoordproperty'] = [
+ 'default' => '',
+ ];
+
+ $params['ajaxquery'] = [
+ 'default' => '',
+ 'type' => 'string'
+ ];
+
+ // Messages:
+ // semanticmaps-par-staticlocations, semanticmaps-par-showtitle, semanticmaps-par-hidenamespace,
+ // semanticmaps-par-template, semanticmaps-par-userparam, semanticmaps-par-activeicon,
+ // semanticmaps-par-pagelabel, semanticmaps-par-ajaxcoordproperty semanticmaps-par-ajaxquery
+ foreach ( $params as $name => &$data ) {
+ if ( is_array( $data ) && !array_key_exists( 'message', $data ) ) {
+ $data['message'] = 'semanticmaps-par-' . $name;
+ }
+ }
+
+ return $params;
+ }
+}
diff --git a/www/wiki/extensions/Maps/src/SemanticMW/ResultPrinters/QueryHandler.php b/www/wiki/extensions/Maps/src/SemanticMW/ResultPrinters/QueryHandler.php
new file mode 100644
index 00000000..af4d2421
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/SemanticMW/ResultPrinters/QueryHandler.php
@@ -0,0 +1,511 @@
+<?php
+
+namespace Maps\SemanticMW\ResultPrinters;
+
+use Html;
+use Linker;
+use Maps\Elements\Location;
+use Maps\MapsFunctions;
+use Maps\SemanticMW\DataValues\CoordinateValue;
+use SMWDataValue;
+use SMWDIGeoCoord;
+use SMWPrintRequest;
+use SMWQueryResult;
+use SMWResultArray;
+use SMWWikiPageValue;
+use Title;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class QueryHandler {
+
+ /**
+ * The global icon.
+ * @var string
+ */
+ public $icon = '';
+
+ /**
+ * The global text.
+ * @var string
+ */
+ public $text = '';
+
+ /**
+ * The global title.
+ * @var string
+ */
+ public $title = '';
+
+ private $queryResult;
+
+ private $outputMode;
+
+ /**
+ * The template to use for the text, or false if there is none.
+ * @var string|boolean false
+ */
+ private $template = false;
+
+ /**
+ * Should link targets be made absolute (instead of relative)?
+ * @var boolean
+ */
+ private $linkAbsolute;
+
+ /**
+ * A separator to use between the subject and properties in the text field.
+ * @var string
+ */
+ private $subjectSeparator = '<hr />';
+
+ /**
+ * Show the subject in the text or not?
+ * @var boolean
+ */
+ private $showSubject = true;
+
+ /**
+ * Hide the namespace or not.
+ * @var boolean
+ */
+ private $hideNamespace = false;
+
+ /**
+ * Defines which article names in the result are hyperlinked, all normally is the default
+ * none, subject, all
+ */
+ private $linkStyle = 'all';
+
+ /*
+ * Show headers (with links), show headers (just text) or hide them. show is default
+ * show, plain, hide
+ */
+ private $headerStyle = 'show';
+
+ /**
+ * Marker icon to show when marker equals active page
+ * @var string|null
+ */
+ private $activeIcon = null;
+
+ /**
+ * @var string
+ */
+ private $userParam = '';
+
+ public function __construct( SMWQueryResult $queryResult, int $outputMode, bool $linkAbsolute = false ) {
+ $this->queryResult = $queryResult;
+ $this->outputMode = $outputMode;
+ $this->linkAbsolute = $linkAbsolute;
+ }
+
+ public function setTemplate( string $template ) {
+ $this->template = $template === '' ? false : $template;
+ }
+
+ public function setUserParam( string $userParam ) {
+ $this->userParam = $userParam;
+ }
+
+ /**
+ * Sets the global icon.
+ */
+ public function setIcon( string $icon ) {
+ $this->icon = $icon;
+ }
+
+ /**
+ * Sets the global title.
+ */
+ public function setTitle( string $title ) {
+ $this->title = $title;
+ }
+
+ /**
+ * Sets the global text.
+ */
+ public function setText( string $text ) {
+ $this->text = $text;
+ }
+
+ public function setSubjectSeparator( string $subjectSeparator ) {
+ $this->subjectSeparator = $subjectSeparator;
+ }
+
+ public function setShowSubject( bool $showSubject ) {
+ $this->showSubject = $showSubject;
+ }
+
+ public function setLinkStyle( string $link ) {
+ $this->linkStyle = $link;
+ }
+
+ public function setHeaderStyle( string $headers ) {
+ $this->headerStyle = $headers;
+ }
+
+ /**
+ * @return Location[]
+ */
+ public function getLocations(): iterable {
+ while ( ( $row = $this->queryResult->getNext() ) !== false ) {
+ yield from $this->handlePageResult( $row );
+ }
+ }
+
+ /**
+ * @param SMWResultArray[] $row
+ * @return Location[]
+ */
+ private function handlePageResult( array $row ): array {
+ [ $title, $text ] = $this->getTitleAndText( $row[0] );
+ [ $locations, $properties ] = $this->getLocationsAndProperties( $row );
+
+ if ( $properties !== [] && $text !== '' ) {
+ $text .= $this->subjectSeparator;
+ }
+
+ $icon = $this->getLocationIcon( $row );
+
+ return $this->buildLocationsList(
+ $locations,
+ $text,
+ $icon,
+ $properties,
+ Title::newFromText( $title )
+ );
+ }
+
+ private function getTitleAndText( SMWResultArray $resultArray ): array {
+ while ( ( $dataValue = $resultArray->getNextDataValue() ) !== false ) {
+ if ( $dataValue instanceof SMWWikiPageValue ) {
+ return [
+ $dataValue->getLongText( $this->outputMode, null ),
+ $this->getResultSubjectText( $dataValue )
+ ];
+ }
+
+ if ( $dataValue->getTypeID() == '_str' ) {
+ return [
+ $dataValue->getLongText( $this->outputMode, null ),
+ $dataValue->getLongText( $this->outputMode, smwfGetLinker() )
+ ];
+ }
+ }
+
+ return [ '', '' ];
+ }
+
+ /**
+ * @param SMWResultArray[] $row
+ * @return array
+ */
+ private function getLocationsAndProperties( array $row ): array {
+ $locations = [];
+ $properties = [];
+
+ // Loop through all fields of the record.
+ foreach ( $row as $i => $resultArray ) {
+ if ( $i === 0 ) {
+ continue;
+ }
+
+ // Loop through all the parts of the field value.
+ while ( ( $dataValue = $resultArray->getNextDataValue() ) !== false ) {
+ if ( $dataValue instanceof \SMWRecordValue ) {
+ foreach ( $dataValue->getDataItems() as $dataItem ) {
+ if ( $dataItem instanceof \SMWDIGeoCoord ) {
+ $locations[] = $this->locationFromDataItem( $dataItem );
+ }
+ }
+ } elseif ( $dataValue instanceof CoordinateValue ) {
+ $locations[] = $this->locationFromDataItem( $dataValue->getDataItem() );
+ }
+ else {
+ $properties[] = $this->handleResultProperty(
+ $dataValue,
+ $resultArray->getPrintRequest()
+ );
+ }
+ }
+ }
+
+ return [ $locations, $properties ];
+ }
+
+ private function locationFromDataItem( SMWDIGeoCoord $dataItem ): Location {
+ return Location::newFromLatLon(
+ $dataItem->getLatitude(),
+ $dataItem->getLongitude()
+ );
+ }
+
+ /**
+ * Handles a SMWWikiPageValue subject value.
+ * Gets the plain text title and creates the HTML text with headers and the like.
+ *
+ * @param SMWWikiPageValue $object
+ *
+ * @return string
+ */
+ private function getResultSubjectText( SMWWikiPageValue $object ): string {
+ if ( !$this->showSubject ) {
+ return '';
+ }
+
+ $dataItem = $object->getDataItem();
+
+ if ( $this->showArticleLink() ) {
+ if ( $this->linkAbsolute ) {
+ $text = Html::element(
+ 'a',
+ [ 'href' => $dataItem->getTitle()->getFullUrl() ],
+ $this->hideNamespace ? $object->getText() : $dataItem->getTitle()->getFullText()
+ );
+ } else {
+ if ( $this->hideNamespace ) {
+ $text = $object->getShortHTMLText( smwfGetLinker() );
+ } else {
+ $text = $object->getLongHTMLText( smwfGetLinker() );
+ }
+ }
+ } else {
+ $text = $this->hideNamespace ? $object->getText() : $dataItem->getTitle()->getFullText();
+ }
+
+ return '<b>' . $text . '</b>';
+ }
+
+ private function showArticleLink() {
+ return $this->linkStyle !== 'none';
+ }
+
+ /**
+ * Handles a single property (SMWPrintRequest) to be displayed for a record (SMWDataValue).
+ */
+ private function handleResultProperty( SMWDataValue $object, SMWPrintRequest $printRequest ): string {
+ if ( $this->hasTemplate() ) {
+ if ( $object instanceof SMWWikiPageValue ) {
+ return $object->getDataItem()->getTitle()->getPrefixedText();
+ }
+
+ return $object->getLongText( SMW_OUTPUT_WIKI, null );
+ }
+
+ $propertyName = $this->getPropertyName( $printRequest );
+ return $propertyName . ( $propertyName === '' ? '' : ': ' ) . $this->getPropertyValue( $object );
+ }
+
+ private function getPropertyName( SMWPrintRequest $printRequest ): string {
+ if ( $this->headerStyle === 'hide' ) {
+ return '';
+ }
+
+ if ( $this->linkAbsolute ) {
+ $titleText = $printRequest->getText( null );
+ $t = Title::newFromText( $titleText, SMW_NS_PROPERTY );
+
+ if ( $t instanceof Title && $t->exists() ) {
+ return Html::element(
+ 'a',
+ [ 'href' => $t->getFullUrl() ],
+ $printRequest->getHTMLText( null )
+ );
+ }
+
+ return $titleText;
+ }
+
+ return $printRequest->getHTMLText( $this->getPropertyLinker() );
+ }
+
+ private function getPropertyLinker(): ?Linker {
+ return $this->headerStyle === 'show' && $this->linkStyle !== 'none' ? smwfGetLinker() : null;
+ }
+
+ private function getValueLinker(): ?Linker {
+ return $this->linkStyle === 'all' ? smwfGetLinker() : null;
+ }
+
+ private function getPropertyValue( SMWDataValue $object ): string {
+ if ( !$this->linkAbsolute ) {
+ return $object->getLongHTMLText(
+ $this->getValueLinker()
+ );
+ }
+
+ if ( $this->hasPage( $object ) ) {
+ return Html::element(
+ 'a',
+ [
+ 'href' => Title::newFromText(
+ $object->getLongText( $this->outputMode, null ),
+ NS_MAIN
+ )->getFullUrl()
+ ],
+ $object->getLongText( $this->outputMode, null )
+ );
+ }
+
+ return $object->getLongText( $this->outputMode, null );
+ }
+
+ private function hasPage( SMWDataValue $object ): bool {
+ $hasPage = $object->getTypeID() == '_wpg';
+
+ if ( $hasPage ) {
+ $t = Title::newFromText( $object->getLongText( $this->outputMode, null ), NS_MAIN );
+ $hasPage = $t !== null && $t->exists();
+ }
+
+ return $hasPage;
+ }
+
+ private function hasTemplate() {
+ return is_string( $this->template );
+ }
+
+ /**
+ * Get the icon for a row.
+ *
+ * @param array $row
+ *
+ * @return string
+ */
+ private function getLocationIcon( array $row ) {
+ $icon = '';
+ $legendLabels = [];
+
+ //Check for activeicon parameter
+
+ if ( $this->shouldGetActiveIconUrlFor( $row[0]->getResultSubject()->getTitle() ) ) {
+ $icon = MapsFunctions::getFileUrl( $this->activeIcon );
+ }
+
+ // Look for display_options field, which can be set by Semantic Compound Queries
+ // the location of this field changed in SMW 1.5
+ $display_location = method_exists( $row[0], 'getResultSubject' ) ? $row[0]->getResultSubject() : $row[0];
+
+ if ( property_exists( $display_location, 'display_options' ) && is_array(
+ $display_location->display_options
+ ) ) {
+ $display_options = $display_location->display_options;
+ if ( array_key_exists( 'icon', $display_options ) ) {
+ $icon = $display_options['icon'];
+
+ // This is somewhat of a hack - if a legend label has been set, we're getting it for every point, instead of just once per icon
+ if ( array_key_exists( 'legend label', $display_options ) ) {
+
+ $legend_label = $display_options['legend label'];
+
+ if ( !array_key_exists( $icon, $legendLabels ) ) {
+ $legendLabels[$icon] = $legend_label;
+ }
+ }
+ }
+ } // Icon can be set even for regular, non-compound queries If it is, though, we have to translate the name into a URL here
+ elseif ( $this->icon !== '' ) {
+ $icon = MapsFunctions::getFileUrl( $this->icon );
+ }
+
+ return $icon;
+ }
+
+ private function shouldGetActiveIconUrlFor( Title $title ) {
+ global $wgTitle;
+
+ return isset( $this->activeIcon ) && is_object( $wgTitle )
+ && $wgTitle->equals( $title );
+ }
+
+ /**
+ * Builds a set of locations with the provided title, text and icon.
+ *
+ * @param Location[] $locations
+ * @param string $text
+ * @param string $icon
+ * @param array $properties
+ * @param Title|null $title
+ *
+ * @return Location[]
+ */
+ private function buildLocationsList( array $locations, $text, $icon, array $properties, Title $title = null ): array {
+ if ( !$this->hasTemplate() ) {
+ $text .= implode( '<br />', $properties );
+ }
+
+ $titleOutput = $this->getTitleOutput( $title );
+
+ foreach ( $locations as &$location ) {
+ if ( $this->hasTemplate() ) {
+ $segments = array_merge(
+ [
+ $this->template,
+ 'title=' . $titleOutput,
+ 'latitude=' . $location->getCoordinates()->getLatitude(),
+ 'longitude=' . $location->getCoordinates()->getLongitude(),
+ 'userparam=' . $this->userParam
+ ],
+ $properties
+ );
+
+ $text .= $this->getParser()->recursiveTagParseFully(
+ '{{' . implode( '|', $segments ) . '}}'
+ );
+ }
+
+ $location->setTitle( $titleOutput );
+ $location->setText( $text );
+ $location->setIcon( trim( $icon ) );
+ }
+
+ return $locations;
+ }
+
+ private function getTitleOutput( Title $title = null ) {
+ if ( $title === null ) {
+ return '';
+ }
+
+ return $this->hideNamespace ? $title->getText() : $title->getFullText();
+ }
+
+ /**
+ * @return \Parser
+ */
+ private function getParser() {
+ return $GLOBALS['wgParser'];
+ }
+
+ /**
+ * @return boolean
+ */
+ public function getHideNamespace() {
+ return $this->hideNamespace;
+ }
+
+ /**
+ * @param boolean $hideNamespace
+ */
+ public function setHideNamespace( $hideNamespace ) {
+ $this->hideNamespace = $hideNamespace;
+ }
+
+ /**
+ * @return string
+ */
+ public function getActiveIcon() {
+ return $this->activeIcon;
+ }
+
+ /**
+ * @param string $activeIcon
+ */
+ public function setActiveIcon( $activeIcon ) {
+ $this->activeIcon = $activeIcon;
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/SemanticMW/ValueDescriptions/AreaDescription.php b/www/wiki/extensions/Maps/src/SemanticMW/ValueDescriptions/AreaDescription.php
new file mode 100644
index 00000000..ac885095
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/SemanticMW/ValueDescriptions/AreaDescription.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace Maps\SemanticMW\ValueDescriptions;
+
+use DataValues\Geo\Values\LatLongValue;
+use InvalidArgumentException;
+use Maps\GeoFunctions;
+use Maps\Presentation\MapsDistanceParser;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\Query\Language\ValueDescription;
+use SMWDataItem;
+use SMWDIGeoCoord;
+use SMWThingDescription;
+use Wikimedia\Rdbms\IDatabase;
+
+/**
+ * Description of a geographical area defined by a coordinates set and a distance to the bounds.
+ * The bounds are a 'rectangle' (but bend due to the earths curvature), as the resulting query
+ * would otherwise be to resource intensive.
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class AreaDescription extends ValueDescription {
+
+ /**
+ * @var SMWDIGeoCoord
+ */
+ private $center;
+
+ private $radius;
+
+ public function __construct( SMWDataItem $areaCenter, int $comparator, string $radius, DIProperty $property = null ) {
+ if ( !( $areaCenter instanceof SMWDIGeoCoord ) ) {
+ throw new InvalidArgumentException( '$areaCenter needs to be a SMWDIGeoCoord' );
+ }
+
+ parent::__construct( $areaCenter, $property, $comparator );
+
+ $this->center = $areaCenter;
+ $this->radius = $radius;
+ }
+
+ /**
+ * @see Description::prune
+ */
+ public function prune( &$maxsize, &$maxdepth, &$log ) {
+ if ( ( $maxsize < $this->getSize() ) || ( $maxdepth < $this->getDepth() ) ) {
+ $log[] = $this->getQueryString();
+
+ $result = new SMWThingDescription();
+ $result->setPrintRequests( $this->getPrintRequests() );
+
+ return $result;
+ }
+
+ $maxsize = $maxsize - $this->getSize();
+ $maxdepth = $maxdepth - $this->getDepth();
+
+ return $this;
+ }
+
+ public function getQueryString( $asValue = false ) {
+ $centerString = DataValueFactory::getInstance()->newDataValueByItem(
+ $this->center,
+ $this->getProperty()
+ )->getWikiValue();
+
+ $queryString = "$centerString ({$this->radius})";
+
+ return $asValue ? $queryString : "[[$queryString]]";
+ }
+
+ /**
+ * @see SomePropertyInterpreter::mapValueDescription
+ *
+ * FIXME: store specific code should be in the store component
+ *
+ * @param string $tableName
+ * @param string[] $fieldNames
+ * @param IDatabase $db
+ *
+ * @return string|false
+ */
+ public function getSQLCondition( $tableName, array $fieldNames, IDatabase $db ) {
+ if ( $this->center->getDIType() != SMWDataItem::TYPE_GEO ) {
+ throw new \LogicException( 'Constructor should have prevented this' );
+ }
+
+ if ( !$this->comparatorIsSupported() ) {
+ return false;
+ }
+
+ $bounds = $this->getBoundingBox();
+
+ $north = $db->addQuotes( $bounds['north'] );
+ $east = $db->addQuotes( $bounds['east'] );
+ $south = $db->addQuotes( $bounds['south'] );
+ $west = $db->addQuotes( $bounds['west'] );
+
+ $isEq = $this->getComparator() == SMW_CMP_EQ;
+
+ $smallerThen = $isEq ? '<' : '>=';
+ $biggerThen = $isEq ? '>' : '<=';
+ $joinCond = $isEq ? 'AND' : 'OR';
+
+ $conditions = [];
+
+ $conditions[] = "{$tableName}.$fieldNames[1] $smallerThen $north";
+ $conditions[] = "{$tableName}.$fieldNames[1] $biggerThen $south";
+ $conditions[] = "{$tableName}.$fieldNames[2] $smallerThen $east";
+ $conditions[] = "{$tableName}.$fieldNames[2] $biggerThen $west";
+
+ return implode( " $joinCond ", $conditions );
+ }
+
+ private function comparatorIsSupported(): bool {
+ return $this->getComparator() === SMW_CMP_EQ || $this->getComparator() === SMW_CMP_NEQ;
+ }
+
+ /**
+ * @return float[] An associative array containing the limits with keys north, east, south and west.
+ */
+ public function getBoundingBox(): array {
+ $center = new LatLongValue(
+ $this->center->getLatitude(),
+ $this->center->getLongitude()
+ );
+
+ $radiusInMeters = MapsDistanceParser::parseDistance( $this->radius ); // TODO: this can return false
+
+ $north = GeoFunctions::findDestination( $center, 0, $radiusInMeters );
+ $east = GeoFunctions::findDestination( $center, 90, $radiusInMeters );
+ $south = GeoFunctions::findDestination( $center, 180, $radiusInMeters );
+ $west = GeoFunctions::findDestination( $center, 270, $radiusInMeters );
+
+ return [
+ 'north' => $north['lat'],
+ 'east' => $east['lon'],
+ 'south' => $south['lat'],
+ 'west' => $west['lon'],
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/SemanticMW/ValueDescriptions/CoordinateDescription.php b/www/wiki/extensions/Maps/src/SemanticMW/ValueDescriptions/CoordinateDescription.php
new file mode 100644
index 00000000..a63ea9a0
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/SemanticMW/ValueDescriptions/CoordinateDescription.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace Maps\SemanticMW\ValueDescriptions;
+
+use SMW\DataValueFactory;
+use SMW\Query\Language\ValueDescription;
+use SMWDIGeoCoord;
+use Wikimedia\Rdbms\IDatabase;
+
+/**
+ * Description of one data value of type Geographical Coordinates.
+ *
+ * @author Jeroen De Dauw
+ */
+class CoordinateDescription extends ValueDescription {
+
+ public function getQueryString( $asValue = false ) {
+ $queryString = DataValueFactory::getInstance()->newDataValueByItem(
+ $this->getDataItem(),
+ $this->getProperty()
+ )->getWikiValue();
+
+ return $asValue ? $queryString : "[[$queryString]]";
+ }
+
+ /**
+ * @see SomePropertyInterpreter::mapValueDescription
+ *
+ * FIXME: store specific code should be in the store component
+ *
+ * @param string $tableName
+ * @param string[] $fieldNames
+ * @param IDatabase $db
+ *
+ * @return string|false
+ */
+ public function getSQLCondition( $tableName, array $fieldNames, IDatabase $db ) {
+ $dataItem = $this->getDataItem();
+
+ // Only execute the query when the description's type is geographical coordinates,
+ // the description is valid, and the near comparator is used.
+ if ( $dataItem instanceof SMWDIGeoCoord ) {
+ switch ( $this->getComparator() ) {
+ case SMW_CMP_EQ:
+ $comparator = '=';
+ break;
+ case SMW_CMP_LEQ:
+ $comparator = '<=';
+ break;
+ case SMW_CMP_GEQ:
+ $comparator = '>=';
+ break;
+ case SMW_CMP_NEQ:
+ $comparator = '!=';
+ break;
+ default:
+ return false;
+ }
+
+ $lat = $db->addQuotes( $dataItem->getLatitude() );
+ $lon = $db->addQuotes( $dataItem->getLongitude() );
+
+ $conditions = [];
+
+ $conditions[] = "{$tableName}.$fieldNames[1] $comparator $lat";
+ $conditions[] = "{$tableName}.$fieldNames[2] $comparator $lon";
+
+ return implode( ' AND ', $conditions );
+ }
+
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/src/SemanticMaps.php b/www/wiki/extensions/Maps/src/SemanticMaps.php
new file mode 100644
index 00000000..8d48dc56
--- /dev/null
+++ b/www/wiki/extensions/Maps/src/SemanticMaps.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace Maps;
+
+use Maps\SemanticMW\ResultPrinters\KmlPrinter;
+use Maps\SemanticMW\ResultPrinters\MapPrinter;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class SemanticMaps {
+
+ private $mwGlobals;
+
+ private function __construct( array &$mwGlobals ) {
+ $this->mwGlobals =& $mwGlobals;
+ }
+
+ public static function newFromMediaWikiGlobals( array &$mwGlobals ) {
+ return new self( $mwGlobals );
+ }
+
+ public function initExtension() {
+ // Hook for initializing the Geographical Data types.
+ $this->mwGlobals['wgHooks']['SMW::DataType::initTypes'][] = 'Maps\MediaWiki\SemanticMapsHooks::initGeoDataTypes';
+
+ // Hook for defining the default query printer for queries that ask for geographical coordinates.
+ $this->mwGlobals['wgHooks']['SMWResultFormat'][] = 'Maps\MediaWiki\SemanticMapsHooks::addGeoCoordsDefaultFormat';
+
+ // Hook for adding a Semantic Maps links to the Admin Links extension.
+ $this->mwGlobals['wgHooks']['AdminLinks'][] = 'Maps\MediaWiki\SemanticMapsHooks::addToAdminLinks';
+
+ $this->registerGoogleMaps();
+ $this->registerLeaflet();
+
+ $this->mwGlobals['smwgResultFormats']['kml'] = KmlPrinter::class;
+
+ $this->mwGlobals['smwgResultAliases'][$this->mwGlobals['egMapsDefaultService']][] = 'map';
+ MapPrinter::registerDefaultService( $this->mwGlobals['egMapsDefaultService'] );
+
+ // Internationalization
+ $this->mwGlobals['wgMessagesDirs']['SemanticMaps'] = __DIR__ . '/i18n';
+ }
+
+ private function registerGoogleMaps() {
+ // TODO: inject
+ $services = MapsFactory::globalInstance()->getMappingServices();
+
+ if ( $services->nameIsKnown( 'googlemaps3' ) ) {
+ $googleMaps = $services->getService( 'googlemaps3' );
+
+ MapPrinter::registerService( $googleMaps );
+
+ $this->mwGlobals['smwgResultFormats'][$googleMaps->getName()] = MapPrinter::class;
+ $this->mwGlobals['smwgResultAliases'][$googleMaps->getName()] = $googleMaps->getAliases();
+ }
+ }
+
+ private function registerLeaflet() {
+ // TODO: inject
+ $services = MapsFactory::globalInstance()->getMappingServices();
+
+ if ( $services->nameIsKnown( 'leaflet' ) ) {
+ $leaflet = $services->getService( 'leaflet' );
+
+ MapPrinter::registerService( $leaflet );
+
+ $this->mwGlobals['smwgResultFormats'][$leaflet->getName()] = MapPrinter::class;
+ $this->mwGlobals['smwgResultAliases'][$leaflet->getName()] = $leaflet->getAliases();
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Integration/DataAccess/MediaWikiFileUrlFinderTest.php b/www/wiki/extensions/Maps/tests/Integration/DataAccess/MediaWikiFileUrlFinderTest.php
new file mode 100644
index 00000000..648c8c94
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/DataAccess/MediaWikiFileUrlFinderTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Maps\Tests\Integration\DataAccess;
+
+use Maps\DataAccess\MediaWikiFileUrlFinder;
+use Maps\FileUrlFinder;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers \Maps\DataAccess\MediaWikiFileUrlFinder
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class MediaWikiFileUrlFinderTest extends TestCase {
+
+ /**
+ * @var FileUrlFinder
+ */
+ private $urlFinder;
+
+ public function setUp() {
+ $this->urlFinder = new MediaWikiFileUrlFinder();
+ }
+
+ public function testGivenUrl_urlIsReturnedAsProvided() {
+ $this->assertSame(
+ 'http://example.com/such',
+ $this->urlFinder->getUrlForFileName( 'http://example.com/such' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Integration/InitializationTest.php b/www/wiki/extensions/Maps/tests/Integration/InitializationTest.php
new file mode 100644
index 00000000..5d94e291
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/InitializationTest.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Maps\Tests\Integration;
+
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class InitializationTest extends TestCase {
+
+ public function testVersionConstantIsDefined() {
+ $this->assertInternalType( 'string', Maps_VERSION );
+ $this->assertInternalType( 'string', SM_VERSION );
+ $this->assertSame( Maps_VERSION, SM_VERSION );
+ $this->assertNotEmpty( Maps_VERSION );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Integration/MapsDistanceParserTest.php b/www/wiki/extensions/Maps/tests/Integration/MapsDistanceParserTest.php
new file mode 100644
index 00000000..e5b04787
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/MapsDistanceParserTest.php
@@ -0,0 +1,177 @@
+<?php
+
+namespace Maps\Tests\Integration;
+
+use Maps\Presentation\MapsDistanceParser;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers MapsDistanceParser
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class MapsDistanceParserTest extends TestCase {
+
+ public static $distances = [
+ '1' => 1,
+ '1m' => 1,
+ '1 m' => 1,
+ ' 1 m ' => 1,
+ '1.1' => 1.1,
+ '1,1' => 1.1,
+ '1 km' => 1000,
+ '42 km' => 42000,
+ '4.2 km' => 4200,
+ '4,20km' => 4200,
+ '1 mile' => 1609.344,
+ '10 nauticalmiles' => 18520,
+ '1.0nautical mile' => 1852,
+ ];
+ public static $formatTests = [
+ 'm' => [
+ '1 m' => 1,
+ '1000 m' => 1000.00,
+ '42.42 m' => 42.42,
+ '42.4242 m' => 42.4242,
+ ],
+ 'km' => [
+ //'0.001 km' => 1,
+ '1 km' => 1000,
+ '4.24 km' => 4242,
+ ],
+ 'kilometers' => [
+ '0.001 kilometers' => 1,
+ '1 kilometers' => 1000,
+ '4.24 kilometers' => 4242,
+ ],
+ ];
+ /**
+ * Invalid distances.
+ *
+ * @var array
+ */
+ public static $fakeDistances = [
+ 'IN YOUR CODE, BEING TOTALLY RIDICULOUS',
+ '0x20 km',
+ 'km 42',
+ '42 42 km',
+ '42 km km',
+ '42 foo',
+ '3.4.2 km'
+ ];
+
+ public function setUp() {
+ if ( !defined( 'MEDIAWIKI' ) ) {
+ $this->markTestSkipped( 'MediaWiki is not available' );
+ }
+ }
+
+ /**
+ * Tests Maps\Presentation\MapsDistanceParser::parseDistance()
+ */
+ public function testParseDistance() {
+ foreach ( self::$distances as $rawValue => $parsedValue ) {
+ $this->assertEquals(
+ $parsedValue,
+ MapsDistanceParser::parseDistance( $rawValue ),
+ "'$rawValue' was not parsed to '$parsedValue':"
+ );
+ }
+
+ foreach ( self::$fakeDistances as $fakeDistance ) {
+ $this->assertFalse(
+ MapsDistanceParser::parseDistance( $fakeDistance ),
+ "'$fakeDistance' should not be recognized:"
+ );
+ }
+ }
+
+ /**
+ * Tests Maps\Presentation\MapsDistanceParser::formatDistance()
+ */
+ public function testFormatDistance() {
+ foreach ( self::$formatTests['km'] as $rawValue => $parsedValue ) {
+ $this->assertEquals(
+ $rawValue,
+ MapsDistanceParser::formatDistance( $parsedValue, 'km' ),
+ "'$parsedValue' was not formatted to '$rawValue':"
+ );
+ }
+ }
+
+ /**
+ * Tests Maps\Presentation\MapsDistanceParser::parseAndFormat()
+ */
+ public function testParseAndFormat() {
+ $conversions = [
+ '42 km' => '42000 m'
+ ];
+
+ foreach ( array_merge( $conversions, array_reverse( $conversions ) ) as $source => $target ) {
+ global $wgContLang;
+ $unit = explode( ' ', $target, 2 );
+ $unit = $unit[1];
+ $this->assertEquals(
+ $wgContLang->formatNum( $target ),
+ MapsDistanceParser::parseAndFormat( $source, $unit ),
+ "'$source' was not parsed and formatted to '$target':"
+ );
+ }
+ }
+
+ /**
+ * Tests Maps\Presentation\MapsDistanceParser::isDistance()
+ */
+ public function testIsDistance() {
+ foreach ( self::$fakeDistances as $fakeDistance ) {
+ $this->assertFalse(
+ MapsDistanceParser::isDistance( $fakeDistance ),
+ "'$fakeDistance' should not be recognized:"
+ );
+ }
+
+ foreach ( self::$distances as $distance ) {
+ $this->assertTrue( MapsDistanceParser::isDistance( $distance ), "'$distance' was not be recognized:" );
+ }
+ }
+
+ /**
+ * Tests Maps\Presentation\MapsDistanceParser::getUnitRatio()
+ */
+ public function testGetUnitRatio() {
+ foreach ( $GLOBALS['egMapsDistanceUnits'] as $unit => $ratio ) {
+ $r = MapsDistanceParser::getUnitRatio( $unit );
+ $this->assertEquals( $ratio, $r, "The ratio for '$unit' should be '$ratio' but was '$r'" );
+ }
+ }
+
+ /**
+ * Tests Maps\Presentation\MapsDistanceParser::getValidUnit()
+ */
+ public function testGetValidUnit() {
+ foreach ( $GLOBALS['egMapsDistanceUnits'] as $unit => $ratio ) {
+ $u = MapsDistanceParser::getValidUnit( $unit );
+ $this->assertEquals( $unit, $u, "The valid unit for '$unit' should be '$unit' but was '$u'" );
+ }
+
+ global $egMapsDistanceUnit;
+
+ foreach ( [ '0', 'swfwdffdhy', 'dxwgdrfh' ] as $unit ) {
+ $u = MapsDistanceParser::getValidUnit( $unit );
+ $this->assertEquals(
+ $egMapsDistanceUnit,
+ $u,
+ "The valid unit for '$unit' should be '$egMapsDistanceUnit' but was '$u'"
+ );
+ }
+ }
+
+ /**
+ * Tests Maps\Presentation\MapsDistanceParser::getUnits()
+ */
+ public function testGetUnits() {
+ $this->assertEquals( array_keys( $GLOBALS['egMapsDistanceUnits'] ), MapsDistanceParser::getUnits() );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Integration/MapsMapperTest.php b/www/wiki/extensions/Maps/tests/Integration/MapsMapperTest.php
new file mode 100644
index 00000000..4760db5f
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/MapsMapperTest.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Maps\Tests\Integration;
+
+use Maps\MapsFunctions;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers MapsFunctions
+ *
+ * @licence GNU GPL v2+
+ * @author Peter Grassberger < petertheone@gmail.com >
+ */
+class MapsMapperTest extends TestCase {
+
+ public function setUp() {
+ if ( !defined( 'MEDIAWIKI' ) ) {
+ $this->markTestSkipped( 'MediaWiki is not available' );
+ }
+ }
+
+ public function imageUrlProvider() {
+ return [
+ [ 'markerImage.png', 'markerImage.png' ],
+ [ '/w/images/c/ce/Green_marker.png', '/w/images/c/ce/Green_marker.png' ],
+ [
+ '//semantic-mediawiki.org/w/images/c/ce/Green_marker.png',
+ '//semantic-mediawiki.org/w/images/c/ce/Green_marker.png'
+ ],
+ [ 'Cat2.jpg', 'Cat2.jpg' ],
+ ];
+ }
+
+ /**
+ * Tests MapsMapperTest::getFileUrl()
+ *
+ * @dataProvider imageUrlProvider
+ */
+ public function testGetFileUrl( $file, $expected ) {
+ $this->assertSame( $expected, MapsFunctions::getFileUrl( $file ) );
+ }
+}
diff --git a/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/CoordinatesTest.php b/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/CoordinatesTest.php
new file mode 100644
index 00000000..9ee324fe
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/CoordinatesTest.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace Maps\Tests\Integration\MediaWiki\ParserHooks;
+
+use DataValues\Geo\Values\LatLongValue;
+use Maps\MediaWiki\ParserHooks\CoordinatesFunction;
+use ParamProcessor\ParamDefinition;
+
+/**
+ * @covers CoordinatesFunction
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class CoordinatesTest extends ParserHookTest {
+
+ /**
+ * @see ParserHookTest::parametersProvider
+ */
+ public function parametersProvider() {
+ $paramLists = [];
+
+ $paramLists[] = [
+ [
+ 'location' => '4,2',
+ 'format' => 'dms',
+ 'directional' => 'no',
+ ],
+ '4° 0\' 0.00", 2° 0\' 0.00"'
+ ];
+
+ $paramLists[] = [
+ [
+ 'location' => '55 S, 37.6176330 W',
+ 'format' => 'dms',
+ 'directional' => 'no',
+ ],
+ '-55° 0\' 0.00", -37° 37\' 3.48"'
+ ];
+
+ $paramLists[] = [
+ [
+ 'location' => '4,2',
+ 'format' => 'float',
+ 'directional' => 'no',
+ ],
+ '4, 2'
+ ];
+
+ $paramLists[] = [
+ [
+ 'location' => '-4,-2',
+ 'format' => 'float',
+ 'directional' => 'yes',
+ ],
+ '4 S, 2 W'
+ ];
+
+ $paramLists[] = [
+ [
+ 'location' => '55 S, 37.6176330 W',
+ 'directional' => 'yes',
+ ],
+ '55° 0\' 0.00" S, 37° 37\' 3.48" W'
+ ];
+
+ return $paramLists;
+ }
+
+ /**
+ * @see ParserHookTest::processingProvider
+ */
+ public function processingProvider() {
+ $definitions = ParamDefinition::getCleanDefinitions( $this->getInstance()->getParamDefinitions() );
+ $argLists = [];
+
+ $values = [
+ 'location' => '4,2',
+ ];
+
+ $expected = [
+ 'location' => new LatLongValue( 4, 2 ),
+ ];
+
+ $argLists[] = [ $values, $expected ];
+
+ $values = [
+ 'location' => '4,2',
+ 'directional' => $definitions['directional']->getDefault() ? 'no' : 'yes',
+ 'format' => 'dd',
+ ];
+
+ $expected = [
+ 'location' => new LatLongValue( 4, 2 ),
+ 'directional' => !$definitions['directional']->getDefault(),
+ 'format' => 'dd',
+ ];
+
+ $argLists[] = [ $values, $expected ];
+
+ $values = [
+ 'location' => '4,2',
+ 'directional' => $definitions['directional']->getDefault() ? 'NO' : 'YES',
+ 'format' => ' DD ',
+ ];
+
+ $expected = [
+ 'location' => new LatLongValue( 4, 2 ),
+ 'directional' => !$definitions['directional']->getDefault(),
+ 'format' => 'dd',
+ ];
+
+ $argLists[] = [ $values, $expected ];
+
+ return $argLists;
+ }
+
+ /**
+ * @see ParserHookTest::getInstance
+ */
+ protected function getInstance() {
+ return new \Maps\MediaWiki\ParserHooks\CoordinatesFunction();
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/DistanceTest.php b/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/DistanceTest.php
new file mode 100644
index 00000000..3e3889c8
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/DistanceTest.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace Maps\Tests\Integration\MediaWiki\ParserHooks;
+
+use Maps\MediaWiki\ParserHooks\DistanceFunction;
+
+/**
+ * @covers DistanceFunction
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class DistanceTest extends ParserHookTest {
+
+ private $distances = [
+ '42' => 42,
+ '42m' => 42,
+ '42 m' => 42,
+ '42 km' => 42000,
+ '4.2 km' => 4200,
+ '4.2 m' => 4.2,
+ ];
+
+ /**
+ * @see ParserHookTest::parametersProvider
+ */
+ public function parametersProvider() {
+ $paramLists = [];
+
+ foreach ( array_keys( $this->distances ) as $distance ) {
+ $paramLists[] = [ 'distance' => (string)$distance ];
+ }
+
+ return $this->arrayWrap( $paramLists );
+ }
+
+ /**
+ * @see ParserHookTest::processingProvider
+ */
+ public function processingProvider() {
+ $argLists = [];
+
+ foreach ( $this->distances as $input => $output ) {
+ $values = [
+ 'distance' => (string)$input,
+ ];
+
+ $expected = [
+ 'distance' => $output,
+ ];
+
+ $argLists[] = [ $values, $expected ];
+ }
+
+ $values = [
+ 'distance' => '42m',
+ 'unit' => 'km',
+ 'decimals' => '1',
+ ];
+
+ $expected = [
+ 'distance' => 42,
+ 'unit' => 'km',
+ 'decimals' => 1,
+ ];
+
+ $argLists[] = [ $values, $expected ];
+
+ $values = [
+ 'distance' => '42m',
+ 'unit' => '~=[,,_,,]:3',
+ 'decimals' => 'foobar',
+ ];
+
+ $expected = [
+ 'distance' => 42,
+ ];
+
+ $argLists[] = [ $values, $expected ];
+
+ return $argLists;
+ }
+
+ /**
+ * @see ParserHookTest::getInstance
+ */
+ protected function getInstance() {
+ return new \Maps\MediaWiki\ParserHooks\DistanceFunction();
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/FinddestinationTest.php b/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/FinddestinationTest.php
new file mode 100644
index 00000000..811d16e2
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/FinddestinationTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Maps\Tests\Integration\MediaWiki\ParserHooks;
+
+use DataValues\Geo\Parsers\LatLongParser;
+use Maps\Elements\Location;
+use Maps\MediaWiki\ParserHooks\FindDestinationFunction;
+
+/**
+ * @covers FindDestinationFunction
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class FinddestinationTest extends ParserHookTest {
+
+ /**
+ * @var string[]
+ */
+ private $locations = [
+ '4,2',
+ '4.2,-42',
+ ];
+
+ private $bearings = [
+ 1,
+ 42,
+ -42,
+ 0,
+ 4.2,
+ ];
+
+ private $distances = [
+ '42' => 42,
+ '0' => 0,
+ '42 m' => 42,
+ '42 km' => 42000,
+ '4.2 km' => 4200,
+ ];
+
+ /**
+ * @see ParserHookTest::parametersProvider
+ */
+ public function parametersProvider() {
+ $paramLists = [];
+
+ $paramLists[] = [
+ 'location' => '4,2',
+ 'bearing' => '1',
+ 'distance' => '42 km'
+ ];
+
+ return $this->arrayWrap( $paramLists );
+ }
+
+ /**
+ * @see ParserHookTest::processingProvider
+ */
+ public function processingProvider() {
+ $argLists = [];
+
+ $coordinateParser = new LatLongParser();
+
+ foreach ( $this->distances as $distance => $expectedDistance ) {
+ foreach ( $this->bearings as $bearing ) {
+ foreach ( $this->locations as $locationString ) {
+ $values = [
+ 'distance' => (string)$distance,
+ 'bearing' => (string)$bearing,
+ 'location' => (string)$locationString,
+ ];
+
+ $expected = [
+ 'distance' => $expectedDistance,
+ 'bearing' => (float)$bearing,
+ 'location' => new Location( $coordinateParser->parse( $locationString )->getValue() ),
+ ];
+
+ $argLists[] = [ $values, $expected ];
+ }
+ }
+ }
+
+ return $argLists;
+ }
+
+ /**
+ * @see ParserHookTest::getInstance
+ */
+ protected function getInstance() {
+ return new \Maps\MediaWiki\ParserHooks\FindDestinationFunction();
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/GeocodeTest.php b/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/GeocodeTest.php
new file mode 100644
index 00000000..0469650c
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/GeocodeTest.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Maps\Tests\Integration\MediaWiki\ParserHooks;
+
+use DataValues\Geo\Values\LatLongValue;
+use Jeroen\SimpleGeocoder\Geocoders\InMemoryGeocoder;
+use Maps\MediaWiki\ParserHooks\GeocodeFunction;
+
+/**
+ * @covers GeocodeFunction
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class GeocodeTest extends ParserHookTest {
+
+ /**
+ * @see ParserHookTest::parametersProvider
+ */
+ public function parametersProvider() {
+ $paramLists = [];
+
+ $paramLists[] = [ 'location' => 'New York', '4, 2' ];
+ $paramLists[] = [ 'location' => 'Brussels', '2, 3' ];
+ $paramLists[] = [ 'location' => 'I am a tomato', 'Geocoding failed' ];
+
+ return $this->arrayWrap( $paramLists );
+ }
+
+ /**
+ * @see ParserHookTest::processingProvider
+ */
+ public function processingProvider() {
+ $argLists = [];
+
+ $argLists[] = [
+ [
+ 'location' => '4,2',
+ 'directional' => 'yes',
+ ],
+ [
+ 'location' => '4,2',
+ 'directional' => true,
+ ]
+ ];
+
+ return $argLists;
+ }
+
+ /**
+ * @see ParserHookTest::getInstance
+ */
+ protected function getInstance() {
+ return new \Maps\MediaWiki\ParserHooks\GeocodeFunction(
+ new InMemoryGeocoder(
+ [
+ 'New York' => new LatLongValue( 4, 2 ),
+ 'Brussels' => new LatLongValue( 2, 3 ),
+ ]
+ )
+ );
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/GeodistanceTest.php b/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/GeodistanceTest.php
new file mode 100644
index 00000000..59489371
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/GeodistanceTest.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Maps\Tests\Integration\MediaWiki\ParserHooks;
+
+use DataValues\Geo\Values\LatLongValue;
+use Maps\Elements\Location;
+use Maps\MediaWiki\ParserHooks\GeoDistanceFunction;
+
+/**
+ * @covers GeoDistanceFunction
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class GeodistanceTest extends ParserHookTest {
+
+ /**
+ * @see ParserHookTest::parametersProvider
+ */
+ public function parametersProvider() {
+ $paramLists = [];
+
+ $paramLists[] = [
+ 'location1' => '4,2',
+ 'location2' => '42,0',
+ ];
+
+ $paramLists[] = [
+ '4,2',
+ '42,0',
+ ];
+
+ return $this->arrayWrap( $paramLists );
+ }
+
+ /**
+ * @see ParserHookTest::processingProvider
+ * @since 3.0
+ * @return array
+ */
+ public function processingProvider() {
+ $argLists = [];
+
+ $values = [
+ 'location1' => '4,2',
+ 'location2' => '42,0',
+ ];
+
+ $expected = [
+ 'location1' => new Location( new LatLongValue( 4, 2 ) ),
+ 'location2' => new Location( new LatLongValue( 42, 0 ) ),
+ ];
+
+ $argLists[] = [ $values, $expected ];
+
+ $values = [
+ 'location1' => '4,2',
+ 'location2' => '42,0',
+ 'unit' => '~=[,,_,,]:3',
+ 'decimals' => '1',
+ ];
+
+ $expected = [
+ 'location1' => new Location( new LatLongValue( 4, 2 ) ),
+ 'location2' => new Location( new LatLongValue( 42, 0 ) ),
+ 'decimals' => 1,
+ ];
+
+ $argLists[] = [ $values, $expected ];
+
+ return $argLists;
+ }
+
+ /**
+ * @see ParserHookTest::getInstance
+ */
+ protected function getInstance() {
+ return new \Maps\MediaWiki\ParserHooks\GeoDistanceFunction();
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/MapsDocTest.php b/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/MapsDocTest.php
new file mode 100644
index 00000000..96a06ca7
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/MapsDocTest.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Maps\Tests\Integration\MediaWiki\ParserHooks;
+
+use Maps\MediaWiki\ParserHooks\MapsDocFunction;
+
+/**
+ * @covers MapsDocFunction
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class MapsDocTest extends ParserHookTest {
+
+ /**
+ * @see ParserHookTest::parametersProvider
+ */
+ public function parametersProvider() {
+ $paramLists = [];
+
+ $paramLists[] = [];
+
+ return $this->arrayWrap( $paramLists );
+ }
+
+ /**
+ * @see ParserHookTest::processingProvider
+ */
+ public function processingProvider() {
+ $argLists = [];
+
+ $values = [ 'service' => 'googlemaps3' ];
+
+ $expected = [ 'service' => 'googlemaps3' ];
+
+ $argLists[] = [ $values, $expected ];
+
+ $values = [ 'service' => 'GOOGLEmaps3' ];
+
+ $expected = [ 'service' => 'googlemaps3' ];
+
+ $argLists[] = [ $values, $expected ];
+
+ return $argLists;
+ }
+
+ /**
+ * @see ParserHookTest::getInstance
+ */
+ protected function getInstance() {
+ return new \Maps\MediaWiki\ParserHooks\MapsDocFunction();
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/ParserHookTest.php b/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/ParserHookTest.php
new file mode 100644
index 00000000..eeb19d61
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/MediaWiki/ParserHooks/ParserHookTest.php
@@ -0,0 +1,133 @@
+<?php
+
+namespace Maps\Tests\Integration\MediaWiki\ParserHooks;
+
+use ParamProcessor\ParamDefinition;
+use ParamProcessor\Processor;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+abstract class ParserHookTest extends TestCase {
+
+ public static function setUpBeforeClass() {
+ if ( !defined( 'MEDIAWIKI' ) ) {
+ self::markTestSkipped( 'MediaWiki is not available' );
+ }
+ }
+
+ /**
+ * @since 2.0
+ * @return array
+ */
+ public abstract function parametersProvider();
+
+ /**
+ * Triggers the render process with different sets of parameters to see if
+ * no errors or notices are thrown and the result indeed is a string.
+ *
+ * @dataProvider parametersProvider
+ * @since 2.0
+ *
+ * @param array $parameters
+ * @param string|null $expected
+ */
+ public function testRender( array $parameters, $expected = null ) {
+ $parserHook = $this->getInstance();
+
+ $parser = new \Parser();
+ $parser->mOptions = new \ParserOptions();
+ $parser->clearState();
+ $parser->setTitle( \Title::newMainPage() );
+
+ $renderResult = call_user_func_array(
+ [ $parserHook, 'renderFunction' ],
+ array_merge( [ &$parser ], $parameters )
+ );
+
+ if ( is_string( $renderResult ) ) {
+ $this->assertTrue( true );
+ } else {
+ $this->assertInternalType( 'array', $renderResult );
+ $this->assertInternalType( 'string', $renderResult[0] );
+ }
+
+ if ( $expected !== null ) {
+ $this->assertEquals( $expected, $renderResult[0] );
+ }
+ }
+
+ /**
+ * @since 2.0
+ * @return \ParserHook
+ */
+ protected abstract function getInstance();
+
+ public function processingProvider() {
+ return [];
+ }
+
+ /**
+ * @dataProvider processingProvider
+ * @since 3.0
+ */
+ public function testParamProcessing( array $parameters, array $expectedValues ) {
+ $definitions = $this->getInstance()->getParamDefinitions();
+
+ $processor = Processor::newDefault();
+ $processor->setParameters( $parameters, $definitions );
+
+ $result = $processor->processParameters();
+
+ if ( $result->hasFatal() ) {
+ $this->fail(
+ 'Fatal error occurred during the param processing: ' . $processor->hasFatalError()->getMessage()
+ );
+ }
+
+ $actual = $result->getParameters();
+
+ $expectedValues = array_merge( $this->getDefaultValues(), $expectedValues );
+
+ foreach ( $expectedValues as $name => $expected ) {
+ $this->assertArrayHasKey( $name, $actual );
+
+ $this->assertEquals(
+ $expected,
+ $actual[$name]->getValue(),
+ 'Expected ' . var_export( $expected, true )
+ . ' should match actual '
+ . var_export( $actual[$name]->getValue(), true )
+ );
+ }
+ }
+
+ /**
+ * Returns an array with the default values of the parameters.
+ */
+ private function getDefaultValues() {
+ $definitions = ParamDefinition::getCleanDefinitions( $this->getInstance()->getParamDefinitions() );
+
+ $defaults = [];
+
+ foreach ( $definitions as $definition ) {
+ if ( !$definition->isRequired() ) {
+ $defaults[$definition->getName()] = $definition->getDefault();
+ }
+ }
+
+ return $defaults;
+ }
+
+ protected function arrayWrap( array $elements ) {
+ return array_map(
+ function ( $element ) {
+ return [ $element ];
+ },
+ $elements
+ );
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/tests/Integration/Parser/CoordinatesTest.php b/www/wiki/extensions/Maps/tests/Integration/Parser/CoordinatesTest.php
new file mode 100644
index 00000000..4ec71630
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/Parser/CoordinatesTest.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Maps\Tests\Integration\Parser;
+
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class CoordinatesTest extends TestCase {
+
+ private function parse( string $textToParse ): string {
+ $parser = new \Parser();
+
+ return $parser->parse( $textToParse, \Title::newMainPage(), new \ParserOptions() )->getText();
+ }
+
+ public function testGivenInvalidCoordinates_errorIsShown() {
+ $this->assertContains(
+ '<span class="errorbox">',
+ $this->parse( '{{#coordinates:nope}}' )
+ );
+ }
+
+ public function testGivenNoCoordinates_errorIsShown() {
+ $this->assertContains(
+ '<span class="errorbox">',
+ $this->parse( '{{#coordinates:}}' )
+ );
+ }
+
+ public function testGivenValidCoordinates_theyAreFormatted() {
+ $this->assertContains(
+ '1° 0\' 0.00" N, 1° 0\' 0.00" E',
+ $this->parse( '{{#coordinates:1,1}}' )
+ );
+ }
+
+ public function testGivenFormat_coordinatesAreConvertedToIt() {
+ $this->assertContains(
+ '1.000000° N, 1.000000° E',
+ $this->parse( '{{#coordinates:1,1|format=dd}}' )
+ );
+ }
+
+ public function testGivenDirectionalParameter_itGetsUsed() {
+ $this->assertContains(
+ '1° 0\' 0.00", 1° 0\' 0.00"',
+ $this->parse( '{{#coordinates:1,1|directional=no}}' )
+ );
+ }
+
+ public function testCoordinatesInNonDms_theyGetParsed() {
+ $this->assertContains(
+ '1° 20\' 13.20" N, 4° 12\' 0.00" W',
+ $this->parse( '{{#coordinates:1.337°, -4.2°}}' )
+ );
+ }
+
+ public function testGivenInvalidFormat_defaultFormatGetsUsed() {
+ $this->assertContains(
+ '1° 0\' 0.00" N, 1° 0\' 0.00" E',
+ $this->parse( '{{#coordinates:1,1|format=such}}' )
+ );
+ }
+
+ public function testRoundingWhenFormattingAsFloat() {
+ $this->assertContains(
+ '52.136945 N, 0.466722 W',
+ $this->parse( '{{#coordinates:52.136945,-0.466722|format=float}}' )
+ );
+ }
+
+ public function testRoundingWhenFormattingAsDMS() {
+ $this->assertContains(
+ '52° 8\' 13.00" N, 0° 28\' 0.20" W',
+ $this->parse( '{{#coordinates:52.136945,-0.466722|format=dms}}' )
+ );
+ }
+
+ public function testRoundingWhenFormattingAsDD() {
+ $this->assertContains(
+ '52.136945° N, 0.466722° W',
+ $this->parse( '{{#coordinates:52.136945,-0.466722|format=dd}}' )
+ );
+ }
+
+ public function testRoundingWhenFormattingAsDM() {
+ $this->assertContains(
+ '52° 8.2167\' N, 0° 28.0033\' W',
+ $this->parse( '{{#coordinates:52.136945,-0.466722|format=dm}}' )
+ );
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/tests/Integration/Parser/DisplayMapTest.php b/www/wiki/extensions/Maps/tests/Integration/Parser/DisplayMapTest.php
new file mode 100644
index 00000000..9bd64bcf
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/Parser/DisplayMapTest.php
@@ -0,0 +1,220 @@
+<?php
+
+namespace Maps\Tests\Integration\Parser;
+
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class DisplayMapTest extends TestCase {
+
+ private $originalHeight;
+ private $originalWidth;
+
+ public function setUp() {
+ $this->originalHeight = $GLOBALS['egMapsMapHeight'];
+ $this->originalWidth = $GLOBALS['egMapsMapWidth'];
+ }
+
+ public function tearDown() {
+ $GLOBALS['egMapsMapHeight'] = $this->originalHeight;
+ $GLOBALS['egMapsMapWidth'] = $this->originalWidth;
+ }
+
+ public function testMapIdIsSet() {
+ $this->assertContains(
+ 'id="map_leaflet_',
+ $this->parse( '{{#display_map:1,1|service=leaflet}}' )
+ );
+ }
+
+ private function parse( string $textToParse ): string {
+ $parser = new \Parser();
+
+ return $parser->parse( $textToParse, \Title::newMainPage(), new \ParserOptions() )->getText();
+ }
+
+ public function testServiceSelectionWorks() {
+ $this->assertContains(
+ 'maps-googlemaps3',
+ $this->parse( '{{#display_map:1,1|service=google}}' )
+ );
+ }
+
+ public function testSingleCoordinatesAreIncluded() {
+ $this->assertContains(
+ '"lat":1,"lon":1',
+ $this->parse( '{{#display_map:1,1}}' )
+ );
+ }
+
+ public function testMultipleCoordinatesAreIncluded() {
+ $result = $this->parse( '{{#display_map:1,1; 4,2}}' );
+
+ $this->assertContains( '"lat":1,"lon":1', $result );
+ $this->assertContains( '"lat":4,"lon":2', $result );
+ }
+
+ public function testWhenValidZoomIsSpecified_itGetsUsed() {
+ $this->assertContains(
+ '"zoom":5',
+ $this->parse( '{{#display_map:1,1|service=google|zoom=5}}' )
+ );
+ }
+
+ public function testWhenZoomIsNotSpecifiedAndThereIsOnlyOneLocation_itIsDefaulted() {
+ $this->assertContains(
+ '"zoom":' . $GLOBALS['egMapsGMaps3Zoom'],
+ $this->parse( '{{#display_map:1,1|service=google}}' )
+ );
+ }
+
+ public function testWhenZoomIsNotSpecifiedAndThereAreMultipleLocations_itIsDefaulted() {
+ $this->assertContains(
+ '"zoom":false',
+ $this->parse( '{{#display_map:1,1;2,2|service=google}}' )
+ );
+ }
+
+ public function testWhenZoomIsInvalid_itIsDefaulted() {
+ $this->assertContains(
+ '"zoom":' . $GLOBALS['egMapsGMaps3Zoom'],
+ $this->parse( '{{#display_map:1,1|service=google|zoom=tomato}}' )
+ );
+ }
+
+ public function testTagIsRendered() {
+ $this->assertContains(
+ '"lat":1,"lon":1',
+ $this->parse( '<display_map>1,1</display_map>' )
+ );
+ }
+
+ public function testTagServiceParameterIsUsed() {
+ $this->assertContains(
+ 'maps-googlemaps3',
+ $this->parse( '<display_map service="google">1,1</display_map>' )
+ );
+ }
+
+ public function testWhenThereAreNoLocations_locationsArrayIsEmpty() {
+ $this->assertContains(
+ '"locations":[]',
+ $this->parse( '{{#display_map:}}' )
+ );
+ }
+
+ public function testLocationTitleGetsIncluded() {
+ $this->assertContains(
+ '"title":"title',
+ $this->parse( '{{#display_map:1,1~title}}' )
+ );
+ }
+
+ public function testLocationDescriptionGetsIncluded() {
+ $this->assertContains(
+ 'such description',
+ $this->parse( '{{#display_map:1,1~title~such description}}' )
+ );
+ }
+
+ public function testRectangleDisplay() {
+ $this->assertContains(
+ '"title":"title',
+ $this->parse( '{{#display_map:rectangles=1,1:2,2~title}}' )
+ );
+ }
+
+ public function testCircleDisplay() {
+ $this->assertContains(
+ '"title":"title',
+ $this->parse( '{{#display_map:circles=1,1:2~title}}' )
+ );
+ }
+
+ public function testRectangleFillOpacityIsUsed() {
+ $this->assertContains(
+ '"fillOpacity":"fill opacity"',
+ $this->parse( '{{#display_map:rectangles=1,1:2,2~title~text~color~opacity~thickness~fill color~fill opacity}}' )
+ );
+ }
+
+ public function testRectangleFillColorIsUsed() {
+ $this->assertContains(
+ '"fillColor":"fill color"',
+ $this->parse( '{{#display_map:rectangles=1,1:2,2~title~text~color~opacity~thickness~fill color~fill opacity}}' )
+ );
+ }
+
+ public function testServiceSelectionWorksWhenItIsPrecededByMultipleParameters() {
+ $this->assertContains(
+ 'maps-googlemaps3',
+ $this->parse(
+ "{{#display_map:rectangles=\n 1,1:2,2~title~text~color\n| scrollwheelzoom=off\n| service = google}}"
+ )
+ );
+ }
+
+ public function testDimensionDefaultsAsInteger() {
+ $GLOBALS['egMapsMapHeight'] = 420;
+ $GLOBALS['egMapsMapWidth'] = 230;
+
+ $this->assertContains(
+ 'height: 420px;',
+ $this->parse( '{{#display_map:1,1}}' )
+ );
+
+ $this->assertContains(
+ 'width: 230px;',
+ $this->parse( '{{#display_map:1,1}}' )
+ );
+ }
+
+ // TODO: need DI to test
+// public function testWhenLocationHasVisitedIconModifier_itIsUsed() {
+// $this->assertContains(
+// '"visitedicon":"VisitedIcon.png"',
+// $this->parse( '{{#display_map:1,1~title~text~icon~group~inline label~VisitedIcon.png}}' )
+// );
+// }
+//
+// public function testWhenLocationHasVisitedIconModifierWithNamespacePrefix_thePrefixGetsRemoved() {
+// $this->assertContains(MapsMapperTest
+// '"visitedicon":"VisitedIcon.png"',
+// $this->parse( '{{#display_map:1,1~title~text~icon~group~inline label~File:VisitedIcon.png}}' )
+// );
+// }
+//
+// public function testWhenVisitedIconParameterIsProvidedWithNamespacePrefix_thePrefixGetsRemoved() {
+// $this->assertContains(
+// '"visitedicon":"VisitedIcon.png"',
+// $this->parse( '{{#display_map:1,1|visitedicon=File:VisitedIcon.png}}' )
+// );
+// }
+//
+// public function testWhenLocationHasIconModifierWithNamespacePrefix_thePrefixGetsRemoved() {
+// $this->assertContains(
+// '"icon":"Icon.png"',
+// $this->parse( '{{#display_map:1,1~title~text~File:Icon.png}}' )
+// );
+// }
+
+ public function testWhenIconParameterIsProvidedButEmpty_itIsDefaulted() {
+ $this->assertContains(
+ '"icon":"","inlineLabel":"Ghent',
+ $this->parse(
+ "{{#display_map:Gent, Belgie~The city Ghent~Ghent is awesome~ ~ ~Ghent}}"
+ )
+ );
+ }
+
+ public function testWhenLocationHasNoTitleAndText_textFieldIsEmptyString() {
+ $this->assertContains(
+ '"text":""',
+ $this->parse( '{{#display_map:1,1}}' )
+ );
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/Maps/tests/Integration/Semantic/ValueDescriptions/AreaDescriptionTest.php b/www/wiki/extensions/Maps/tests/Integration/Semantic/ValueDescriptions/AreaDescriptionTest.php
new file mode 100644
index 00000000..9950b7d3
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/Semantic/ValueDescriptions/AreaDescriptionTest.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Maps\Tests\Semantic\ValueDescriptions;
+
+use CoordinateValue;
+use Maps\SemanticMW\ValueDescriptions\AreaDescription;
+use PHPUnit\Framework\TestCase;
+use SMWDIGeoCoord;
+
+/**
+ * @covers \Maps\SemanticMW\ValueDescriptions\AreaDescription
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class AreaDescriptionTest extends TestCase {
+
+ public function setUp() {
+ if ( !defined( 'SMW_VERSION' ) ) {
+ $this->markTestSkipped( 'SMW is not available' );
+ }
+ }
+
+ public function testGetBoundingBox() {
+ $area = new AreaDescription(
+ new SMWDIGeoCoord( 0, 5 ),
+ SMW_CMP_EQ,
+ '10 km'
+ );
+
+ $this->assertEquals(
+ [
+ 'north' => 0.089932160591873,
+ 'east' => 5.089932160591873,
+ 'south' => -0.089932160591873,
+ 'west' => 4.9100678394081
+ ],
+ $area->getBoundingBox()
+ );
+ }
+
+ public function testGetSQLCondition() {
+ $area = new AreaDescription(
+ new SMWDIGeoCoord( 0, 5 ),
+ SMW_CMP_EQ,
+ '10 km'
+ );
+
+ $this->assertSame(
+ 'geo_table.lat_field < \'0.089932160591873\' AND geo_table.lat_field > \'-0.089932160591873\' '
+ . 'AND geo_table.long_field < \'5.0899321605919\' AND geo_table.long_field > \'4.9100678394081\'',
+ $area->getSQLCondition( 'geo_table', [ 'id_field', 'lat_field', 'long_field' ], wfGetDB( DB_MASTER ) )
+ );
+ }
+
+ public function testWhenComparatorIsNotSupported_getSQLConditionReturnsFalse() {
+ $area = new AreaDescription(
+ new SMWDIGeoCoord( 0, 5 ),
+ SMW_CMP_LIKE,
+ '10 km'
+ );
+
+ $this->assertFalse(
+ $area->getSQLCondition( 'geo_table', [ 'id_field', 'lat_field', 'long_field' ], wfGetDB( DB_MASTER ) )
+ );
+ }
+
+ public function testGetQueryString() {
+ $area = new AreaDescription(
+ new SMWDIGeoCoord( 1, 5 ),
+ SMW_CMP_EQ,
+ '10 km'
+ );
+
+ $this->assertSame(
+ '[[1° 0\' 0.00" N, 5° 0\' 0.00" E (10 km)]]',
+ $area->getQueryString()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Integration/parsers/CircleParserTest.php b/www/wiki/extensions/Maps/tests/Integration/parsers/CircleParserTest.php
new file mode 100644
index 00000000..9009947b
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/parsers/CircleParserTest.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Maps\Tests\Integration\parsers;
+
+use DataValues\Geo\Values\LatLongValue;
+use Jeroen\SimpleGeocoder\Geocoders\Decorators\CoordinateFriendlyGeocoder;
+use Jeroen\SimpleGeocoder\Geocoders\NullGeocoder;
+use Maps\Elements\Circle;
+use Maps\Presentation\WikitextParsers\CircleParser;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers \Maps\Presentation\WikitextParsers\CircleParser
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class CircleParserTest extends TestCase {
+
+ public function testGivenCoordinateAndRadius_parserReturnsCircle() {
+ $parser = new CircleParser( new CoordinateFriendlyGeocoder( new NullGeocoder() ) );
+
+ $circle = $parser->parse( '57.421,23.90625:32684.605182' );
+
+ $this->assertInstanceOf( Circle::class, $circle );
+
+ $expectedLatLong = new LatLongValue( 57.421, 23.90625 );
+ $this->assertTrue( $expectedLatLong->equals( $circle->getCircleCentre() ) );
+
+ $this->assertSame( 32684.605182, $circle->getCircleRadius() );
+ }
+
+ public function testGivenTitleAndText_circleHasProvidedMetaData() {
+ $parser = new CircleParser( new CoordinateFriendlyGeocoder( new NullGeocoder() ) );
+
+ $circle = $parser->parse( '57.421,23.90625:32684.605182~title~text' );
+
+ $this->assertInstanceOf( Circle::class, $circle );
+
+ $this->assertSame( 'title', $circle->getTitle() );
+ $this->assertSame( 'text', $circle->getText() );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Integration/parsers/DistanceParserTest.php b/www/wiki/extensions/Maps/tests/Integration/parsers/DistanceParserTest.php
new file mode 100644
index 00000000..f402d966
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/parsers/DistanceParserTest.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace Maps\Tests\Integration\parsers;
+
+use Maps\Presentation\WikitextParsers\DistanceParser;
+use PHPUnit\Framework\TestCase;
+use PHPUnit4And6Compat;
+use ValueParsers\ParseException;
+
+/**
+ * @covers \Maps\Presentation\WikitextParsers\DistanceParser
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class DistanceParserTest extends TestCase {
+ use PHPUnit4And6Compat;
+
+ /**
+ * @dataProvider validInputProvider
+ */
+ public function testValidInputs( $input, $expected ) {
+ $this->assertSame(
+ $expected,
+ ( new DistanceParser() )->parse( $input )
+ );
+ }
+
+ public function validInputProvider() {
+ return [
+ [ '1', 1.0 ],
+ [ '1m', 1.0 ],
+ [ '42 km', 42000.0 ],
+ [ '4.2 km', 4200.0 ],
+ [ '4.2 m', 4.2 ],
+ [ '4.02 m', 4.02 ],
+ [ '4.02 km', 4020.0 ],
+ [ '0.001 km', 1.0 ],
+ ];
+ }
+
+ /**
+ * @dataProvider invalidInputProvider
+ */
+ public function testGivenInvalidInput_exceptionIsThrown( $input ) {
+ $parser = new DistanceParser();
+
+ $this->expectException( ParseException::class );
+ $parser->parse( $input );
+ }
+
+ public function invalidInputProvider() {
+ return [
+ [ '' ],
+ [ 'kittens' ],
+ [ '1 kittens' ],
+ [ '-1m' ],
+ [ 'foo m' ],
+ [ '1m foo' ],
+ [ 'foo 1m' ],
+ [ 'm1' ],
+ [ '4. m' ],
+ [ '4.2.1 m' ],
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Integration/parsers/JsonFileParserTest.php b/www/wiki/extensions/Maps/tests/Integration/parsers/JsonFileParserTest.php
new file mode 100644
index 00000000..a169beb1
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/parsers/JsonFileParserTest.php
@@ -0,0 +1,120 @@
+<?php
+
+namespace Maps\Tests\Integration\parsers;
+
+use FileFetcher\FileFetcher;
+use FileFetcher\NullFileFetcher;
+use FileFetcher\SimpleFileFetcher;
+use FileFetcher\StubFileFetcher;
+use FileFetcher\ThrowingFileFetcher;
+use Maps\DataAccess\JsonFileParser;
+use Maps\MediaWiki\Content\GeoJsonContent;
+use PHPUnit\Framework\TestCase;
+use PHPUnit4And6Compat;
+use Title;
+
+/**
+ * @covers \Maps\DataAccess\JsonFileParser
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class JsonFileParserTest extends TestCase {
+ use PHPUnit4And6Compat;
+
+ private const VALID_FILE_JSON = [
+ 'such' => 'string',
+ 42 => 13.37,
+ 'array' => [
+ '~[,,_,,]:3'
+ ]
+ ];
+
+ private const VALID_PAGE_JSON = [
+ 'foo' => 'bar',
+ 1 => 2.3,
+ 'array' => [
+ '~[,,_,,]:3'
+ ]
+ ];
+
+ private const EXISTING_GEO_JSON_PAGE = 'Such';
+ private const EXISTING_GEO_JSON_PAGE_WITH_PREFIX = 'GeoJson:Such';
+ private const NON_EXISTING_GEO_JSON_PAGE = 'GeoJson:Nope';
+
+ /**
+ * @var FileFetcher
+ */
+ private $fileFetcher;
+
+ public function setUp() {
+ $this->fileFetcher = new StubFileFetcher( json_encode( self::VALID_FILE_JSON ) );
+
+ $page = new \WikiPage( Title::newFromText( self::EXISTING_GEO_JSON_PAGE_WITH_PREFIX ) );
+ $page->doEditContent( new GeoJsonContent( json_encode( self::VALID_PAGE_JSON ) ), '' );
+ }
+
+ private function newJsonFileParser(): JsonFileParser {
+ return new JsonFileParser(
+ $this->fileFetcher,
+ null
+ );
+ }
+
+ public function testWhenFileRetrievalFails_emptyJsonIsReturned() {
+ $this->fileFetcher = new ThrowingFileFetcher();
+
+ $this->assertSame(
+ [],
+ $this->newJsonFileParser()->parse( 'http://such.a/file' )
+ );
+ }
+
+ public function testWhenFileHasValidJson_jsonIsReturned() {
+ $this->fileFetcher = new StubFileFetcher( json_encode( self::VALID_FILE_JSON ) );
+
+ $this->assertEquals(
+ self::VALID_FILE_JSON,
+ $this->newJsonFileParser()->parse( 'http://such.a/file' )
+ );
+ }
+
+ public function testWhenFileIsEmpty_emptyJsonIsReturned() {
+ $this->fileFetcher = new NullFileFetcher();
+
+ $this->assertSame(
+ [],
+ $this->newJsonFileParser()->parse( 'http://such.a/file' )
+ );
+ }
+
+ public function testWhenFileLocationIsNotUrl_emptyJsonIsReturned() {
+ $this->fileFetcher = new SimpleFileFetcher();
+
+ $jsonFilePath = __DIR__ . '/../../../composer.json';
+ $this->assertFileExists( $jsonFilePath );
+
+ $this->assertSame( [], $this->newJsonFileParser()->parse( $jsonFilePath ) );
+ }
+
+ public function testWhenPageExists_itsContentsIsReturned() {
+ $this->assertSame(
+ self::VALID_PAGE_JSON,
+ $this->newJsonFileParser()->parse( self::EXISTING_GEO_JSON_PAGE_WITH_PREFIX )
+ );
+ }
+
+ public function testWhenPageDoesNotExist_emptyJsonIsReturned() {
+ $this->assertSame(
+ [],
+ $this->newJsonFileParser()->parse( self::NON_EXISTING_GEO_JSON_PAGE )
+ );
+ }
+
+ public function testWhenExistingPageIsSpecifiedWithoutPrefix_itsContentsIsReturned() {
+ $this->assertSame(
+ self::VALID_PAGE_JSON,
+ $this->newJsonFileParser()->parse( self::EXISTING_GEO_JSON_PAGE )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Integration/parsers/LineParserTest.php b/www/wiki/extensions/Maps/tests/Integration/parsers/LineParserTest.php
new file mode 100644
index 00000000..39b149da
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/parsers/LineParserTest.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Maps\Tests\Integration\parsers;
+
+use DataValues\Geo\Values\LatLongValue;
+use Jeroen\SimpleGeocoder\Geocoders\InMemoryGeocoder;
+use Maps\Elements\Line;
+use Maps\Presentation\WikitextParsers\LineParser;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers \Maps\Presentation\WikitextParsers\LineParser
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class LineParserTest extends TestCase {
+
+ public function testGivenOneCoordinate_lineWithOneCoordinateIsReturned() {
+ $parser = $this->newParser();
+
+ $this->assertEquals(
+ new Line( [ new LatLongValue( 4, 2 ) ] ),
+ $parser->parse( '4,2' )
+ );
+ }
+
+ /**
+ * @return \Maps\Presentation\WikitextParsers\LineParser
+ */
+ private function newParser() {
+ $parser = new LineParser();
+
+ $parser->setGeocoder(
+ new InMemoryGeocoder(
+ [
+ '4,2' => new LatLongValue( 4, 2 ),
+ '2,3' => new LatLongValue( 2, 3 ),
+ ]
+ )
+ );
+
+ return $parser;
+ }
+
+ public function testGivenTwoCoordinates_lineWithBothCoordinateIsReturned() {
+ $parser = $this->newParser();
+
+ $this->assertEquals(
+ new Line(
+ [
+ new LatLongValue( 4, 2 ),
+ new LatLongValue( 2, 3 )
+ ]
+ ),
+ $parser->parse( '4,2:2,3' )
+ );
+ }
+
+ public function testTitleAndTextGetSetWhenPresent() {
+ $parser = $this->newParser();
+
+ $expectedLine = new Line(
+ [
+ new LatLongValue( 4, 2 ),
+ new LatLongValue( 2, 3 )
+ ]
+ );
+ $expectedLine->setTitle( 'title' );
+ $expectedLine->setText( 'text' );
+
+ $this->assertEquals(
+ $expectedLine,
+ $parser->parse( '4,2:2,3~title~text' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Integration/parsers/LocationParserTest.php b/www/wiki/extensions/Maps/tests/Integration/parsers/LocationParserTest.php
new file mode 100644
index 00000000..60f93284
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/parsers/LocationParserTest.php
@@ -0,0 +1,119 @@
+<?php
+
+namespace Maps\Tests\Integration\parsers;
+
+use DataValues\Geo\Values\LatLongValue;
+use Jeroen\SimpleGeocoder\Geocoders\StubGeocoder;
+use Maps\DataAccess\MediaWikiFileUrlFinder;
+use Maps\Elements\Location;
+use Maps\Presentation\WikitextParsers\LocationParser;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers \Maps\Presentation\WikitextParsers\LocationParser
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class LocationParserTest extends TestCase {
+
+ private $geocoder;
+ private $fileUrlFinder;
+ private $useAddressAsTitle;
+
+ public function setUp() {
+ $this->geocoder = new StubGeocoder( new LatLongValue( 1, 2 ) );
+ $this->fileUrlFinder = new MediaWikiFileUrlFinder();
+ $this->useAddressAsTitle = false;
+ }
+
+ private function newLocationParser() {
+ return LocationParser::newInstance( $this->geocoder, $this->fileUrlFinder, $this->useAddressAsTitle );
+ }
+
+ /**
+ * @dataProvider titleProvider
+ */
+ public function testGivenTitleThatIsNotLink_titleIsSetAndLinkIsNot( $title ) {
+ $location = $this->newLocationParser()->parse( '4,2~' . $title );
+
+ $this->assertTitleAndLinkAre( $location, $title, '' );
+ }
+
+ protected function assertTitleAndLinkAre( Location $location, $title, $link ) {
+ $this->assertHasJsonKeyWithValue( $location, 'title', $title );
+ $this->assertHasJsonKeyWithValue( $location, 'link', $link );
+ }
+
+ protected function assertHasJsonKeyWithValue( Location $polygon, $key, $value ) {
+ $json = $polygon->getJSONObject();
+
+ $this->assertArrayHasKey( $key, $json );
+ $this->assertEquals( $value, $json[$key] );
+ }
+
+ public function titleProvider() {
+ return [
+ [ '' ],
+ [ 'Title' ],
+ [ 'Some title' ],
+ [ 'link' ],
+ [ 'links:foo' ],
+ ];
+ }
+
+ /**
+ * @dataProvider linkProvider
+ */
+ public function testGivenTitleThatIsLink_linkIsSetAndTitleIsNot( $link ) {
+ $location = $this->newLocationParser()->parse( '4,2~link:' . $link );
+
+ $this->assertTitleAndLinkAre( $location, '', $link );
+ }
+
+ public function linkProvider() {
+ return [
+ [ 'https://www.semantic-mediawiki.org' ],
+ [ 'irc://freenode.net' ],
+ ];
+ }
+
+// /**
+// * @dataProvider titleLinkProvider
+// */
+// public function testGivenPageTitleAsLink_pageTitleIsTurnedIntoUrl( $link ) {
+// $parser = new LocationParser();
+// $location = $parser->parse( '4,2~link:' . $link );
+//
+// $linkUrl = Title::newFromText( $link )->getFullURL();
+// $this->assertTitleAndLinkAre( $location, '', $linkUrl );
+// }
+//
+// public function titleLinkProvider() {
+// return array(
+// array( 'Foo' ),
+// array( 'Some_Page' ),
+// );
+// }
+
+ public function testGivenAddressAndNoTitle_addressIsSetAsTitle() {
+ $this->useAddressAsTitle = true;
+ $location = $this->newLocationParser()->parse( 'Tempelhofer Ufer 42' );
+
+ $this->assertSame( 'Tempelhofer Ufer 42', $location->getTitle() );
+ }
+
+ public function testGivenAddressAndTitle_addressIsNotUsedAsTitle() {
+ $this->useAddressAsTitle = true;
+ $location = $this->newLocationParser()->parse( 'Tempelhofer Ufer 42~Great title of doom' );
+
+ $this->assertSame( 'Great title of doom', $location->getTitle() );
+ }
+
+ public function testGivenCoordinatesAndNoTitle_noTitleIsSet() {
+ $this->useAddressAsTitle = true;
+ $location = $this->newLocationParser()->parse( '4,2' );
+
+ $this->assertSame( '', $location->getTitle() );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Integration/parsers/RectlangleParserTest.php b/www/wiki/extensions/Maps/tests/Integration/parsers/RectlangleParserTest.php
new file mode 100644
index 00000000..027bdb07
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/parsers/RectlangleParserTest.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Maps\Tests\Integration\parsers;
+
+use DataValues\Geo\Values\LatLongValue;
+use Jeroen\SimpleGeocoder\Geocoders\Decorators\CoordinateFriendlyGeocoder;
+use Jeroen\SimpleGeocoder\Geocoders\NullGeocoder;
+use Maps\Elements\Rectangle;
+use Maps\Presentation\WikitextParsers\RectangleParser;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers \Maps\Presentation\WikitextParsers\RectangleParser
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class RectangleParserTest extends TestCase {
+
+ public function testGivenBoundingBox_parserReturnsRectangle() {
+ $parser = new RectangleParser( new CoordinateFriendlyGeocoder( new NullGeocoder() ) );
+
+ $rectangle = $parser->parse( '51.8357775,33.83789:46,23.37890625' );
+
+ $this->assertInstanceOf( Rectangle::class, $rectangle );
+
+ $expectedNorthEast = new LatLongValue( 51.8357775, 33.83789 );
+ $this->assertTrue( $expectedNorthEast->equals( $rectangle->getRectangleNorthEast() ) );
+
+ $expectedSouthWest = new LatLongValue( 46, 23.37890625 );
+ $this->assertTrue( $expectedSouthWest->equals( $rectangle->getRectangleSouthWest() ) );
+ }
+
+ public function testGivenTitleAndText_rectangleHasProvidedMetaData() {
+ $parser = new RectangleParser( new CoordinateFriendlyGeocoder( new NullGeocoder() ) );
+
+ $rectangle = $parser->parse( "51.8357775,33.83789:46,23.37890625~I'm a square~of doom" );
+
+ $this->assertInstanceOf( Rectangle::class, $rectangle );
+
+ $this->assertSame( "I'm a square", $rectangle->getTitle() );
+ $this->assertSame( 'of doom', $rectangle->getText() );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Integration/parsers/WmsOverlayParserTest.php b/www/wiki/extensions/Maps/tests/Integration/parsers/WmsOverlayParserTest.php
new file mode 100644
index 00000000..4787f263
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Integration/parsers/WmsOverlayParserTest.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Maps\Tests\Integration\parsers;
+
+use Maps\Presentation\WikitextParsers\WmsOverlayParser;
+use PHPUnit\Framework\TestCase;
+use PHPUnit4And6Compat;
+use ValueParsers\ParseException;
+
+/**
+ * @covers \Maps\Presentation\WikitextParsers\WmsOverlayParser
+ * @licence GNU GPL v2+
+ * @author Mathias Mølster Lidal <mathiaslidal@gmail.com>
+ */
+class WmsOverlayParserTest extends TestCase {
+ use PHPUnit4And6Compat;
+
+ public function testGivenValidInput_parserReturnsOverlayObject() {
+ $parser = new WmsOverlayParser();
+
+ $overlay = $parser->parse( 'http://demo.cubewerx.com/demo/cubeserv/cubeserv.cgi? Foundation.GTOPO30' );
+
+ $this->assertSame(
+ 'http://demo.cubewerx.com/demo/cubeserv/cubeserv.cgi?',
+ $overlay->getWmsServerUrl()
+ );
+
+ $this->assertSame(
+ 'Foundation.GTOPO30',
+ $overlay->getWmsLayerName()
+ );
+ }
+
+ public function testWhenStyleNameIsSpecified_getStyleNameReturnsIt() {
+ $parser = new WmsOverlayParser();
+
+ $overlay = $parser->parse(
+ 'http://maps.imr.no:80/geoserver/wms? vulnerable_areas:Identified_coral_area coral_identified_areas'
+ );
+
+ $this->assertSame(
+ 'coral_identified_areas',
+ $overlay->getWmsStyleName()
+ );
+ }
+
+ public function testWhenThereAreLessThanTwoSegments_parseExceptionIsThrown() {
+ $parser = new WmsOverlayParser();
+
+ $this->expectException( ParseException::class );
+ $parser->parse( 'Such' );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/System/SemanticMW/MapQueryTest.php b/www/wiki/extensions/Maps/tests/System/SemanticMW/MapQueryTest.php
new file mode 100644
index 00000000..be04fb44
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/System/SemanticMW/MapQueryTest.php
@@ -0,0 +1,93 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Maps\Tests\System\SemanticMW;
+
+use Maps\DataAccess\PageContentFetcher;
+use Maps\MapsFactory;
+use Maps\Tests\Util\PageCreator;
+use Maps\Tests\Util\TestFactory;
+use PHPUnit\Framework\TestCase;
+use Title;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class MapQueryTest extends TestCase {
+
+ /**
+ * @var PageCreator
+ */
+ private $pageCreator;
+
+ /**
+ * @var PageContentFetcher
+ */
+ private $contentFetcher;
+
+ public function setUp() {
+ if ( !defined( 'SMW_VERSION' ) ) {
+ $this->markTestSkipped( 'SMW is not available' );
+ }
+
+ $this->pageCreator = TestFactory::newInstance()->getPageCreator();
+ $this->contentFetcher = MapsFactory::newDefault()->getPageContentFetcher();
+ }
+
+ public function testMapQuery() {
+ $this->createPages();
+
+ $this->pageCreator->createPage(
+ 'MapQuery',
+ '{{#ask:[[Coordinates::+]]|?Coordinates|?Description|?URL|format=map}}'
+ );
+
+ // TODO: saner way
+ $content = $this->contentFetcher->getPageContent( 'MapQuery' )->getParserOutput( Title::newFromText( 'MapQuery' ) )->getText();
+
+ $this->assertContains(
+ '<div id="map_',
+ $content
+ );
+
+ $this->assertContains(
+ '<div id="map_',
+ $content
+ );
+ }
+
+ private function createPages() {
+ $this->pageCreator->createPage(
+ 'Property:Coordinates',
+ '[[Has type::Geographic coordinate|geographic coordinate]]'
+ );
+
+ $this->pageCreator->createPage(
+ 'Property:Description',
+ '[[Has type::Text]]'
+ );
+
+ $this->pageCreator->createPage(
+ 'Property:URL',
+ '[[Has type::URL]]'
+ );
+
+ $this->pageCreator->createPage(
+ 'Berlin',
+ '[[Coordinates::52° 31\' 0", 13° 24\' 0"]] [[Description::Capital of Germany]] [[URL::http://example.com/Berlin]]'
+ );
+
+ $this->pageCreator->createPage(
+ 'Brussels',
+ '[[Coordinates::50° 51\' 1", 4° 21\' 6"]] [[Description::Capital of Belgium]]'
+ );
+
+ $this->pageCreator->createPage(
+ 'Hamburg',
+ '[[Coordinates::53° 33\' 4", 9° 59\' 37"]]'
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Unit/Elements/BaseElementTest.php b/www/wiki/extensions/Maps/tests/Unit/Elements/BaseElementTest.php
new file mode 100644
index 00000000..bda1c9bd
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Unit/Elements/BaseElementTest.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace Maps\Tests\Unit\Elements;
+
+use InvalidArgumentException;
+use PHPUnit\Framework\TestCase;
+use PHPUnit4And6Compat;
+
+/**
+ * Base class for unit tests classes for the Maps\BaseElement deriving objects.
+ *
+ * @since 3.0
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+abstract class BaseElementTest extends TestCase {
+ use PHPUnit4And6Compat;
+
+ public function invalidConstructorProvider() {
+ return [];
+ }
+
+ /**
+ * Creates and returns a new instance of the concrete class.
+ *
+ * @since 3.0
+ *
+ * @return mixed
+ */
+ public function newInstance() {
+ $reflector = new \ReflectionClass( $this->getClass() );
+ $args = func_get_args();
+ $instance = $reflector->newInstanceArgs( $args );
+ return $instance;
+ }
+
+ /**
+ * Returns the name of the concrete class tested by this test.
+ *
+ * @since 3.0
+ *
+ * @return string
+ */
+ public abstract function getClass();
+
+ /**
+ * @since 3.0
+ *
+ * @return array [instance, constructor args]
+ */
+ public function instanceProvider() {
+ $phpFails = [ $this, 'newInstance' ];
+
+ return array_map(
+ function ( array $args ) use ( $phpFails ) {
+ return [ call_user_func_array( $phpFails, $args ), $args ];
+ },
+ $this->validConstructorProvider()
+ );
+ }
+
+ public abstract function validConstructorProvider();
+
+ /**
+ * @dataProvider validConstructorProvider
+ *
+ * @since 3.0
+ */
+ public function testGivenValidArguments_constructorDoesNotThrowException() {
+ $instance = call_user_func_array( [ $this, 'newInstance' ], func_get_args() );
+ $this->assertInstanceOf( $this->getClass(), $instance );
+ }
+
+ /**
+ * @dataProvider invalidConstructorProvider
+ *
+ * @since 3.0
+ */
+ public function testGivenInvalidArguments_constructorThrowsException() {
+ $this->expectException( InvalidArgumentException::class );
+ call_user_func_array( [ $this, 'newInstance' ], func_get_args() );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Unit/Elements/CircleTest.php b/www/wiki/extensions/Maps/tests/Unit/Elements/CircleTest.php
new file mode 100644
index 00000000..3800f059
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Unit/Elements/CircleTest.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Maps\Tests\Unit\Elements;
+
+use DataValues\Geo\Values\LatLongValue;
+use Maps\Elements\Circle;
+
+/**
+ * @covers \Maps\Elements\Circle
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class CircleTest extends BaseElementTest {
+
+ /**
+ * @see BaseElementTest::getClass
+ *
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getClass() {
+ return Circle::class;
+ }
+
+ public function validConstructorProvider() {
+ $argLists = [];
+
+ $argLists[] = [ new LatLongValue( 4, 2 ), 42 ];
+ $argLists[] = [ new LatLongValue( 42, 2.2 ), 9000.1 ];
+ $argLists[] = [ new LatLongValue( 4, 2 ), 1 ];
+ $argLists[] = [ new LatLongValue( 4, 2 ), 0.1 ];
+
+ return $argLists;
+ }
+
+ public function invalidConstructorProvider() {
+ $argLists = [];
+
+ $argLists[] = [ new LatLongValue( 4, 2 ), 0 ];
+ $argLists[] = [ new LatLongValue( 4, 2 ), -42 ];
+
+ return $argLists;
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ *
+ * @param Circle $circle
+ * @param array $arguments
+ */
+ public function testGetCircleCentre( Circle $circle, array $arguments ) {
+ $this->assertTrue( $circle->getCircleCentre()->equals( $arguments[0] ) );
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ *
+ * @param Circle $circle
+ * @param array $arguments
+ */
+ public function testGetCircleRadius( Circle $circle, array $arguments ) {
+ $this->assertEquals( $arguments[1], $circle->getCircleRadius() );
+ }
+
+}
+
+
+
diff --git a/www/wiki/extensions/Maps/tests/Unit/Elements/ImageOverlayTest.php b/www/wiki/extensions/Maps/tests/Unit/Elements/ImageOverlayTest.php
new file mode 100644
index 00000000..21c43c31
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Unit/Elements/ImageOverlayTest.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace Maps\Tests\Unit\Elements;
+
+use DataValues\Geo\Values\LatLongValue;
+use Jeroen\SimpleGeocoder\Geocoders\Decorators\CoordinateFriendlyGeocoder;
+use Jeroen\SimpleGeocoder\Geocoders\NullGeocoder;
+use Maps\Elements\ImageOverlay;
+use Maps\Presentation\WikitextParsers\ImageOverlayParser;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers \Maps\Elements\ImageOverlay
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class ImageOverlayTest extends TestCase {
+
+ public function testGetImage() {
+ $imageOverlay = new ImageOverlay(
+ new LatLongValue( 4, 2 ),
+ new LatLongValue( -4, -2 ),
+ 'Foo.png'
+ );
+
+ $this->assertSame( 'Foo.png', $imageOverlay->getImage() );
+ }
+
+ public function testGivenMetaData_overlayHasProvidedMetaData() {
+ $parser = new ImageOverlayParser( new CoordinateFriendlyGeocoder( new NullGeocoder() ) );
+
+ $overlay = $parser->parse( "1,2:3,4:https://such.an/image.png~Semantic MediaWiki~World domination imminent!~https://such.link" );
+
+ $this->assertSame( 'https://such.an/image.png', $overlay->getImage() );
+ $this->assertSame( 'Semantic MediaWiki', $overlay->getTitle() );
+ $this->assertSame( 'World domination imminent!', $overlay->getText() );
+ $this->assertSame( 'https://such.link', $overlay->getLink() );
+ }
+
+ public function testGivenLinkWithPrefix_linkIsParsedAndPrefixIsRemoved() {
+ $parser = new ImageOverlayParser( new CoordinateFriendlyGeocoder( new NullGeocoder() ) );
+
+ $overlay = $parser->parse( "1,2:3,4:https://such.an/image.png~Semantic MediaWiki~World domination imminent!~link:https://such.link" );
+
+ $this->assertSame( 'https://such.link', $overlay->getLink() );
+ }
+
+}
+
+
+
diff --git a/www/wiki/extensions/Maps/tests/Unit/Elements/LineTest.php b/www/wiki/extensions/Maps/tests/Unit/Elements/LineTest.php
new file mode 100644
index 00000000..5ca462ff
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Unit/Elements/LineTest.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace Maps\Tests\Unit\Elements;
+
+use DataValues\Geo\Values\LatLongValue;
+use Maps\Elements\Line;
+
+/**
+ * @covers \Maps\Elements\Line
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class LineTest extends BaseElementTest {
+
+ /**
+ * @see BaseElementTest::getClass
+ *
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getClass() {
+ return Line::class;
+ }
+
+ public function validConstructorProvider() {
+ $argLists = [];
+
+ $argLists[] = [ [] ];
+ $argLists[] = [ [ new LatLongValue( 4, 2 ) ] ];
+
+ $argLists[] = [
+ [
+ new LatLongValue( 4, 2 ),
+ new LatLongValue( 2, 4 ),
+ new LatLongValue( 42, 42 ),
+ ]
+ ];
+
+ return $argLists;
+ }
+
+ public function invalidConstructorProvider() {
+ $argLists = [];
+
+ $argLists[] = [ [ '~=[,,_,,]:3' ] ];
+ $argLists[] = [ [ new LatLongValue( 4, 2 ), '~=[,,_,,]:3' ] ];
+ $argLists[] = [ [ '~=[,,_,,]:3', new LatLongValue( 4, 2 ) ] ];
+
+ return $argLists;
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ *
+ * @param Line $line
+ * @param array $arguments
+ */
+ public function testGetLineCoordinates( Line $line, array $arguments ) {
+ $coordinates = $line->getLineCoordinates();
+
+ $this->assertInternalType( 'array', $coordinates );
+ $this->assertEquals( count( $arguments[0] ), count( $coordinates ) );
+
+ foreach ( $coordinates as $geoCoordinate ) {
+ $this->assertInstanceOf( LatLongValue::class, $geoCoordinate );
+ }
+ }
+
+}
+
+
+
diff --git a/www/wiki/extensions/Maps/tests/Unit/Elements/LocationTest.php b/www/wiki/extensions/Maps/tests/Unit/Elements/LocationTest.php
new file mode 100644
index 00000000..57041e07
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Unit/Elements/LocationTest.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Maps\Tests\Unit\Elements;
+
+use DataValues\Geo\Values\LatLongValue;
+use Maps\Elements\Location;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers \Maps\Elements\Location
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class LocationTest extends TestCase {
+
+ public function latLongValueProvider() {
+ $argLists = [];
+
+ $argLists[] = [ new LatLongValue( 0, 0 ) ];
+ $argLists[] = [ new LatLongValue( 4, 2 ) ];
+ $argLists[] = [ new LatLongValue( 42, 42 ) ];
+ $argLists[] = [ new LatLongValue( -4.2, -42 ) ];
+
+ return $argLists;
+ }
+
+ /**
+ * @dataProvider latLongValueProvider
+ */
+ public function testGivenLatLongInConstructor_getCoordinatesReturnsIt( LatLongValue $latLong ) {
+ $location = new Location( $latLong );
+ $this->assertTrue( $latLong->equals( $location->getCoordinates() ) );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Unit/Elements/PolygonTest.php b/www/wiki/extensions/Maps/tests/Unit/Elements/PolygonTest.php
new file mode 100644
index 00000000..f1c8f513
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Unit/Elements/PolygonTest.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Maps\Tests\Unit\Elements;
+
+use Maps\Elements\Polygon;
+
+/**
+ * @covers \Maps\Elements\Polygon
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class PolygonTest extends LineTest {
+
+ /**
+ * @see BaseElementTest::getClass
+ *
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getClass() {
+ return Polygon::class;
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ */
+ public function testSetOnlyVisibleOnHover( Polygon $polygon ) {
+ $this->assertFalse( $polygon->isOnlyVisibleOnHover() );
+
+ $polygon->setOnlyVisibleOnHover( true );
+ $this->assertTrue( $polygon->isOnlyVisibleOnHover() );
+
+ $polygon->setOnlyVisibleOnHover( false );
+ $this->assertFalse( $polygon->isOnlyVisibleOnHover() );
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ */
+ public function testSetFillOpacity( Polygon $polygon ) {
+ $polygon->setFillOpacity( '0.42' );
+ $this->assertHasJsonKeyWithValue( $polygon, 'fillOpacity', '0.42' );
+ }
+
+ protected function assertHasJsonKeyWithValue( Polygon $polygon, $key, $value ) {
+ $json = $polygon->getJSONObject();
+
+ $this->assertArrayHasKey( $key, $json );
+ $this->assertEquals(
+ $value,
+ $json[$key]
+ );
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ */
+ public function testSetFillColor( Polygon $polygon ) {
+ $polygon->setFillColor( '#FFCCCC' );
+ $this->assertHasJsonKeyWithValue( $polygon, 'fillColor', '#FFCCCC' );
+ }
+
+}
+
+
+
diff --git a/www/wiki/extensions/Maps/tests/Unit/Elements/RectangleTest.php b/www/wiki/extensions/Maps/tests/Unit/Elements/RectangleTest.php
new file mode 100644
index 00000000..daed71cb
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Unit/Elements/RectangleTest.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Maps\Tests\Unit\Elements;
+
+use DataValues\Geo\Values\LatLongValue;
+use Maps\Elements\Rectangle;
+
+/**
+ * @covers \Maps\Elements\Rectangle
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class RectangleTest extends BaseElementTest {
+
+ /**
+ * @see BaseElementTest::getClass
+ *
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getClass() {
+ return Rectangle::class;
+ }
+
+ public function validConstructorProvider() {
+ $argLists = [];
+
+ $argLists[] = [ new LatLongValue( 4, 2 ), new LatLongValue( -4, -2 ) ];
+ $argLists[] = [ new LatLongValue( -42, -42 ), new LatLongValue( -4, -2 ) ];
+
+ return $argLists;
+ }
+
+ public function invalidConstructorProvider() {
+ $argLists = [];
+
+ $argLists[] = [ new LatLongValue( 4, 2 ), new LatLongValue( 4, 2 ) ];
+
+ return $argLists;
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ */
+ public function testGetCorners( Rectangle $rectangle, array $arguments ) {
+ $this->assertTrue( $rectangle->getRectangleNorthEast()->equals( $arguments[0] ) );
+ $this->assertTrue( $rectangle->getRectangleSouthWest()->equals( $arguments[1] ) );
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ */
+ public function testSetCorners( Rectangle $rectangle ) {
+ $coordinates = [
+ new LatLongValue( 42, 42 ),
+ new LatLongValue( 0, 0 )
+ ];
+
+ foreach ( $coordinates as $coordinate ) {
+ $rectangle->setRectangleNorthEast( $coordinate );
+ $this->assertTrue( $rectangle->getRectangleNorthEast()->equals( $coordinate ) );
+
+ $rectangle->setRectangleSouthWest( $coordinate );
+ $this->assertTrue( $rectangle->getRectangleSouthWest()->equals( $coordinate ) );
+ }
+ }
+
+}
+
+
+
diff --git a/www/wiki/extensions/Maps/tests/Unit/Presentation/KmlFormatterTest.php b/www/wiki/extensions/Maps/tests/Unit/Presentation/KmlFormatterTest.php
new file mode 100644
index 00000000..cc88f005
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Unit/Presentation/KmlFormatterTest.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace Maps\Tests\Unit\Presentation;
+
+use DataValues\Geo\Values\LatLongValue;
+use Maps\Elements\Location;
+use Maps\Presentation\KmlFormatter;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers \Maps\Presentation\KmlFormatter
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class KmlFormatterTest extends TestCase {
+
+ public function testEmptyList() {
+ $this->assertSame(
+ '<?xml version="1.0" encoding="UTF-8"?>
+<kml xmlns="http://www.opengis.net/kml/2.2">
+ <Document>
+
+ </Document>
+</kml>',
+ ( new KmlFormatter() )->formatLocationsAsKml()
+ );
+ }
+
+ public function testSeveralLocations() {
+ $this->assertSame(
+ '<?xml version="1.0" encoding="UTF-8"?>
+<kml xmlns="http://www.opengis.net/kml/2.2">
+ <Document>
+ <Placemark>
+ <name><![CDATA[first title]]></name>
+ <description><![CDATA[first text]]></description>
+ <Point>
+ <coordinates>23,42.42,0</coordinates>
+ </Point>
+ </Placemark>
+ <Placemark>
+ <name><![CDATA[second title]]></name>
+ <description><![CDATA[second text]]></description>
+ <Point>
+ <coordinates>0,-1,0</coordinates>
+ </Point>
+ </Placemark>
+ </Document>
+</kml>',
+ ( new KmlFormatter() )->formatLocationsAsKml(
+ new Location(
+ new LatLongValue( 42.42,23 ),
+ 'first title',
+ 'first text'
+ ),
+ new Location(
+ new LatLongValue( -1,0 ),
+ 'second title',
+ 'second text'
+ )
+ )
+ );
+ }
+
+ public function testLocationWithoutTitleAndText() {
+ $this->assertSame(
+ '<?xml version="1.0" encoding="UTF-8"?>
+<kml xmlns="http://www.opengis.net/kml/2.2">
+ <Document>
+ <Placemark>
+ <name><![CDATA[]]></name>
+ <description><![CDATA[]]></description>
+ <Point>
+ <coordinates>23,42.42,0</coordinates>
+ </Point>
+ </Placemark>
+ </Document>
+</kml>',
+ ( new KmlFormatter() )->formatLocationsAsKml(
+ new Location(
+ new LatLongValue( 42.42,23 )
+ )
+ )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Unit/Presentation/ParameterExtractorTest.php b/www/wiki/extensions/Maps/tests/Unit/Presentation/ParameterExtractorTest.php
new file mode 100644
index 00000000..2e526809
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Unit/Presentation/ParameterExtractorTest.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Maps\Tests\Unit\Presentation;
+
+use Maps\Presentation\ParameterExtractor;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers \Maps\Presentation\ParameterExtractor
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class ParameterExtractorTest extends TestCase {
+
+ public function testGivenNoParameters_nullIsReturned() {
+ $this->assertNull( ( new ParameterExtractor() )->extract( [ 'name' ], [] ) );
+ }
+
+ public function testGivenWhenPrimaryNameIsPresent_itsValueIsReturned() {
+ $this->assertSame(
+ 'value',
+ ( new ParameterExtractor() )->extract(
+ [ 'name' ],
+ [ 'foo' => 'bar', 'name' => 'value', 'baz' => 'bah' ]
+ )
+ );
+ }
+
+ public function testGivenAliasIsPresent_itsValueIsReturned() {
+ $this->assertSame(
+ 'value',
+ ( new ParameterExtractor() )->extract(
+ [ 'name', 'secondary', 'alias' ],
+ [ 'foo' => 'bar', 'alias' => 'value', 'baz' => 'bah' ]
+ )
+ );
+ }
+
+ public function testWhenAliasAndPrimaryArePresent_thePrimariesValueIsReturned() {
+ $this->assertSame(
+ 'value',
+ ( new ParameterExtractor() )->extract(
+ [ 'name', 'secondary', 'alias' ],
+ [ 'foo' => 'bar', 'alias' => 'wrong', 'name' => 'value' ]
+ )
+ );
+ }
+
+ public function testValueIsTrimmed() {
+ $this->assertSame(
+ 'value',
+ ( new ParameterExtractor() )->extract(
+ [ 'name' ],
+ [ 'name' => " value\t " ]
+ )
+ );
+ }
+
+ public function testWhenUpperCaseIsUsedInTheName_itIsStillFound() {
+ $this->assertSame(
+ 'value',
+ ( new ParameterExtractor() )->extract(
+ [ 'name' ],
+ [ 'nAmE' => 'value' ]
+ )
+ );
+ }
+
+ public function testNameHasSpacesAroundIt_itIsStillFound() {
+ $this->assertSame(
+ 'value',
+ ( new ParameterExtractor() )->extract(
+ [ 'name' ],
+ [ ' name ' => 'value' ]
+ )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Unit/Semantic/DataValues/CoordinateValueTest.php b/www/wiki/extensions/Maps/tests/Unit/Semantic/DataValues/CoordinateValueTest.php
new file mode 100644
index 00000000..bbf8d0f1
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Unit/Semantic/DataValues/CoordinateValueTest.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace Maps\Tests\Unit\Semantic\DataValues;
+
+use Maps\SemanticMW\DataValues\CoordinateValue;
+use Maps\SemanticMW\ValueDescriptions\AreaDescription;
+use Maps\SemanticMW\ValueDescriptions\CoordinateDescription;
+use PHPUnit\Framework\TestCase;
+use SMW\DataValueFactory;
+use SMWDataItem;
+use SMWDIGeoCoord;
+
+/**
+ * @covers \Maps\SemanticMW\ValueDescriptions\CoordinateValue
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class CoordinateValueTest extends TestCase {
+
+ public function setUp() {
+ if ( !defined( 'SMW_VERSION' ) ) {
+ $this->markTestSkipped( 'SMW is not available' );
+ }
+ }
+
+ public function testConstruct() {
+ $geoDI = new SMWDIGeoCoord( 23, 42 );
+
+ /**
+ * @var CoordinateValue $geoValue
+ */
+ $geoValue = DataValueFactory::newDataItemValue( $geoDI );
+
+ $this->assertInstanceOf( CoordinateValue::class, $geoValue );
+
+ $this->assertEquals( $geoDI, $geoValue->getDataItem() );
+ $this->assertSame( '23° 0\' 0.00" N, 42° 0\' 0.00" E', $geoValue->getShortWikiText() );
+ }
+
+ /**
+ * @dataProvider coordinateProvider
+ */
+ public function testGetQueryDescription( $lat, $long, $serialization ) {
+ $geoValue = $this->newInstance();
+
+ $description = $geoValue->getQueryDescription( $serialization );
+
+ $this->assertIsCorrectCoordValue( $description, $lat, $long );
+ }
+
+ protected function newInstance() {
+ return new CoordinateValue( SMWDataItem::TYPE_GEO );
+ }
+
+ private function assertIsCorrectCoordValue( $description, $lat, $long ) {
+ /**
+ * @var CoordinateDescription $description
+ */
+ $this->assertInstanceOf( CoordinateDescription::class, $description );
+ $this->assertEquals( $lat, $description->getDataItem()->getLatitude() );
+ $this->assertEquals( $long, $description->getDataItem()->getLongitude() );
+ }
+
+ public function coordinateProvider() {
+ return [
+ [
+ 23,
+ 42,
+ '23° 0\' 0", 42° 0\' 0"',
+ ],
+ [
+ 0,
+ 0,
+ '0° 0\' 0", 0° 0\' 0"',
+ ],
+ [
+ -23.5,
+ -42.5,
+ '-23° 30\' 0", -42° 30\' 0"',
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider coordinateWithDistanceProvider
+ */
+ public function testGetQueryDescriptionForArea( $serialization ) {
+ $geoValue = $this->newInstance();
+
+ $description = $geoValue->getQueryDescription( $serialization );
+
+ $this->assertInstanceOf( AreaDescription::class, $description );
+ }
+
+ public function coordinateWithDistanceProvider() {
+ return [
+ [
+ '23° 0\' 0", 42° 0\' 0"(1km)',
+ 1000,
+ ],
+ [
+ '0° 0\' 0", 0° 0\' 0" ( 1 m )',
+ 1,
+ ],
+ [
+ '-23° 30\' 0", -42° 30\' 0" (9001m)',
+ 9001,
+ ],
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Util/PageCreator.php b/www/wiki/extensions/Maps/tests/Util/PageCreator.php
new file mode 100644
index 00000000..ca5ba675
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Util/PageCreator.php
@@ -0,0 +1,24 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Maps\Tests\Util;
+
+use Title;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class PageCreator {
+
+ public function createPage( string $title, string $content = null ) {
+ $page = new \WikiPage( Title::newFromText( $title ) );
+
+ $page->doEditContent(
+ new \WikitextContent( $content ?? 'Content of ' . $title ),
+ __CLASS__ . ' creating page ' . $title
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/Util/TestFactory.php b/www/wiki/extensions/Maps/tests/Util/TestFactory.php
new file mode 100644
index 00000000..286272d1
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/Util/TestFactory.php
@@ -0,0 +1,21 @@
+<?php
+
+declare( strict_types = 1 );
+
+namespace Maps\Tests\Util;
+
+/**
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class TestFactory {
+
+ public static function newInstance(): self {
+ return new self();
+ }
+
+ public function getPageCreator(): PageCreator {
+ return new PageCreator();
+ }
+
+}
diff --git a/www/wiki/extensions/Maps/tests/bootstrap.php b/www/wiki/extensions/Maps/tests/bootstrap.php
new file mode 100644
index 00000000..1b0e39e5
--- /dev/null
+++ b/www/wiki/extensions/Maps/tests/bootstrap.php
@@ -0,0 +1,33 @@
+<?php
+
+if ( PHP_SAPI !== 'cli' ) {
+ die( 'Not an entry point' );
+}
+
+if ( defined( 'MEDIAWIKI' ) ) {
+ // If testing against an older version of MediaWiki, define
+ // an empty trait to avoid fatal errors.
+ if ( !trait_exists( PHPUnit4And6Compat::class ) ) {
+ trait PHPUnit4And6Compat {
+ public function expectException( string $exception ) {
+ $this->setExpectedException( $exception );
+ }
+ }
+ }
+
+ return;
+}
+
+if ( !trait_exists( PHPUnit4And6Compat::class ) ) {
+ trait PHPUnit4And6Compat {
+ }
+}
+
+error_reporting( -1 );
+ini_set( 'display_errors', 1 );
+
+if ( !is_readable( __DIR__ . '/../vendor/autoload.php' ) ) {
+ die( 'You need to install this package with Composer before you can run the tests' );
+}
+
+require __DIR__ . '/../vendor/autoload.php'; \ No newline at end of file