jenkins-bot has submitted this change and it was merged.

Change subject: Static map page for non-JS support of maplinks
......................................................................


Static map page for non-JS support of maplinks

Special:Map/{zoom}/{latitude}/{longitude}

Bug: T144003
Change-Id: I90fcd2fb0e458c9d5a805b441f5114157ea470a4
---
A Kartographer.alias.php
M extension.json
M i18n/en.json
M i18n/qqq.json
A includes/Projection/EPSG3857.php
A includes/Projection/SphericalMercator.php
A includes/Projection/Transformation.php
A includes/SpecialMap.php
M includes/Tag/MapLink.php
A styles/specialMap.less
M tests/parserTests.txt
A tests/phpunit/SpecialMapTest.php
12 files changed, 474 insertions(+), 24 deletions(-)

Approvals:
  Yurik: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/Kartographer.alias.php b/Kartographer.alias.php
new file mode 100644
index 0000000..c788e1a
--- /dev/null
+++ b/Kartographer.alias.php
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Aliases for special pages for extension Echo
+ *
+ * @file
+ * @ingroup Extensions
+ */
+// @codingStandardsIgnoreFile
+
+$specialPageAliases = array();
+
+/** English (English) */
+$specialPageAliases['en'] = [
+       'Map' => [ 'Map' ],
+];
diff --git a/extension.json b/extension.json
index 0af590a..ea345dd 100644
--- a/extension.json
+++ b/extension.json
@@ -16,6 +16,12 @@
                        "modules/wikivoyage/i18n"
                ]
        },
+       "ExtensionMessagesFiles": {
+               "KartogrpaherAliases": "Kartographer.alias.php"
+       },
+       "SpecialPages": {
+               "Map": "Kartographer\\SpecialMap"
+       },
        "AutoloadClasses": {
                "Kartographer\\ApiQueryMapData": "includes/ApiQueryMapData.php",
                "Kartographer\\ApiSanitizeMapData": 
"includes/ApiSanitizeMapData.php",
@@ -23,10 +29,14 @@
                "Kartographer\\DataModule": "includes/DataModule.php",
                "Kartographer\\Hooks": "includes/Hooks.php",
                "Kartographer\\SimpleStyleParser": 
"includes/SimpleStyleParser.php",
+               "Kartographer\\SpecialMap": "includes/SpecialMap.php",
                "Kartographer\\State": "includes/State.php",
                "Kartographer\\Tag\\MapFrame": "includes/Tag/MapFrame.php",
                "Kartographer\\Tag\\MapLink": "includes/Tag/MapLink.php",
-               "Kartographer\\Tag\\TagHandler": "includes/Tag/TagHandler.php"
+               "Kartographer\\Tag\\TagHandler": "includes/Tag/TagHandler.php",
+               "Kartographer\\Projection\\EPSG3857": 
"includes/Projection/EPSG3857.php",
+               "Kartographer\\Projection\\SphericalMercator": 
"includes/Projection/SphericalMercator.php",
+               "Kartographer\\Projection\\Transformation": 
"includes/Projection/Transformation.php"
        },
        "APIModules": {
                "sanitize-mapdata": "Kartographer\\ApiSanitizeMapData"
@@ -324,6 +334,15 @@
                                "mobile",
                                "desktop"
                        ]
