JGirault has uploaded a new change for review.

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

Change subject: Adds a router to Kartographer, fixes back button in fullscreen 
mode, and allows sharing a link that opens a map in fullscreen mode.
......................................................................

Adds a router to Kartographer, fixes back button
in fullscreen mode, and allows sharing a link that
opens a map in fullscreen mode.

This patch includes the Router proposal made in patch:
https://gerrit.wikimedia.org/r/#/c/260950
Ideally mediawiki or OO would support a Router by default.

Bug: T128940
Change-Id: I456a4582a67e31533d51d5817d0f4af57528c35e
---
M extension.json
A lib/oo-router.js
M modules/kartographer.MapDialog.js
M modules/kartographer.js
4 files changed, 262 insertions(+), 6 deletions(-)


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

diff --git a/extension.json b/extension.json
index e161a40..af7bd6a 100644
--- a/extension.json
+++ b/extension.json
@@ -182,6 +182,18 @@
                                "mobile",
                                "desktop"
                        ]
+               },
+               "oo-router": {
+                       "scripts": [
+                               "lib/oo-router.js"
+                       ],
+                       "targets": [
+                               "mobile",
+                               "desktop"
+                       ],
+                       "dependencies": [
+                               "oojs"
+                       ]
                }
        },
        "ResourceFileModulePaths": {
diff --git a/lib/oo-router.js b/lib/oo-router.js
new file mode 100644
index 0000000..ffb10ce
--- /dev/null
+++ b/lib/oo-router.js
@@ -0,0 +1,177 @@
+OO.Router = ( function ( $ ) {
+       /**
+        * Does hash match entry.path?
+        *
+        * @method
+        * @private
+        * @ignore
+        * @param {string} hash string to match
+        * @param {Object} entry Entry object
+        * @return {boolean} Whether hash matches entry.path
+        */
+       function matchRoute( hash, entry ) {
+               var match = hash.match( entry.path );
+               if ( match ) {
+                       entry.callback.apply( this, match.slice( 1 ) );
+                       return true;
+               }
+               return false;
+       }
+
+       /**
+        * Provides navigation routing and location information
+        *
+        * @class Router
+        * @mixins OO.EventEmitter
+        */
+       function Router() {
+               var self = this;
+               OO.EventEmitter.call( this );
+               // use an object instead of an array for routes so that we don't
+               // duplicate entries that already exist
+               this.routes = {};
+               this.enabled = true;
+               this.oldHash = this.getPath();
+
+               $( window ).on( 'popstate', function () {
+                       self.emit( 'popstate' );
+               } );
+
+               $( window ).on( 'hashchange', function () {
+                       self.emit( 'hashchange' );
+               } );
+
+               this.on( 'hashchange', function () {
+                       // ev.originalEvent.newURL is undefined on Android 2.x
+                       var routeEv;
+
+                       if ( self.enabled ) {
+                               routeEv = $.Event( 'route', {
+                                       path: self.getPath()
+                               } );
+                               self.emit( 'route', routeEv );
+
+                               if ( !routeEv.isDefaultPrevented() ) {
+                                       self.checkRoute();
+                               } else {
+                                       // if route was prevented, ignore the 
next hash change and revert the
+                                       // hash to its old value
+                                       self.enabled = false;
+                                       self.navigate( self.oldHash );
+                               }
+                       } else {
+                               self.enabled = true;
+                       }
+
+                       self.oldHash = self.getPath();
+               } );
+       }
+       OO.mixinClass( Router, OO.EventEmitter );
+
+       /**
+        * Check the current route and run appropriate callback if it matches.
+        *
+        * @method
+        */
+       Router.prototype.checkRoute = function () {
+               var hash = this.getPath();
+
+               $.each( this.routes, function ( id, entry ) {
+                       return !matchRoute( hash, entry );
+               } );
+       };
+
+       /**
+        * Bind a specific callback to a hash-based route, e.g.
+        *
+        *     @example
+        *     route( 'alert', function () { alert( 'something' ); } );
+        *     route( /hi-(.*)/, function ( name ) { alert( 'Hi ' + name ) } );
+        *
+        * @method
+        * @param {Object} path string or RegExp to match.
+        * @param {Function} callback Callback to be run when hash changes to 
one
+        * that matches.
+        */
+       Router.prototype.route = function ( path, callback ) {
+               var entry = {
+                       path: typeof path === 'string' ? new RegExp( '^' + path 
+ '$' ) : path,
+                       callback: callback
+               };
+               this.routes[ entry.path ] = entry;
+               matchRoute( this.getPath(), entry );
+       };
+
+       /**
+        * Navigate to a specific route. This is only a wrapper for changing the
+        * hash now.
+        *
+        * @method
+        * @param {string} path string with a route (hash without #).
+        */
+       Router.prototype.navigate = function ( path ) {
+               window.location.hash = path;
+       };
+
+       /**
+        * Triggers back on the window
+        */
+       Router.prototype.goBack = function () {
+               window.history.back();
+       };
+
+       /**
+        * Navigate to the previous route. This is a wrapper for 
window.history.back
+        *
+        * @method
+        * @return {jQuery.Deferred}
+        */
+       Router.prototype.back = function () {
+               var deferredRequest = $.Deferred(),
+                       self = this,
+                       timeoutID;
+
+               this.once( 'popstate', function () {
+                       clearTimeout( timeoutID );
+                       deferredRequest.resolve();
+               } );
+
+               this.goBack();
+
+               // If for some reason (old browser, bug in IE/windows 8.1, etc) 
popstate doesn't fire,
+               // resolve manually. Since we don't know for sure which 
browsers besides IE10/11 have
+               // this problem, it's better to fall back this way rather than 
singling out browsers
+               // and resolving the deferred request for them individually.
+               // See 
https://connect.microsoft.com/IE/feedback/details/793618/history-back-popstate-not-working-as-expected-in-webview-control
+               // Give browser a few ms to update its history.
+               timeoutID = setTimeout( function () {
+                       self.off( 'popstate' );
+                       deferredRequest.resolve();
+               }, 50 );
+
+               return deferredRequest;
+       };
+
+       /**
+        * Get current path (hash).
+        *
+        * @method
+        * @return {string} Current path.
+        */
+       Router.prototype.getPath = function () {
+               return window.location.hash.slice( 1 );
+       };
+
+       /**
+        * Determine if current browser supports onhashchange event
+        *
+        * @method
+        * @return {boolean}
+        */
+       Router.prototype.isSupported = function () {
+               return 'onhashchange' in window;
+       };
+
+       return new Router();
+
+}( jQuery ) );
diff --git a/modules/kartographer.MapDialog.js 
b/modules/kartographer.MapDialog.js
index fd69c1c..0233fdd 100644
--- a/modules/kartographer.MapDialog.js
+++ b/modules/kartographer.MapDialog.js
@@ -27,6 +27,22 @@
        this.$body.append( this.$map );
 };
 
