MaxSem has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/295317

Change subject: WIP: Wikivoyage maps
......................................................................

WIP: Wikivoyage maps

Change-Id: Ib6f92eaedddb453fe6346d6f2e55e9de6d78f278
---
A modules/wikivoyage/controls/layers.js
A modules/wikivoyage/controls/nearby.js
A modules/wikivoyage/controls/scale.js
A modules/wikivoyage/data/en-articles.js
A modules/wikivoyage/data/maptiles.json
A modules/wikivoyage/i18n/en.json
A modules/wikivoyage/images/nearby.svg
A modules/wikivoyage/index.js
A modules/wikivoyage/map/control-layers.js
A modules/wikivoyage/map/map.js
A modules/wikivoyage/mw.message.js
A modules/wikivoyage/mw.wikivoyage.js
A modules/wikivoyage/styles/wikivoyage.css
13 files changed, 807 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Kartographer 
refs/changes/17/295317/1

diff --git a/modules/wikivoyage/controls/layers.js 
b/modules/wikivoyage/controls/layers.js
new file mode 100644
index 0000000..d6a2340
--- /dev/null
+++ b/modules/wikivoyage/controls/layers.js
@@ -0,0 +1,46 @@
+import L from 'leaflet';
+import wikivoyage from '../mw.wikivoyage';
+
+/*jscs:disable disallowDanglingUnderscores, requireVarDeclFirst */
+var ControlLayers = L.Control.Layers.extend( {
+
+       _addItem: function ( obj ) {
+               var label = L.Control.Layers.prototype._addItem.call( this, obj 
);
+               if ( !obj.overlay && label.childNodes[ 0 ].checked ) {
+                       this._previousSelected = label.childNodes[ 0 ];
+               }
+       },
+
+       _onInputClick: function ( event ) {
+               var self = this,
+                       proto = L.Control.Layers.prototype._onInputClick,
+                       input = event && event.target,
+                       obj;
+
+               if ( input &&
+                       event.type === 'click' &&
+                       /leaflet-control-layers-selector/.test( input.className 
) ) {
+
+                       obj = this._layers[ input.layerId ];
+                       if ( this._map.hasLayer( obj.layer ) ) {
+                               proto.call( self );
+                       } else {
+                               event.stopPropagation();
+                               if ( !obj.overlay && this._previousSelected ) {
+                                       this._previousSelected.checked = true;
+                               }
+                               input.checked = false;
+                               this._expand();
+                               wikivoyage.isAllowed( obj.layer )
+                                       .done( function () {
+                                               input.checked = true;
+                                               proto.call( self );
+                                       } );
+                       }
+               } else {
+                       proto.call( this );
+               }
+       }
+} );
+
+export default ControlLayers;
diff --git a/modules/wikivoyage/controls/nearby.js 
b/modules/wikivoyage/controls/nearby.js
new file mode 100644
index 0000000..b265c6d
--- /dev/null
+++ b/modules/wikivoyage/controls/nearby.js
@@ -0,0 +1,105 @@
+import L from 'leaflet';
+import $ from 'jquery';
+import wikivoyage from '../mw.wikivoyage';
+import getArticles from '../data/en-articles';
+
+function mousepopup( marker, data ) {
+       marker.bindPopup( data.title, { minWidth: 120, maxWidth: 120 } );
+       marker.on( 'click', function ( e ) {
+               this.openPopup();
+       } );
+}
+
+/*jscs:disable disallowDanglingUnderscores, requireVarDeclFirst */
+var ControlNearby = L.Control.extend( {
+       options: {
+               // Do not switch for RTL because zoom also stays in place
+               position: 'topleft'
+       },
+
+       onAdd: function ( map ) {
+               var container = L.DomUtil.create( 'div', 'leaflet-bar' ),
+                       link = L.DomUtil.create( 'a', 
'mw-kartographer-icon-nearby', container ),
+                       pruneCluster = new PruneClusterForLeaflet( 70 ),
+                       control = this;
+
+               link.href = '#';
+               link.title = mw.msg( 'kartographer-wv-nearby-articles-control' 
);
+               pruneCluster.options = {
+                       wvIsOverlay: true,
+                       wvIsExternal: true,
+                       wvName: 'nearby-articles'
+               };
+
+               this.map = map;
+               this.link = link;
+               this.pruneCluster = pruneCluster;
+
+               L.DomEvent.addListener( link, 'click', 
this._onToggleNearbyLayer, this );
+               L.DomEvent.disableClickPropagation( container );
+
+               map.on( 'overlayadd', this._onOverlayAdd, this );
+               map.on( 'overlayremove', this._onOverlayRemove, this );
+
+               return container;
+       },
+
+       _onOverlayAdd: function ( obj ) {
+               var pruneCluster = this.pruneCluster;
+               if ( pruneCluster !== obj.layer ) {
+                       return;
+               }
+               // Zoom out to get a better picture of the markers nearby.
+               if ( this.map.getZoom() >= 12 ) {
+                       this.map.setZoom( 10 );
+               }
+               this._toggleActiveClass( true );
+               if ( pruneCluster._objectsOnMap.length > 0 ) {
+                       return;
+               }
+               getArticles().done( function ( addressPoints ) {
+                       var nr = addressPoints.length,
+                               a, jslang = 'en',
+                               tp = 
'//upload.wikimedia.org/wikipedia/commons/thumb/', // thumbnail path
+                               ap = '//' + jslang + '.wikivoyage.org/wiki/', 
// WV article path
+                               i = 0;
+
+                       for ( i = 0; i < nr; i++ ) {
+                               a = addressPoints[ i ];
+                               pruneCluster.RegisterMarker( new 
PruneCluster.Marker( a[ 0 ], a[ 1 ], { title: '<img src="' + tp + a[ 3 ] + 
'/120px-' + a[ 3 ].substring( 5 ) + '"> <a href="' + ap + a[ 2 ] + '" 
target="_blank">' + a[ 2 ] + '</a>' } ) );
+                               pruneCluster.PrepareLeafletMarker = mousepopup;
+                       }
+                       pruneCluster.ProcessView();
+               } );
+       },
+
+       _onOverlayRemove: function ( obj ) {
+               if ( this.pruneCluster !== obj.layer ) {
+                       return;
+               }
+               this._toggleActiveClass( false );
+       },
+
+       _toggleActiveClass: function ( enabled ) {
+               enabled = ( enabled !== undefined ) ? enabled : 
this.map.hasLayer( this.pruneCluster );
+               $( this.link ).toggleClass( 'mapbox-icon-nearby-active', 
enabled );
+       },
+
+       _onToggleNearbyLayer: function ( e ) {
+               var control = this,
+                       enabled = this.map.hasLayer( this.pruneCluster );
+
+               L.DomEvent.stop( e );
+
+               if ( !enabled ) {
+                       wikivoyage.isAllowed( this.pruneCluster )
+                               .done( function () {
+                                       control.map.addLayer( 
control.pruneCluster );
+                               } );
+               } else {
+                       this.map[ enabled ? 'removeLayer' : 'addLayer' ]( 
this.pruneCluster );
+               }
+       }
+} );
+
+export default ControlNearby;
diff --git a/modules/wikivoyage/controls/scale.js 
b/modules/wikivoyage/controls/scale.js
new file mode 100644
index 0000000..2d94c88
--- /dev/null
+++ b/modules/wikivoyage/controls/scale.js
@@ -0,0 +1,38 @@
+import L from 'leaflet';
+
+/*jscs:disable disallowDanglingUnderscores, requireVarDeclFirst */
+var ControlScale = L.Control.Scale.extend( {
+
+       isMetric: true,
+
+       _updateScales: function ( options, maxMeters ) {
+
+               L.Control.Scale.prototype._updateScales.call( this, options, 
maxMeters );
+
+               this._toggleScale();
+       },
+
+       _addScales: function ( options, className, container ) {
+               L.Control.Scale.prototype._addScales.call( this, options, 
className, container );
+
+               if ( options.metric && options.imperial ) {
+                       L.DomEvent.addListener( this._mScale, 'click', 
this._onToggleScale, this );
+                       L.DomEvent.addListener( this._iScale, 'click', 
this._onToggleScale, this );
+                       L.DomEvent.disableClickPropagation( container );
+               }
+       },
+
+       _toggleScale: function () {
+               if ( this.options.metric && this.options.imperial ) {
+                       this._mScale.style.display = this.isMetric ? 'block' : 
'none';
+                       this._iScale.style.display = !this.isMetric ? 'block' : 
'none';
+               }
+       },
+
+       _onToggleScale: function () {
+               this.isMetric = !this.isMetric;
+               this._toggleScale();
+       }
+} );
+
+export default ControlScale;
diff --git a/modules/wikivoyage/data/en-articles.js 
b/modules/wikivoyage/data/en-articles.js
new file mode 100644
index 0000000..984c307
--- /dev/null
+++ b/modules/wikivoyage/data/en-articles.js
@@ -0,0 +1,22 @@
+var fetchArticlesDeferred,
+       data;
+
+export default function () {
+       if ( fetchArticlesDeferred ) {
+               return fetchArticlesDeferred;
+       } else {
+               fetchArticlesDeferred = $.Deferred();
+       }
+
+       if ( !data ) {
+               // fetch
+               $.getScript( 
'https://tools.wmflabs.org/wikivoyage/w/data/en-articles.js' )
+                       .done( function ( script, textStatus ) {
+                               data = window.addressPoints;
+                               fetchArticlesDeferred.resolve( data ).promise();
+                       } );
+       } else {
+               fetchArticlesDeferred.resolve( data );
+       }
+       return fetchArticlesDeferred.promise();
+}
diff --git a/modules/wikivoyage/data/maptiles.json 
b/modules/wikivoyage/data/maptiles.json
new file mode 100644
index 0000000..5b9823a
--- /dev/null
+++ b/modules/wikivoyage/data/maptiles.json
@@ -0,0 +1,177 @@
+{
+       "mapnik": {
+               "tilesUrl": "//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
+               "options": {
+                       "wvIsExternal": true,
+                       "wvName": "Mapnik",
+                       "subdomains": [
+                               "a",
+                               "b",
+                               "c"
+                       ],
+                       "attribs": [
+                               {
+                                       "url": 
"http://www.openstreetmap.org/copyright";,
+                                       "name": "OpenStreetMap",
+                                       "label": "Map data"
+                               }
+                       ]
+               }
+       },
+       "mapquestopen": {
+               "tilesUrl": 
"https://{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png";,
+               "options": {
+                       "wvIsExternal": true,
+                       "wvName": "Mapquest open",
+                       "subdomains": [
+                               "otile1-s",
+                               "otile2-s",
+                               "otile3-s",
+                               "otile4-s"
+                       ],
+                       "attribs": [
+                               {
+                                       "url": 
"http://www.openstreetmap.org/copyright";,
+                                       "name": "OpenStreetMap",
+                                       "label": "Map data"
+                               },
+                               {
+                                       "url": "http://open.mapquest.co.uk";,
+                                       "name": "Mapquest",
+                                       "label": "Tiles"
+                               }
+                       ]
+               }
+       },
+       "mapquest": {
+               "tilesUrl": 
"https://{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg";,
+               "options": {
+                       "wvIsExternal": true,
+                       "wvName": "Mapquest",
+                       "subdomains": [
+                               "otile1-s",
+                               "otile2-s",
+                               "otile3-s",
+                               "otile4-s"
+                       ],
+                       "attribs": [
+                               {
+                                       "url": "http://open.mapquest.co.uk";,
+                                       "name": "Mapquest",
+                                       "label": "Data, imagery and map 
information provided by"
+                               }
+                       ]
+               }
+       },
+       "landscape": {
+               "tilesUrl": 
"http://{s}.tile.thunderforest.com/landscape/{z}/{x}/{y}.png";,
+               "options": {
+                       "wvIsExternal": true,
+                       "wvName": "Relief map",
+                       "attribs": [
+                               {
+                                       "url": 
"http://www.openstreetmap.org/copyright";,
+                                       "name": "OpenStreetMap",
+                                       "label": "Map data"
+                               },
+                               {
+                                       "url": "http://www.opencyclemap.org/";,
+                                       "name": "Andy Allan",
+                                       "label": "Tiles"
+                               }
+                       ]
+               }
+       },
+       "traffic": {
+               "tilesUrl": "http://www.openptmap.org/tiles/{z}/{x}/{y}.png";,
+               "options": {
+                       "wvIsOverlay": true,
+                       "wvIsExternal": true,
+                       "wvName": "Traffic line network",
+                       "attribs": [
+                               {
+                                       "url": "http://openptmap.org/";,
+                                       "name": "Openptmap.org",
+                                       "label": "Traffic lines"
+                               }
+                       ],
+                       "opacity": 0.5,
+                       "maxNativeZoom": 17
+               }
+       },
+       "maplabels": {
+               "tilesUrl": 
"https://{s}.mqcdn.com/tiles/1.0.0/hyb/{z}/{x}/{y}.png";,
+               "options": {
+                       "wvIsOverlay": true,
+                       "wvIsExternal": true,
+                       "wvName": "Mapquest (labels)",
+                       "subdomains": [
+                               "otile1-s",
+                               "otile2-s",
+                               "otile3-s",
+                               "otile4-s"
+                       ],
+                       "attribs": [
+                               {
+                                       "url": "http://open.mapquest.co.uk";,
+                                       "name": "Mapquest",
+                                       "label": "Data, imagery and map 
information provided by"
+                               }
+                       ]
+               }
+       },
+       "boundaries": {
+               "tilesUrl": 
"http://korona.geog.uni-heidelberg.de/tiles/adminb/x={x}&y={y}&z={z}";,
+               "options": {
+                       "wvIsOverlay": true,
+                       "wvIsExternal": true,
+                       "wvName": "Boundaries",
+                       "attribs": []
+               }
+       },
+       "cycling": {
+               "tilesUrl": "http://tile.lonvia.de/cycling/{z}/{x}/{y}.png";,
+               "options": {
+                       "wvIsOverlay": true,
+                       "wvIsExternal": true,
+                       "wvName": "Cycling",
+                       "attribs": [
+                               {
+                                       "url": "http://cycling.lonvia.de";,
+                                       "name": "Cycling Map",
+                                       "label": "Cycling routes"
+                               }
+                       ]
+               }
+       },
+       "hiking": {
+               "tilesUrl": 
"http://tile.waymarkedtrails.org/hiking/{z}/{x}/{y}.png";,
+               "options": {
+                       "wvIsOverlay": true,
+                       "wvIsExternal": true,
+                       "wvName": "Hiking",
+                       "attribs": [
+                               {
+                                       "url": 
"http://hiking.waymarkedtrails.org/de/";,
+                                       "name": "Hiking Map",
+                                       "label": "Hiking trails"
+                               }
+                       ]
+               }
+       },
+       "hill": {
+               "tilesUrl": 
"http://{s}.tiles.wmflabs.org/hillshading/{z}/{x}/{y}.png";,
+               "options": {
+                       "wvIsOverlay": true,
+                       "wvIsExternal": true,
+                       "wvName": "Hill shading",
+                       "attribs": [
+                               {
+                                       "url": "http://www2.jpl.nasa.gov/srtm/";,
+                                       "name": "NASA",
+                                       "label": "Hill shading"
+                               }
+                       ]
+               }
+       }
+}
diff --git a/modules/wikivoyage/i18n/en.json b/modules/wikivoyage/i18n/en.json
new file mode 100644
index 0000000..d1d9ecf
--- /dev/null
+++ b/modules/wikivoyage/i18n/en.json
@@ -0,0 +1,20 @@
+{
+       "kartographer-wv-group": "Group: ",
+       "kartographer-wv-Mapnik": "Mapnik",
+       "kartographer-wv-Hiking": "Hiking",
+       "kartographer-wv-Wikimedia": "Wikimedia",
+       "kartographer-wv-nearby-articles": "Nearby articles",
+       "kartographer-wv-Mapquest open": "Mapquest open",
+       "kartographer-wv-Mapquest": "Mapquest",
+       "kartographer-wv-Relief map": "Relief map",
+       "kartographer-wv-Traffic line network": "Traffic line network",
+       "kartographer-wv-Mapquest (labels)": "Mapquest (labels)",
+       "kartographer-wv-Boundaries": "Boundaries",
+       "kartographer-wv-Hill shading": "Hill shading",
+       "kartographer-wv-Cycling": "Cycling",
+       "kartographer-wv-warning-external-source-title": "External data source",
+       "kartographer-wv-warning-external-source-message": "This content is 
hosted externally, so enabling it shares your data with other sites.",
+       "kartographer-wv-warning-external-source-agree": "It's okay",
+       "kartographer-wv-warning-external-source-disagree": "Cancel",
+       "kartographer-wv-nearby-articles-control": "Explore nearby destinations"
+}
diff --git a/modules/wikivoyage/images/nearby.svg 
b/modules/wikivoyage/images/nearby.svg
new file mode 100644
index 0000000..4e32fe3
--- /dev/null
+++ b/modules/wikivoyage/images/nearby.svg
@@ -0,0 +1,9 @@
+<svg width="24px" height="24px" viewBox="-3195 -3703 24 24" version="1.1" 
xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink";>
+    <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" 
transform="translate(-3194.000000, -3692.000000)">
+        <path d="M12.0543874,8.84786092 C16.7188107,8.84786092 
20.5000728,7.08703806 20.5000728,4.91495298 C20.5000728,2.74286791 
16.7188107,0.982045042 12.0543874,0.982045042 C7.38996415,0.982045042 
3.60870199,2.74286791 3.60870199,4.91495298 C3.60870199,7.08703806 
7.38996415,8.84786092 12.0543874,8.84786092 Z M11.7850466,11.0592528 
C17.1891786,11.0592528 21.5700932,8.58355477 21.5700932,5.52962642 
C21.5700932,2.47569808 17.1891786,0 11.7850466,0 C6.38091459,0 2,2.47569808 
2,5.52962642 C2,8.58355477 6.38091459,11.0592528 11.7850466,11.0592528 Z" 
fill="#000000"></path>
+        <ellipse stroke="#FFFFFF" stroke-width="1.4" fill="#000000" 
cx="15.9394612" cy="9.58434489" rx="2.93946123" ry="2.58434489"></ellipse>
+        <path d="M2.5,8.18444824 C3.88071187,8.18444824 5,7.2477276 
5,6.09222412 C5,4.93672065 3.88071187,4 2.5,4 C1.11928813,4 0,4.93672065 
0,6.09222412 C0,7.2477276 1.11928813,8.18444824 2.5,8.18444824 Z" 
stroke="#FFFFFF" stroke-width="1.3" fill="#000000"></path>
+        <path d="M19.375,4.65244141 C20.6866763,4.65244141 21.75,3.75701091 
21.75,2.65244141 C21.75,1.54787191 20.6866763,0.652441406 19.375,0.652441406 
C18.0633237,0.652441406 17,1.54787191 17,2.65244141 C17,3.75701091 
18.0633237,4.65244141 19.375,4.65244141 Z" stroke="#FFFFFF" 
fill="#000000"></path>
+    </g>
+    <path d="M-3181.5562,-3703 C-3184.56299,-3703 -3187,-3700.25757 
-3187,-3696.87395 C-3187,-3693.49032 -3183.97506,-3688.70587 
-3181.5562,-3687.34453 C-3179.13613,-3688.70587 -3176.1124,-3693.49032 
-3176.1124,-3696.87395 C-3176.1124,-3700.25757 -3178.54941,-3703 
-3181.5562,-3703 L-3181.5562,-3703 Z M-3181.5562,-3694.49159 
C-3183.05869,-3694.49159 -3184.2781,-3695.86247 -3184.2781,-3697.55462 
C-3184.2781,-3699.24541 -3183.05869,-3700.61765 -3181.5562,-3700.61765 
C-3180.05311,-3700.61765 -3178.8343,-3699.24541 -3178.8343,-3697.55462 
C-3178.8343,-3695.86247 -3180.05311,-3694.49159 -3181.5562,-3694.49159 
L-3181.5562,-3694.49159 Z" stroke="none" fill="#000000" 
fill-rule="evenodd"></path>
+</svg>
diff --git a/modules/wikivoyage/index.js b/modules/wikivoyage/index.js
new file mode 100644
index 0000000..be45fee
--- /dev/null
+++ b/modules/wikivoyage/index.js
@@ -0,0 +1,47 @@
+import $ from 'jquery';
+import mw from 'mediawiki';
+import wikivoyage from './mw.wikivoyage.js';
+import maptiles from './data/maptiles.json';
+
+mw.wikivoyage = wikivoyage;
+
+function wikivoyageMe( map, isFullScreen ) {
+
+       var wvmap = wikivoyage.map( map );
+
+       // wvmap.controlLayers()
+       //      .basemap( 'mapnik' )
+       //      .basemap( 'mapquestopen' )
+       //      .basemap( 'mapquest' )
+       //      .basemap( 'landscape' )
+       //      .overlay( 'traffic' )
+       //      .overlay( 'maplabels' )
+       //      .overlay( 'boundaries' )
+       //      .overlay( 'hill' )
+       //      .overlay( 'cycling' )
+       //      .overlay( 'hiking' )
+       //      .datalayer( this.map.dataLayers )
+       //      .update();
+
+       wvmap.controlLayers().addAll();
+       wvmap.scale();
+       wvmap.nearby();
+
+       return wvmap;
+}
+
+mw.hook( 'wikipage.maps' ).add( function ( map, isFullScreen ) {
+
+       $.each( maptiles, function ( i, tile ) {
+               wikivoyage.addTileLayer( i, tile.tilesUrl, tile.options );
+       } );
+
+       // init ?
+       if ( $.isArray( map ) ) {
+               $.each( map, function ( i, m ) {
+                       wikivoyageMe( m, isFullScreen );
+               } )
+       } else {
+               wikivoyageMe( map, isFullScreen );
+       }
+} );
diff --git a/modules/wikivoyage/map/control-layers.js 
b/modules/wikivoyage/map/control-layers.js
new file mode 100644
index 0000000..c9bd9b3
--- /dev/null
+++ b/modules/wikivoyage/map/control-layers.js
@@ -0,0 +1,74 @@
+import $ from 'jquery';
+import ControlLayers from '../controls/layers';
+import wikivoyage from '../mw.wikivoyage';
+import message from '../mw.message';
+
+/*jscs:disable disallowDanglingUnderscores, requireVarDeclFirst */
+var MapControl = function ( map ) {
+       this.map = map;
+       this.control = new ControlLayers();
+       this.control.addTo( this.map );
+
+       // Add wikimedia basemap
+       this.addLayer(
+               this.map.wikimediaLayer,
+               wikivoyage.formatLayerName( message( 'Wikimedia' ), { wvIsWMF: 
true } )
+       );
+};
+
+MapControl.prototype.addLayer = function ( layer, name, overlay ) {
+       this.control._addLayer( layer, name, overlay );
+       return this;
+};
+
+MapControl.prototype.addAll = function () {
+       this
+               .basemap( 'mapnik' )
+               .basemap( 'mapquestopen' )
+               .basemap( 'mapquest' )
+               .basemap( 'landscape' )
+               .overlay( 'traffic' )
+               .overlay( 'maplabels' )
+               .overlay( 'boundaries' )
+               .overlay( 'hill' )
+               .overlay( 'cycling' )
+               .overlay( 'hiking' )
+               .datalayer( this.map.dataLayers )
+               .update();
+       return this;
+};
+
+MapControl.prototype.update = function () {
+       this.control._update();
+       return this;
+};
+
+MapControl.prototype.basemap = function ( id ) {
+       var tileLayer = wikivoyage.createTileLayer( id );
+       this.addLayer( tileLayer.layer, tileLayer.name );
+       return this;
+};
+
+MapControl.prototype.overlay = function ( id ) {
+       var tileLayer = wikivoyage.createTileLayer( id );
+       this.addLayer( tileLayer.layer, tileLayer.name, true );
+       return this;
+};
+
+MapControl.prototype.datalayer = function ( id, layer ) {
+       if ( typeof id === 'object' ) {
+               var self = this;
+               $.each( id, function ( group, groupLayer ) {
+                       self.datalayer( group, groupLayer );
+               } );
+               return this;
+       }
+       this.addLayer(
+               layer,
+               wikivoyage.formatLayerName( message( 'group' ) + id ),
+               true
+       );
+       return this;
+};
+
+export default MapControl;
diff --git a/modules/wikivoyage/map/map.js b/modules/wikivoyage/map/map.js
new file mode 100644
index 0000000..783e0b8
--- /dev/null
+++ b/modules/wikivoyage/map/map.js
@@ -0,0 +1,41 @@
+import L from 'leaflet';
+import ControlLayers from './control-layers.js';
+import ControlNearby from '../controls/nearby';
+import ControlScale from '../controls/scale';
+import wikivoyage from '../mw.wikivoyage';
+import message from '../mw.message';
+
+/*jscs:disable disallowDanglingUnderscores, requireVarDeclFirst */
+var Map = function ( map ) {
+       this.map = map;
+};
+
+Map.prototype.nearby = function () {
+       var control = this._controlNearby;
+       if ( control ) {
+               return control;
+       }
+
+       control = this._controlNearby = new ControlNearby();
+       control.addTo( this.map );
+
+       this.controlLayers().addLayer(
+               control.pruneCluster,
+               wikivoyage.formatLayerName( message( 
control.pruneCluster.options.wvName ), control.pruneCluster.options ),
+               true
+       ).update();
+
+       return control;
+};
+
+Map.prototype.controlLayers = function () {
+       this._controlLayers = this._controlLayers || new ControlLayers( 
this.map );
+       return this._controlLayers;
+};
+
+Map.prototype.scale = function () {
+       this._controlScale = this._controlScale || new ControlScale( { 
position: 'bottomright' } ).addTo( this.map );
+       return this._controlScale;
+};
+
+export default Map;
diff --git a/modules/wikivoyage/mw.message.js b/modules/wikivoyage/mw.message.js
new file mode 100644
index 0000000..de9d088
--- /dev/null
+++ b/modules/wikivoyage/mw.message.js
@@ -0,0 +1,9 @@
+import mw from 'mediawiki';
+import labels from './i18n/en.json';
+
+mw.messages.set( labels );
+
+export default function ( key, action ) {
+       action = action || 'escaped';
+       return mw.message( 'kartographer-wv-' + key )[ action ]();
+};
diff --git a/modules/wikivoyage/mw.wikivoyage.js 
b/modules/wikivoyage/mw.wikivoyage.js
new file mode 100644
index 0000000..f905ec9
--- /dev/null
+++ b/modules/wikivoyage/mw.wikivoyage.js
@@ -0,0 +1,107 @@
+import WikivoyageMap from './map/map';
+import $ from 'jquery';
+import mw from 'mediawiki';
+import message from './mw.message';
+
+/*jscs:disable requireVarDeclFirst */
+var maps = [],
+       tileLayerDefs = {},
+       areExternalAllowed,
+       windowManager,
+       messageDialog,
+       STORAGE_KEY = 'mwKartographerExternalSources';
+
+mw.loader.using( 'oojs-ui' );
+
+function getWindowManager() {
+       if ( windowManager ) {
+               return windowManager;
+       }
+       messageDialog = new OO.ui.MessageDialog();
+       windowManager = new OO.ui.WindowManager();
+       $( 'body' ).append( windowManager.$element );
+       windowManager.addWindows( [ messageDialog ] );
+       return windowManager;
+}
+
+function alertExternalData() {
+       return getWindowManager().openWindow( messageDialog, {
+               title: message( 'warning-external-source-title', 'text' ),
+               message: message( 'warning-external-source-message', 'text' ),
+               actions: [
+                       { label: message( 'warning-external-source-disagree', 
'text' ), action: 'bad' },
+                       {
+                               label: message( 
'warning-external-source-agree', 'text' ),
+                               action: 'good'
+                       }
+               ]
+       } );
+}
+
+export default {
+       map: function ( map ) {
+               var wikivoyageMap = new WikivoyageMap( map );
+               return wikivoyageMap;
+       },
+
+       addTileLayer: function ( id, url, options ) {
+               options.wvLayerId = id;
+               options.attribution = options.attribution || '';
+               $.each( options.attribs, function ( i, attrib ) {
+                       options.attribution += attrib.label + ' ';
+                       options.attribution += ' <a href="' + attrib.url + '">' 
+ attrib.name + '</a>';
+               } );
+
+               tileLayerDefs[ id.toString() ] = {
+                       url: url,
+                       options: options
+               };
+               return this;
+       },
+
+       createTileLayer: function ( id ) {
+               var layerDefs = tileLayerDefs[ id ];
+               return {
+                       layer: new L.TileLayer( layerDefs.url, 
layerDefs.options ),
+                       name: this.formatLayerName( message( 
layerDefs.options.wvName ), layerDefs.options )
+               };
+       },
+
+       formatLayerName: function ( name, options ) {
+               var icon;
+               options = options || {};
+               if ( options.wvIsExternal ) {
+                       icon = 
'/w/skins/Vector/images/external-link-ltr-icon.png?325de';
+               } else if ( options.wvIsWMF ) {
+                       icon = 
'//upload.wikimedia.org/wikipedia/commons/thumb/8/81/Wikimedia-logo.svg/12px-Wikimedia-logo.svg.png';
+               }
+               return name + ( icon ? ' <img src="' + icon + '" />' : '' );
+       },
+
+       isAllowed: function ( layer ) {
+               var deferred = $.Deferred();
+
+               if ( areExternalAllowed === undefined ) {
+                       areExternalAllowed = mw.storage.get( STORAGE_KEY ) === 
'1';
+               }
+
+               if ( !layer.options.wvIsExternal || areExternalAllowed ) {
+                       deferred.resolve();
+               } else {
+                       alertExternalData()
+                               .then( function ( opened ) {
+                                       opened.then( function ( closing, data ) 
{
+                                               if ( data && data.action && 
data.action === 'good' ) {
+                                                       areExternalAllowed = 
true;
+                                                       mw.storage.set( 
STORAGE_KEY, '1' );
+                                                       deferred.resolve();
+                                               } else {
+                                                       deferred.reject();
+                                               }
+                                       } );
+                               } );
+               }
+
+               return deferred.promise();
+       }
+};
diff --git a/modules/wikivoyage/styles/wikivoyage.css 
b/modules/wikivoyage/styles/wikivoyage.css
new file mode 100644
index 0000000..5c436f5
--- /dev/null
+++ b/modules/wikivoyage/styles/wikivoyage.css
@@ -0,0 +1,112 @@
+/* = Wikivoyage Customization = */
+
+/* == Top Right controls == */
+
+/* Top Right controls won't overlap over left and bottom controls */
+.leaflet-right.leaflet-top {
+       left: 50px;
+       bottom: 20px;
+       overflow: hidden;
+}
+
+/* Expand the dropdown as wide as possible */
+.leaflet-control-layers-expanded {
+       max-height: calc(100% - 50px);
+       max-width: calc(100% - 10px);
+       text-align: left;
+       overflow: scroll;
+}
+
+/* Add ellipsis to long layer labels */
+.leaflet-control-layers-expanded label {
+       text-overflow: ellipsis;
+       white-space: nowrap;
+       overflow-x: hidden;
+}
+
+/* Removes ellipsis on hover */
+.leaflet-control-layers-expanded .leaflet-control-layers-overlays:hover label,
+.leaflet-control-layers-expanded .leaflet-control-layers-base:hover label {
+       overflow-x: visible;
+}
+
+/* == Bottom Right controls == */
+
+/* Bottom right container style */
+.leaflet-right.leaflet-bottom {
+       background-color: rgba(255, 255, 255, 0.6);
+       left: auto;
+       cursor: default;
+       max-width: 100%;
+}
+
+/* Bottom right container style on hover */
+.leaflet-right.leaflet-bottom:hover {
+       background-color: rgba(255, 255, 255, 0.9);
+}
+
+/* === Scale control === */
+
+/** Override initial properties */
+.leaflet-right.leaflet-bottom .leaflet-control.leaflet-control-scale {
+       float: left;
+       clear: none;
+       margin: 1px 5px;
+       line-height: initial;
+}
+
+/* Scale Line style */
+.leaflet-control-scale.leaflet-control .leaflet-control-scale-line {
+       background-color: transparent;
+       padding: 0 5px;
+       font-size: 10px;
+       font-family: sans-serif;
+}
+
+/* Scale Line style on hover */
+.leaflet-right.leaflet-bottom .leaflet-control-scale 
.leaflet-control-scale-line:hover {
+       border-color: black;
+       cursor: pointer;
+}
+
+/** Override initial properties */
+.leaflet-right.leaflet-bottom .leaflet-control-scale 
.leaflet-control-scale-line:not(:first-child) {
+       border: 1px solid #999;
+       border-color: black;
+       border-top: none;
+       margin-top: 0;
+}
+
+/* === Attribution control === */
+
+/** Override initial properties */
+.leaflet-right.leaflet-bottom .leaflet-control.leaflet-control-attribution {
+       float: none;
+       clear: none;
+       font-size: 10px;
+       font-family: sans-serif;
+       line-height: 1.6;
+
+       white-space: nowrap;
+       overflow: hidden;
+       text-overflow: ellipsis;
+}
+
+/* Removes ellipsis on hover */
+.leaflet-right.leaflet-bottom 
.leaflet-control.leaflet-control-attribution:hover {
+       white-space: normal;
+       overflow: hidden;
+       text-overflow: clip;
+}
+
+/* == New icons  == */
+/* New nearby POIs icon */
+.mapbox-icon-nearby-active, .mapbox-icon-nearby-active:hover {
+       background-color: #999999;
+       border-color: #999999;
+}
+
+.leaflet-control-container .leaflet-control .mw-kartographer-icon-nearby {
+       background-image: 
url(https://people.wikimedia.org/~jgirault/maps/icons/nearby.svg);
+       background-size: 20px;
+}

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ib6f92eaedddb453fe6346d6f2e55e9de6d78f278
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Kartographer
Gerrit-Branch: master
Gerrit-Owner: MaxSem <maxsem.w...@gmail.com>

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

Reply via email to