+               },
+               "ext.kartographer.specialMap": {
+                       "styles": [
+                               "styles/specialMap.less"
+                       ],
+                       "targets": [
+                               "mobile",
+                               "desktop"
+                       ]
                }
        },
        "ResourceFileModulePaths": {
diff --git a/i18n/en.json b/i18n/en.json
index 6249d19..7740904 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -33,6 +33,7 @@
        "kartographer-coord-lat-negative": "$1S",
        "kartographer-coord-lon-positive": "$1E",
        "kartographer-coord-lon-negative": "$1W",
+       "kartographer-specialmap-invalid-coordinates": "Invalid coordinates 
supplied",
        "kartographer.css": "/* CSS placed here will be applied to all pages 
with maps */",
        "kartographer.js": "/* Any JavaScript here will be loaded for all users 
on load of the map-containing pages */",
        "leafletdraw-draw-handlers-circle-radius": "Radius",
@@ -67,6 +68,7 @@
        "leafletdraw-edit-toolbar-buttons-editdisabled": "No layers to edit",
        "leafletdraw-edit-toolbar-buttons-remove": "Delete layers",
        "leafletdraw-edit-toolbar-buttons-removedisabled": "No layers to 
delete",
+       "map": "Coordinates information",
        "mapbox-control-zoomin-title": "Zoom in",
        "mapbox-control-zoomout-title": "Zoom out",
        "kartographer-fullscreen-close": "Close",
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 92b856c..1c1e886 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -35,7 +35,8 @@
        "kartographer-coord-lat-positive": "{{optional}}\nNorth latitude suffix 
for geographical coordinates. $1 is value in degrees/minutes/seconds, see 
{{msg-mw|kartographer-coord-dms}}",
        "kartographer-coord-lat-negative": "{{optional}}\nSouth latitude suffix 
for geographical coordinates. $1 is value in degrees/minutes/seconds, see 
{{msg-mw|kartographer-coord-dms}}",
        "kartographer-coord-lon-positive": "{{optional}}\nEast longitude suffix 
for geographical coordinates. $1 is value in degrees/minutes/seconds, see 
{{msg-mw|kartographer-coord-dms}}",
-       "kartographer-coord-lon-negative": "{{optional}}\nWest longitude suffix 
for geographical coordinates. $1 is value in degrees/minutes/seconds, see 
{{msg-mw|kartographer-coord-dms}}",
+       "kartographer-coord-lon-negative": "{`{optional}}\nWest longitude 
suffix for geographical coordinates. $1 is value in degrees/minutes/seconds, 
see {{msg-mw|kartographer-coord-dms}}",
+       "kartographer-specialmap-invalid-coordinates": "Displayed by 
Special:Map when no valid coordinates are supplied",
        "kartographer.css": "{{optional}}",
        "kartographer.js": "{{optional}}",
        "leafletdraw-draw-handlers-circle-radius": "Label for circle 
radius\n{{Identical|Radius}}",
@@ -70,6 +71,7 @@
        "leafletdraw-edit-toolbar-buttons-editdisabled": "Tooltip for edit 
layers button when disabled",
        "leafletdraw-edit-toolbar-buttons-remove": "Tooltip for delete layers 
button",
        "leafletdraw-edit-toolbar-buttons-removedisabled": "Tooltip for delete 
layers button when disabled",
+       "map": "Page title for Special:Map",
        "mapbox-control-zoomin-title": "Title for map zoom in 
button\n{{Identical|Zoom in}}",
        "mapbox-control-zoomout-title": "Title for map zoom out 
button\n{{Identical|Zoom out}}",
        "kartographer-fullscreen-close": "Title of the fullscreen close 
button\n{{Identical|Close}}",
diff --git a/includes/Projection/EPSG3857.php b/includes/Projection/EPSG3857.php
new file mode 100644
index 0000000..5303f38
--- /dev/null
+++ b/includes/Projection/EPSG3857.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Kartographer\Projection;
+
+/**
+ * EPSG3857 (Spherical Mercator) is the most common CRS for web mapping
+ * and is used by Leaflet by default.
+ *
+ * Converted to PHP from L.CRS.EPSG3857 (leaflet.js)
+ */
+class EPSG3857 {
+
+       const EARTH_RADIUS = 6378137;
+
+       /**
+        * (LatLon) -> Point
+        *
+        * @param float[] $latLon
+        * @return float[]
+        */
+       public static function project( $latLon ) {
+               $projectedPoint = SphericalMercator::project( $latLon );
+
+               return [ $projectedPoint * self::EARTH_RADIUS, $projectedPoint 
* self::EARTH_RADIUS ];
+       }
+
+       /**
+        * (LatLon, Number) -> Point
+        *
+        * @param float[] $latLon
+        * @param int $zoom
+        * @return array
+        */
+       public static function latLonToPoint( $latLon, $zoom ) {
+               $projectedPoint = SphericalMercator::project( $latLon );
+               $scale = self::scale( $zoom );
+
+               return Transformation::transform( $projectedPoint, $scale );
+       }
+
+       /**
+        * (Point, Number[, Boolean]) -> LatLon
+        *
+        * @param $point
+        * @param $zoom
+        * @return array
+        */
+       public static function pointToLatLon( $point, $zoom ) {
+               $scale = self::scale( $zoom );
+               $untransformedPoint = Transformation::untransform( $point, 
$scale );
+
+               return SphericalMercator::unproject( $untransformedPoint );
+       }
+
+       public static function scale( $zoom ) {
+               return 256 * pow( 2, $zoom );
+       }
+
+       public static function getSize( $zoom ) {
+               $size = self::scale( $zoom );
+
+               return [ $size, $size ];
+       }
+}
diff --git a/includes/Projection/SphericalMercator.php 
b/includes/Projection/SphericalMercator.php
new file mode 100644
index 0000000..33d9c48
--- /dev/null
+++ b/includes/Projection/SphericalMercator.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Kartographer\Projection;
+
+/**
+ * Spherical Mercator is the most popular map projection,
+ * used by EPSG:3857 CRS.
+ *
+ * Converted to PHP from L.Projection.SphericalMercator (leaflet.js)
+ */
+class SphericalMercator {
+
+       const MAX_LATITUDE = 85.0511287798;
+
+       /**
+        * (LatLon) -> Point
+        *
+        * @param float[] $latLon
+        * @return array
+        */
+       public static function project( $latLon ) {
+
+               $lat = max( min( self::MAX_LATITUDE, $latLon[0] ), 
-self::MAX_LATITUDE );
+               $x = deg2rad( $latLon[1] );
+               $y = deg2rad( $lat );
+
+               $y = log( tan( ( pi() / 4 ) + ( $y / 2 ) ) );
+
+               return [ $x, $y ];
+       }
+
+       /**
+        * (Point, Boolean) -> LatLon
+        *
+        * @param float[] $point
+        * @return array
+        */
+       public static function unproject( $point ) {
+               $lon = rad2deg( $point[0] );
+               $lat = rad2deg( 2 * atan( exp( $point[1] ) ) - ( pi() / 2 ) );
+
+               return [ $lat, $lon ];
+       }
+}
diff --git a/includes/Projection/Transformation.php 
b/includes/Projection/Transformation.php
new file mode 100644
index 0000000..04d8380
--- /dev/null
+++ b/includes/Projection/Transformation.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Kartographer\Projection;
+
+/**
+ * Transformation is an utility class to perform simple point transformations
+ * through a 2d-matrix.
+ *
+ * Converted to PHP from L.Transformation (leaflet.js)
+ */
+class Transformation {
+// @fixme: cleanup
+       const A = 0.159154943; // 0.5 * pi()
+       const C = -0.159154943; // -0.5 * pi()
+
+       /**
+        * (LatLon) -> Point
+        *
+        * @param float[] $point
+        * @param int $scale
+        * @return float[]
+        */
+       public static function transform( $point, $scale = 1 ) {
+
+               $x = $point[0];
+               $y = $point[1];
+
+               $x = $scale * ( self::A * $x + 0.5);
+               $y = $scale * ( self::C * $y + 0.5 );
+
+               return [ $x, $y ];
+       }
+
+       public static function untransform( $point, $scale = 1 ) {
+               $x = $point[0];
+               $y = $point[1];
+
+               $x = ( $x / $scale - 0.5 ) / self::A;
+               $y = ( $y / $scale - 0.5 ) / self::C;
+
+               return [ $x, $y ];
+       }
+}
diff --git a/includes/SpecialMap.php b/includes/SpecialMap.php
new file mode 100644
index 0000000..651e7a1
--- /dev/null
+++ b/includes/SpecialMap.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace Kartographer;
+
+use GeoData\Globe;
+use Html;
+use SpecialPage;
+use Title;
+use Kartographer\Projection\EPSG3857;
+
+/**
+ * Special page that works as a fallback destination for non-JS users
+ * who click on map links. It displays a world map with a dot for the given 
location.
+ * URL format: Special:Map/<zoom>/<lat>/<lon>
+ * Zoom isn't used anywhere yet.
+ */
+class SpecialMap extends SpecialPage {
+       public function __construct( $name = 'Map' ) {
+               parent::__construct( $name, /* $restriction */ '', /* $listed 
*/ false );
+       }
+
+       public function execute( $par ) {
+               $this->setHeaders();
+               $this->getOutput()->addModuleStyles( 
'ext.kartographer.specialMap' );
+
+               $coord = self::parseSubpage( $par );
+               if ( !$coord ) {
+                       $coordText = wfMessage( 
'kartographer-specialmap-invalid-coordinates' )->text();
+                       $markerHtml = '';
+               } else {
+                       list( , $lat, $lon ) = $coord;
+                       $coordText = CoordFormatter::format( $lat, $lon, 
$this->getLanguage() );
+                       list( $x, $y ) = EPSG3857::latLonToPoint( [ $lat, $lon 
], 0 );
+                       $markerHtml = Html::element( 'div',
+                               [
+                                       'id' => 'mw-specialMap-marker',
+                                       'style' => "left:{$x}px; top:{$y}px;"
+                               ]
+                       );
+               }
+
+               $attributions = Html::rawElement( 'div', [ 'id' => 
'mw-specialMap-attributions' ],
+                       wfMessage( 'kartographer-attribution' )->parse() );
+
+               $this->getOutput()->addHTML(
+                       Html::openElement( 'div', [ 'id' => 
'mw-specialMap-container', 'class' => 'thumb' ] )
+                               . Html::openElement( 'div', [ 'class' => 
'thumbinner' ] )
+                                       . Html::openElement( 'div', [ 'id' => 
'mw-specialMap-inner' ] )
+                                               . Html::element( 'div', [ 'id' 
=> 'mw-specialMap-map' ] )
+                                               . $markerHtml
+                                               . $attributions
+                                       . Html::closeElement( 'div' )
+                                       . Html::openElement( 'div', [ 'id' => 
'mw-specialMap-caption', 'class' => 'thumbcaption' ] )
+                                               . Html::element( 'span', [ 'id' 
=> 'mw-specialMap-icon' ] )
+                                               . Html::element( 'span', [ 'id' 
=> 'mw-specialMap-coords' ], $coordText )
+                                       . Html::closeElement( 'div' )
+                               . Html::closeElement( 'div' )
+                       . Html::closeElement( 'div' )
+               );
+       }
+
+       /**
+        * Parses subpage parameter to this special page into zoom / lat /lon
+        *
+        * @param $par
+        * @return array|bool
+        */
+       public static function parseSubpage( $par ) {
+               if ( !preg_match( 
'#^(?<zoom>\d+)/(?<lat>-?\d+(\.\d+)?)/(?<lon>-?\d+(\.\d+)?)$#', $par, $matches 
) ) {
+                       return false;
+               }
+
+               if ( class_exists( Globe::class ) ) {
+                       $globe = new Globe( 'earth' );
+
+                       if ( !$globe->coordinatesAreValid( $matches['lat'], 
$matches['lon'] ) ) {
+                               return false;
+                       }
+               }
+
+               return [ (int)$matches['zoom'], (float)$matches['lat'], 
(float)$matches['lon'] ];
+       }
+
+       /**
+        * Returns a Title for a link to the coordinates provided
+        *
+        * @param float $lat
+        * @param float $lon
+        * @param int $zoom
+        * @return Title
+        */
+       public static function link( $lat, $lon, $zoom ) {
+               return SpecialPage::getTitleFor( 'Map', "$zoom/$lat/$lon" );
+       }
+}
diff --git a/includes/Tag/MapLink.php b/includes/Tag/MapLink.php
index f11c667..a55a5ca 100644
--- a/includes/Tag/MapLink.php
+++ b/includes/Tag/MapLink.php
@@ -5,6 +5,7 @@
 use FormatJson;
 use Html;
 use Kartographer\CoordFormatter;
+use Kartographer\SpecialMap;
 
 /**
  * The <maplink> tag creates a link that, when clicked,
@@ -35,6 +36,7 @@
                        'class' => 'mw-kartographer-maplink',
                        'mw-data' => 'interface',
                        'data-style' => $this->mapStyle,
+                       'href' => SpecialMap::link( $this->lat, $this->lon, 
$this->zoom )->getLocalURL()
                ];
                if ( $this->zoom !== null ) {
                        $attrs['data-zoom'] = $this->zoom;
diff --git a/styles/specialMap.less b/styles/specialMap.less
new file mode 100644
index 0000000..f2cb16c
--- /dev/null
+++ b/styles/specialMap.less
@@ -0,0 +1,92 @@
+@mapWidth: 256px;
+@mapHeight: 256px;
+@markerColor: #7b4e10;
+@markerSize: 10px;
+
+/* stylelint-disable selector-no-id */
+
+div#mw-specialMap-container {
+
+       position: relative;
+
+       .thumbinner {
+               display: inline-block;
+               position: relative;
+               width: @mapWidth;
+       }
+}
+
+div#mw-specialMap-inner {
+       height: @mapHeight;
+       position: relative;
+}
+
+div#mw-specialMap-map {
+       position: absolute;
+       width: @mapWidth;
+       height: @mapHeight;
+       /* stylelint-disable function-parentheses-space-inside */
+       background-image: url(//maps.wikimedia.org/osm-intl/0/0/0.png);
+       /* stylelint-enable function-parentheses-space-inside */
+       background-position: center;
+       background-repeat: no-repeat;
+       background-size: @mapWidth @mapHeight;
+}
+
+/* stylelint-disable media-feature-name-no-vendor-prefix */
+@media ( -webkit-min-device-pixel-ratio: 1.5 ),
+       ( min--moz-device-pixel-ratio: 1.5 ),
+       ( min-resolution: 1.5dppx ),
+       ( min-resolution: 144dpi ) {
+       div#mw-specialMap-map {
+               /* stylelint-disable function-parentheses-space-inside */
+               background-image: 
url(//maps.wikimedia.org/osm-intl/0/0/0...@1.5x.png);
+               /* stylelint-enable function-parentheses-space-inside */
+       }
+}
+
+@media ( -webkit-min-device-pixel-ratio: 2 ),
+       ( min--moz-device-pixel-ratio: 2 ),
+       ( min-resolution: 2dppx ),
+       ( min-resolution: 192dpi ) {
+       div#mw-specialMap-map {
+               /* stylelint-disable function-parentheses-space-inside */
+               background-image: 
url(//maps.wikimedia.org/osm-intl/0/0/0...@2x.png);
+               /* stylelint-enable function-parentheses-space-inside */
+       }
+}
+/* stylelint-enable media-feature-name-no-vendor-prefix */
+
+div#mw-specialMap-marker {
+       position: absolute;
+       background-color: @markerColor;
+       height: @markerSize;
+       width: @markerSize;
+       margin-top: -1 * (@markerSize/2);
+       margin-left: -1 * (@markerSize/2);
+       border-radius: @markerSize/2;
+}
+
+div#mw-specialMap-caption {
+       display: block;
+       padding-top: 0.5em;
+}
+
+span#mw-specialMap-icon {
+       display: inline-block;
+       margin: 0 12px 0 0;
+       background-color: @markerColor;
+       height: @markerSize;
+       width: @markerSize;
+       margin-top: -1 * (@markerSize/2);
+       margin-left: 0.5em;
+       border-radius: @markerSize/2;
+}
+
+div#mw-specialMap-attributions {
+       position: absolute;
+       bottom: 0;
+       font-size: 9px;
+       right: 0;
+       text-align: right;
+}
diff --git a/tests/parserTests.txt b/tests/parserTests.txt
index 856ed8d..0448d48 100644
--- a/tests/parserTests.txt
+++ b/tests/parserTests.txt
@@ -4,8 +4,8 @@
 <maplink />
 <maplink latitude=10 longitude=20 zoom=13 text='Foo &amp; bar'/>
 !! result