+/**
+ * Changes the map within the map dialog.
+ *
+ * @param {Object} data The data for the new map.
+ */
+mw.kartographer.MapDialog.prototype.changeMap = function ( data ) {
+
+       $( '.mw-kartographer-mapDialog-map' ).remove();
+
+       this.$map = $( '<div>' ).addClass( 'mw-kartographer-mapDialog-map' );
+       this.map = null;
+
+       this.$body.append( this.$map );
+       this.setup.call( this, data );
+};
+
 mw.kartographer.MapDialog.prototype.getSetupProcess = function ( data ) {
        return mw.kartographer.MapDialog.super.prototype.getSetupProcess.call( 
this, data )
                .next( function () {
diff --git a/modules/kartographer.js b/modules/kartographer.js
index 40f2f18..6ad426e 100644
--- a/modules/kartographer.js
+++ b/modules/kartographer.js
@@ -35,6 +35,13 @@
 
        mw.kartographer = {};
 
+       /**
+        * References the map containers of the page.
+        *
+        * @type {Array}
+        */
+       mw.kartographer.maps = [];
+
        mw.kartographer.FullScreenControl = L.Control.extend( {
                options: {
                        // Do not switch for RTL because zoom also stays in 
place
@@ -45,7 +52,7 @@
                        var container = L.DomUtil.create( 'div', 
'leaflet-control-mapbox-share leaflet-bar' ),
                                link = L.DomUtil.create( 'a', 'mapbox-share 
mapbox-icon mapbox-icon-share', container );
 
-                       link.href = '#';
+                       link.href = '#' + this.options.fullScreenRoute;
                        link.title = mw.msg( 'kartographer-fullscreen-text' );
                        this.map = map;
 
@@ -57,7 +64,12 @@
 
                onShowFullScreen: function ( e ) {
                        L.DomEvent.stop( e );
-                       mw.kartographer.openFullscreenMap( 
this.options.mapPositionData, this.map );
+
+                       if ( OO && OO.Router && OO.Router.isSupported() ) {
+                               OO.Router.navigate( '#' + 
this.options.fullScreenRoute );
+                       } else {
+                               mw.kartographer.openFullscreenMap( 
this.options.mapPositionData, this.map );
+                       }
                }
        } );
 
@@ -92,8 +104,11 @@
                map.setView( [ data.latitude, data.longitude ], data.zoom );
                map.attributionControl.setPrefix( '' );
 
-               if ( data.enableFullScreenButton ) {
-                       map.addControl( new mw.kartographer.FullScreenControl( 
{ mapPositionData: data } ) );
+               if ( data.enableFullScreenButton && data.hasOwnProperty( 
'articleMapId' ) ) {
+                       map.addControl( new mw.kartographer.FullScreenControl( {
+                               mapPositionData: data,
+                               fullScreenRoute: 'map/' + data.articleMapId
+                       } ) );
                }
 
                L.tileLayer( mapServer + '/' + style + urlFormat, {
@@ -211,12 +226,21 @@
                        }
                        // full screen map should never show "full screen" 
button
                        data.enableFullScreenButton = false;
+                       if ( mapDialog ) {
+                               mapDialog.changeMap( data );
+                               return;
+                       }
                        getWindowManager()
                                .openWindow( mapDialog, data )
                                .then( function ( opened ) { return opened; } )
                                .then( function ( closing ) {
                                        if ( map ) {
                                                map.setView( 
mapDialog.map.getCenter(), mapDialog.map.getZoom() );
+                                       }
+                                       windowManager = mapDialog = undefined;
+
+                                       if ( OO && OO.Router && 
OO.Router.isSupported() ) {
+                                               OO.Router.navigate( '' );
                                        }
                                        return closing;
                                } );
@@ -312,22 +336,49 @@
                        sleepOpacity: 1
                } );
 
-               $content.find( '.mw-kartographer-interactive' ).each( function 
() {
+               $content.find( '.mw-kartographer-interactive' ).each( function 
( index ) {
                        var map,
                                $this = $( this ),
                                data = getMapProps( $this );
 
                        if ( data ) {
+                               data.articleMapId = index;
                                data.enableFullScreenButton = true;
                                map = mw.kartographer.createMap( this, data );
+
                                map.doubleClickZoom.disable();
+
+                               mw.kartographer.maps[ index ] = $this;
 
                                mw.hook( 'wikipage.maps' ).fire( map, false /* 
isFullScreen */ );
 
                                $this.on( 'dblclick', function () {
-                                       mw.kartographer.openFullscreenMap( 
data, map );
+                                       if ( OO && OO.Router && 
OO.Router.isSupported() ) {
+                                               OO.Router.navigate( '#map/' + 
data.articleMapId );
+                                       } else {
+                                               
mw.kartographer.openFullscreenMap( data, map );
+                                       }
                                } );
                        }
                } );
+
+               mw.loader.using( 'oo-router' ).done( function () {
+
+                       // Opens map in full screen. Example: #map/0
+                       OO.Router.route( 'map/([0-9]+)', function ( mapId ) {
+                               var $map = $( mw.kartographer.maps[ mapId ] );
+                               if ( $map.length ) {
+                                       mw.kartographer.openFullscreenMap( 
getMapProps( $map ) );
+                               }
+                       } );
+
+                       // Index route
+                       OO.Router.route( '', function () {
+                               if ( mapDialog ) {
+                                       mapDialog.close();
+                               }
+                       } );
+
+               } );
        } );
 }( jQuery, mediaWiki ) );

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I456a4582a67e31533d51d5817d0f4af57528c35e
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Kartographer
Gerrit-Branch: master
Gerrit-Owner: JGirault <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to