-<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl">0°0′0″N 0°0′0″E</a>
-<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" 
data-zoom="13" data-lat="10" data-lon="20">Foo &amp; bar</a>
+<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" href="/wiki/Special:Map///">0°0′0″N 0°0′0″E</a>
+<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" 
href="/wiki/Special:Map/13/10/20" data-zoom="13" data-lat="10" 
data-lon="20">Foo &amp; bar</a>
 </p>
 !! end
 
@@ -14,14 +14,14 @@
 !! input
 <maplink latitude=10 longitude=20 zoom=13 text='<&'/>
 !! result
-<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" data-zoom="13" data-lat="10" data-lon="20">&lt;&amp;</a>
+<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" href="/wiki/Special:Map/13/10/20" data-zoom="13" 
data-lat="10" data-lon="20">&lt;&amp;</a>
 </p>
 !! end
 
 !! test
 <maplink> - counters and markers
 !! input
-<maplink latitude=10 longitude=20 zoom=13 text='Foo'>
+<maplink latitude=10.123 longitude=20.456 zoom=13 text='Foo'>
 {
 "type": "Feature",
        "geometry": {
@@ -73,10 +73,10 @@
 }
 </maplink>
 !! result
-<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" data-zoom="13" data-lat="10" data-lon="20" 
data-overlays="[&quot;_b34053f88dea58fe70a7da09c558f792394f6e11&quot;]">Foo</a>
-</p><p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" data-zoom="13" data-lat="10" data-lon="20" 
data-overlays="[&quot;_2e4133dc429eb3c3abf4e45916c073e841f9d193&quot;]">2</a>
-</p><p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" data-zoom="13" data-lat="10" data-lon="20" 
data-overlays="[&quot;_dd3c7bae539f7c3747c64d043c97e1a8834fbc8f&quot;]">A</a>
-</p><p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm" 
data-zoom="13" data-lat="10" data-lon="20" 
data-overlays="[&quot;_8ce73f7395e427bd6f462824717c0a214b3ca564&quot;]">B</a>
+<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" href="/wiki/Special:Map/13/10.123/20.456" data-zoom="13" 
data-lat="10.123" data-lon="20.456" 
data-overlays="[&quot;_b34053f88dea58fe70a7da09c558f792394f6e11&quot;]">Foo</a>
+</p><p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" href="/wiki/Special:Map/13/10/20" data-zoom="13" 
data-lat="10" data-lon="20" 
data-overlays="[&quot;_2e4133dc429eb3c3abf4e45916c073e841f9d193&quot;]">2</a>
+</p><p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" href="/wiki/Special:Map/13/10/20" data-zoom="13" 
data-lat="10" data-lon="20" 
data-overlays="[&quot;_dd3c7bae539f7c3747c64d043c97e1a8834fbc8f&quot;]">A</a>
+</p><p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm" 
href="/wiki/Special:Map/13/10/20" data-zoom="13" data-lat="10" data-lon="20" 
data-overlays="[&quot;_8ce73f7395e427bd6f462824717c0a214b3ca564&quot;]">B</a>
 </p>
 !! end
 
@@ -85,7 +85,7 @@
 !! input
 <maplink latitude=10 longitude=20 zoom=13 />
 !! result
-<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" data-zoom="13" data-lat="10" data-lon="20">10°0′0″N 
20°0′0″E</a>
+<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" href="/wiki/Special:Map/13/10/20" data-zoom="13" 
data-lat="10" data-lon="20">10°0′0″N 20°0′0″E</a>
 </p>
 !! end
 
@@ -99,9 +99,9 @@
 <maplink zoom=0 latitude=0 longitude=0 class="class1 class2" text="Multiple 
classes aren't allowed for now"/>
 <maplink zoom=0 latitude=0 longitude=0 class="-Invalid-cl@ss-symbols"/>
 !! result
-<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" data-zoom="0" data-lat="0" data-lon="0">Empty class - do 
nothing</a>
-<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" 
data-zoom="0" data-lat="0" data-lon="0">Whitespace only class - do nothing</a>
-<a class="mw-kartographer-maplink Valid-link_class_123" mw-data="interface" 
data-style="osm-intl" data-zoom="0" data-lat="0" data-lon="0">Valid class</a>
+<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" 
data-lon="0">Empty class - do nothing</a>
+<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" 
href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" 
data-lon="0">Whitespace only class - do nothing</a>
+<a class="mw-kartographer-maplink Valid-link_class_123" mw-data="interface" 
data-style="osm-intl" href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" 
data-lon="0">Valid class</a>
 </p>
 <div class="mw-kartographer-error">&lt;maplink&gt;: Attribute "class" has an 
invalid value</div>
 <div class="mw-kartographer-error">&lt;maplink&gt;: Attribute "class" has an 
invalid value</div>
@@ -228,7 +228,7 @@
 !! input
 <maplink latitude=10 longitude=20 zoom=10 style="color: red;" text="<span 
style='foo: bar; background-image: 
url(https://example.com);'&gt;foo</span&gt;"/><!-- style=... was removed -->
 !! result
-<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" data-zoom="10" data-lat="10" data-lon="20"><span 
style="/* insecure input */">foo</span></a>
+<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" href="/wiki/Special:Map/10/10/20" data-zoom="10" 
data-lat="10" data-lon="20"><span style="/* insecure input */">foo</span></a>
 </p>
 !! end
 
@@ -269,7 +269,7 @@
 }
 ]</maplink>
 !! result
-<p><a class="mw-kartographer-maplink mw-kartographer-autostyled" 
mw-data="interface" data-style="osm-intl" data-zoom="13" data-lat="10" 
data-lon="20" style="background: #abcdef;" 
data-overlays="[&quot;_0d28e3b8954f29b4a6af62bd43a9cacc70ce0261&quot;]">A</a>
+<p><a class="mw-kartographer-maplink mw-kartographer-autostyled" 
mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/13/10/20" 
data-zoom="13" data-lat="10" data-lon="20" style="background: #abcdef;" 
data-overlays="[&quot;_0d28e3b8954f29b4a6af62bd43a9cacc70ce0261&quot;]">A</a>
 </p>
 !! end
 
@@ -292,7 +292,7 @@
 }
 </maplink>
 !! result
-<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" data-zoom="13" data-lat="10" data-lon="20" 
data-overlays="[&quot;_a724df91b09b20c8c7ea0dc28127eaed1727511b&quot;]">A</a>
+<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" href="/wiki/Special:Map/13/10/20" data-zoom="13" 
data-lat="10" data-lon="20" 
data-overlays="[&quot;_a724df91b09b20c8c7ea0dc28127eaed1727511b&quot;]">A</a>
 </p>
 !! end
 
@@ -316,7 +316,7 @@
 }
 </mapframe>
 !! result
-<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" data-zoom="0" data-lat="0" data-lon="0" 
data-overlays="[&quot;ponies&quot;]">0°0′0″N 0°0′0″E</a>
+<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" 
data-lon="0" data-overlays="[&quot;ponies&quot;]">0°0′0″N 0°0′0″E</a>
 </p>
 <div class="mw-kartographer-container thumb tright"><div class="thumbinner" 
style="width: 300px;"><div class="mw-kartographer-map" mw-data="interface" 
data-style="osm-intl" data-width="300" data-height="300" data-zoom="0" 
data-lat="0" data-lon="0" data-overlays="[&quot;ponies&quot;]" style="height: 
300px;"></div><div class="thumbcaption"></div></div></div>
 
@@ -342,7 +342,7 @@
 }
 </mapframe>
 !! result
-<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" data-zoom="0" data-lat="0" data-lon="0">0°0′0″N 
0°0′0″E</a>
+<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" 
data-lon="0">0°0′0″N 0°0′0″E</a>
 </p>
 <div class="mw-kartographer-container thumb tright"><div class="thumbinner" 
style="width: 300px;"><div class="mw-kartographer-map" mw-data="interface" 
data-style="osm-intl" data-width="300" data-height="300" data-zoom="0" 
data-lat="0" data-lon="0" 
data-overlays="[&quot;_a724df91b09b20c8c7ea0dc28127eaed1727511b&quot;]" 
style="height: 300px;"></div><div class="thumbcaption"></div></div></div>
 
@@ -360,11 +360,11 @@
 <maplink zoom=0 latitude=0 longitude=0 show=""/>
 <maplink zoom=0 latitude=0 longitude=0 show='йа криветко'/>
 !! result
-<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" data-zoom="0" data-lat="0" data-lon="0" 
data-overlays="[&quot;foo&quot;]">0°0′0″N 0°0′0″E</a>
-<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" 
data-zoom="0" data-lat="0" data-lon="0" 
data-overlays="[&quot;foo&quot;,&quot;bar&quot;]">0°0′0″N 0°0′0″E</a>
-<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" 
data-zoom="0" data-lat="0" data-lon="0" 
data-overlays="[&quot;foo&quot;]">0°0′0″N 0°0′0″E</a>
-<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" 
data-zoom="0" data-lat="0" data-lon="0">0°0′0″N 0°0′0″E</a>
-<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" 
data-zoom="0" data-lat="0" data-lon="0">0°0′0″N 0°0′0″E</a>
+<p><a class="mw-kartographer-maplink" mw-data="interface" 
data-style="osm-intl" href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" 
data-lon="0" data-overlays="[&quot;foo&quot;]">0°0′0″N 0°0′0″E</a>
+<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" 
href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" data-lon="0" 
data-overlays="[&quot;foo&quot;,&quot;bar&quot;]">0°0′0″N 0°0′0″E</a>
+<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" 
href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" data-lon="0" 
data-overlays="[&quot;foo&quot;]">0°0′0″N 0°0′0″E</a>
+<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" 
href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" data-lon="0">0°0′0″N 
0°0′0″E</a>
+<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" 
href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" data-lon="0">0°0′0″N 
0°0′0″E</a>
 </p>
 <div class="mw-kartographer-error">&lt;maplink&gt;: Attribute "show" has an 
invalid value</div>
 
diff --git a/tests/phpunit/SpecialMapTest.php b/tests/phpunit/SpecialMapTest.php
new file mode 100644
index 0000000..4f2c79a
--- /dev/null
+++ b/tests/phpunit/SpecialMapTest.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace Kartographer\Tests;
+
+use GeoData\Globe;
+use Kartographer\SpecialMap;
+use MediaWikiTestCase;
+use Title;
+
+/**
+ * @group Kartographer
+ */
+class SpecialMapTest extends MediaWikiTestCase {
+
+       /**
+        * @dataProvider provideParseSubpage
+        *
+        * @param string $par
+        * @param float $expectedLat
+        * @param float $expectedLon
+        */
+       public function testParseSubpage( $par, $expectedLat = null, 
$expectedLon = null ) {
+               $res = SpecialMap::parseSubpage( $par );
+               if ( $expectedLat === null || $expectedLon === null ) {
+                       $this->assertFalse( $res, 'Parsing is expected to fail' 
);
+               } else {
+                       list( , $lat, $lon ) = $res;
+                       $this->assertSame( $expectedLat, $lat, 'Comparing 
latitudes' );
+                       $this->assertSame( $expectedLon, $lon, 'Comparing 
longitudes' );
+               }
+       }
+
+       public function provideParseSubpage() {
+               $tests = [
+                       [ '' ],
+                       [ 'foo' ],
+                       [ 'foo/bar/baz' ],
+                       [ '123' ],
+                       [ '1/2' ],
+                       [ '1/2/3/4' ],
+                       [ '1.0/2/3' ],
+                       [ '-1/2/3' ],
+                       [ '1/2/.3' ],
+                       [ '1/2/3.45e+6' ],
+                       [ '1/2,3/4,5' ],
+
+                       [ '0/0/-0', 0.0, 0.0 ],
+                       [ '12/-34.56/0.78', -34.56, 0.78 ],
+                       [ '18/89.9/179.9', 89.9, 179.9 ],
+                       [ '18/-89.9/-179.9', -89.9, -179.9 ],
+                       [ '18/90/-180', 90.0, -180.0 ],
+               ];
+
+               if ( class_exists( Globe::class ) ) {
+                       $tests = array_merge( $tests,
+                               [
+                                       [ '0/90.000001/10' ],
+                                       [ '0/10/180.000000001' ],
+                               ]
+                       );
+               }
+
+               return $tests;
+       }
+
+       public function testLink() {
+               $this->setMwGlobals( 'wgArticlePath', '/wiki/$1' );
+               $title = SpecialMap::link( 12, -34.5, 6 );
+               $this->assertType( Title::class, $title );
+               $this->assertEquals( '/wiki/Special:Map/6/12/-34.5', 
$title->getLocalURL() );
+       }
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/306315
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I90fcd2fb0e458c9d5a805b441f5114157ea470a4
Gerrit-PatchSet: 9
Gerrit-Project: mediawiki/extensions/Kartographer
Gerrit-Branch: master
Gerrit-Owner: MaxSem <maxsem.w...@gmail.com>
Gerrit-Reviewer: JGirault <julien.inbox.w...@gmail.com>
Gerrit-Reviewer: MaxSem <maxsem.w...@gmail.com>
Gerrit-Reviewer: Yurik <yu...@wikimedia.org>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to