Jdlrobson has uploaded a new change for review.
https://gerrit.wikimedia.org/r/70235
Change subject: Refactor nearby into reuseable modules
......................................................................
Refactor nearby into reuseable modules
Change-Id: I10680d1f11e22ba0941a8c192bf3e8ee53d91a1c
---
M includes/Resources.php
A javascripts/modules/NearbyApi.js
M javascripts/specials/nearby.js
A javascripts/widgets/Nearby.js
M tests/javascripts/specials/test_nearby.js
5 files changed, 224 insertions(+), 187 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/MobileFrontend
refs/changes/35/70235/1
diff --git a/includes/Resources.php b/includes/Resources.php
index 31d3ab4..8d7ff07 100644
--- a/includes/Resources.php
+++ b/includes/Resources.php
@@ -614,6 +614,8 @@
'mobile-frontend-nearby-directions',
),
'scripts' => array(
+ 'javascripts/modules/NearbyApi.js',
+ 'javascripts/widgets/Nearby.js',
'javascripts/specials/nearby.js',
),
// stop flash of unstyled content when loading from cache
diff --git a/javascripts/modules/NearbyApi.js b/javascripts/modules/NearbyApi.js
new file mode 100644
index 0000000..7e566e4
--- /dev/null
+++ b/javascripts/modules/NearbyApi.js
@@ -0,0 +1,28 @@
+( function( M ) {
+ var
+ endpoint = mw.config.get( 'wgMFNearbyEndpoint' ),
+ limit = 50,
+ Api = M.require( 'api' ).Api,
+ NearbyApi = Api.extend( {
+ getPages: function( lat, lng, range ) {
+ return this.get( {
+ action: 'query',
+ colimit: 'max',
+ prop: 'pageimages|coordinates',
+ pithumbsize: 180,
+ pilimit: limit,
+ generator: 'geosearch',
+ ggscoord: [ lat, lng ],
+ ggsradius: range,
+ ggsnamespace: 0,
+ ggslimit: limit
+ },
+ {
+ dataType: endpoint ? 'jsonp' : 'json',
+ url: endpoint || M.getApiUrl()
+ } );
+ }
+ } );
+
+ M.define( 'NearbyApi', NearbyApi );
+}( mw.mobileFrontend ) );
\ No newline at end of file
diff --git a/javascripts/specials/nearby.js b/javascripts/specials/nearby.js
index 2c56eca..1406db0 100644
--- a/javascripts/specials/nearby.js
+++ b/javascripts/specials/nearby.js
@@ -1,199 +1,23 @@
( function( M, $ ) {
var CACHE_KEY_RESULTS = 'mfNearbyLastSearchResult',
endpoint = mw.config.get( 'wgMFNearbyEndpoint' ),
- overlay,
+ widget,
+ range = mw.config.get( 'wgMFNearbyRange' ),
CACHE_KEY_LAST_LOCATION = 'mfNearbyLastKnownLocation';
function getOverlay() {
- return overlay;
-}
-
-function distanceMessage( d ) {
- var msg = 'mobile-frontend-nearby-distance';
- if ( d < 1 ) {
- d *= 100;
- d = Math.ceil( d ) * 10;
- if ( d === 1000 ) {
- d = 1;
- } else {
- msg = 'mobile-frontend-nearby-distance-meters';
- }
- d = d + '';
- } else {
- if ( d > 2 ) {
- d *= 10;
- d = Math.ceil( d ) / 10;
- d = d.toFixed( 1 );
- } else {
- d *= 100;
- d = Math.ceil( d ) / 100;
- d = d.toFixed( 2 );
- }
- }
- return mw.msg( msg, d );
+ return widget;
}
$( function() {
var supported = M.supportsGeoLocation(),
- View = M.require( 'view' ),
$userBtn = $( '#user-button' ),
- errorMessages = {
- empty: {
- heading: mw.msg(
'mobile-frontend-nearby-noresults' ),
- guidance: mw.msg(
'mobile-frontend-nearby-noresults-guidance' )
- },
- location: {
- heading: mw.msg(
'mobile-frontend-nearby-lookup-ui-error' ),
- guidance: mw.msg(
'mobile-frontend-nearby-lookup-ui-error-guidance' )
- },
- server: {
- heading: mw.msg( 'mobile-frontend-nearby-error'
),
- guidance: mw.msg(
'mobile-frontend-nearby-error-guidance' )
- },
- // recycle it's already in html
- incompatible: {
- heading: $( '#mw-mf-nearby .noscript h2'
).text(),
- guidance: $( '#mw-mf-nearby .noscript p'
).text()
- }
- },
curLocation,
lastKnownLocation = M.settings.getUserSetting(
CACHE_KEY_LAST_LOCATION ),
cache = M.settings.saveUserSetting,
lastSearchResult = M.settings.getUserSetting( CACHE_KEY_RESULTS
),
- Nearby = View.extend( {
- template: M.template.get( 'articleList' ),
- /**
- * Renders an error in the existing view
- *
- * @param {String} type A string that identifies a
particular type of error message
- */
- renderError: function( type ) {
- this.render( { error: errorMessages[ type ] } );
- },
- openPage: function( ev ) {
- // help back button work
- window.location.hash = '#' + $(
ev.currentTarget ).attr( 'name' );
- window.location = $( ev.currentTarget ).attr(
'href' );
- },
- postRender: function() {
- var self = this;
- this.$( 'a' ).on( 'mousedown', function( ev ) {
- // name funnel for watchlists to catch
subsequent uploads
- $.cookie( 'mwUploadsFunnel', 'nearby',
{ expires: new Date( new Date().getTime() + 60000) } );
- self.openPage( ev );
- } );
- self.emit( 'rendered', this.$el );
- }
- } ),
+ Nearby = M.require( 'widgets/nearby' ),
pendingQuery = false, btn;
-
- overlay = new Nearby( {
- el: $( '#mw-mf-nearby' )
- } );
-
- // FIXME: Api should surely know this and return it in response to save
us the hassle
- // haversine formula ( http://en.wikipedia.org/wiki/Haversine_formula )
- function calculateDistance( from, to ) {
- var distance, a,
- toRadians = Math.PI / 180,
- deltaLat, deltaLng,
- startLat, endLat,
- haversinLat, haversinLng,
- radius = 6378; // radius of Earth in km
-
- if( from.latitude === to.latitude && from.longitude ===
to.longitude ) {
- distance = 0;
- } else {
- deltaLat = ( to.longitude - from.longitude ) *
toRadians;
- deltaLng = ( to.latitude - from.latitude ) * toRadians;
- startLat = from.latitude * toRadians;
- endLat = to.latitude * toRadians;
-
- haversinLat = Math.sin( deltaLat / 2 ) * Math.sin(
deltaLat / 2 );
- haversinLng = Math.sin( deltaLng / 2 ) * Math.sin(
deltaLng / 2 );
-
- a = haversinLat + Math.cos( startLat ) * Math.cos(
endLat ) * haversinLng;
- return 2 * radius * Math.asin( Math.sqrt( a ) );
- }
- return distance;
- }
-
- function render( $content, pages ) {
- cache( CACHE_KEY_RESULTS, $.toJSON( pages ) ); // cache result
- pages = $.map( pages, function( page, i ) {
- var coords, lngLat, thumb;
-
- if ( page.thumbnail ) {
- thumb = page.thumbnail;
- page.listThumbStyleAttribute =
'background-image: url(' + thumb.source + ')';
- page.pageimageClass = thumb.width >
thumb.height ? 'listThumbH' : 'listThumbV';
- } else {
- page.pageimageClass = 'needsPhoto';
- }
- page.anchor = 'item_' + i;
- page.url = M.history.getArticleUrl( page.title );
- if ( page.coordinates ) { // FIXME: protect against bug
47133 (remove when resolved)
- coords = page.coordinates[0],
- lngLat = { latitude: coords.lat, longitude:
coords.lon };
- page.dist = calculateDistance( curLocation,
lngLat );
- page.latitude = coords.lat;
- page.longitude = coords.lon;
- page.proximity = distanceMessage( page.dist );
- }
- page.heading = page.title;
- pages.push( page );
- return page;
- } );
- pages.sort( function( a, b ) {
- return a.dist > b.dist ? 1 : -1;
- } );
-
- overlay.render( {
- pages: pages
- } );
- }
-
- function findResults( lat, lng ) {
- var $content = $( '#mw-mf-nearby' ), range = mw.config.get(
'wgMFNearbyRange' ),
- limit = 50;
-
- $.ajax( {
- dataType: endpoint ? 'jsonp' : 'json',
- url: endpoint || M.getApiUrl(),
- data: {
- action: 'query',
- colimit: 'max',
- prop: 'pageimages|coordinates',
- pithumbsize: 180,
- pilimit: limit,
- generator: 'geosearch',
- format: 'json',
- ggscoord: lat + '|' + lng,
- ggsradius: range,
- ggsnamespace: 0,
- ggslimit: limit
- }
- } ).done( function( data ) {
- var pages;
- // FIXME: API bug 48512
- if ( data.query ) {
- pages = data.query.pages || {};
- } else {
- pages = {};
- }
- // FIXME: API returns object when array would make much
sense
- pages = $.map( pages , function( i ) {
- return i;
- } );
- if ( pages.length > 0 ) {
- render( $content, pages );
- } else {
- overlay.renderError( 'empty' );
- }
- } ).fail( function() {
- overlay.renderError( 'server' );
- } );
- }
function completeRefresh() {
$( 'button.refresh' ).removeClass( 'refreshing' );
@@ -202,17 +26,20 @@
function init() {
var $content = $( '#mw-mf-nearby' ).empty();
+
$( '<div class="content loading"> ').text(
mw.msg( 'mobile-frontend-nearby-loading' ) ).appendTo(
$content );
navigator.geolocation.getCurrentPosition( function( geo ) {
var lat = geo.coords.latitude, lng =
geo.coords.longitude;
curLocation = { latitude: lat, longitude: lng }; //
save as json so it can be cached bug 48268
cache( CACHE_KEY_LAST_LOCATION, $.toJSON( curLocation )
);
- findResults( lat, lng );
+ widget.load( lat, lng, curLocation ).done( function(
pages ) {
+ cache( CACHE_KEY_RESULTS, $.toJSON( pages ) );
// cache result
+ } );
completeRefresh();
},
function() {
- overlay.renderError( 'location' );
+ widget.renderError( 'location' );
completeRefresh();
},
{
@@ -239,12 +66,24 @@
}
}
if ( lastSearchResult && window.location.hash ) {
- render( $( '#content' ), $.parseJSON( lastSearchResult
) );
+ widget = new Nearby( {
+ el: $( '#mw-mf-nearby' ),
+ range: range,
+ pages: $.parseJSON( lastSearchResult )
+ } );
} else {
+ widget = new Nearby( {
+ range: range,
+ el: $( '#mw-mf-nearby' )
+ } );
init();
}
} else {
- overlay.renderError( 'incompatible' );
+ widget = new Nearby( {
+ range: range,
+ el: $( '#mw-mf-nearby' )
+ } );
+ widget.renderError( 'incompatible' );
}
if ( $userBtn.length ) {
@@ -255,7 +94,6 @@
} );
M.define( 'nearby', {
- distanceMessage: distanceMessage,
endpoint: endpoint,
getOverlay: getOverlay
} );
diff --git a/javascripts/widgets/Nearby.js b/javascripts/widgets/Nearby.js
new file mode 100644
index 0000000..1d7ed82
--- /dev/null
+++ b/javascripts/widgets/Nearby.js
@@ -0,0 +1,168 @@
+( function( M, $ ) {
+ var NearbyApi = M.require( 'NearbyApi' ),
+ View = M.require( 'view' ),
+ NearbyWidget = View.extend( {
+ template: M.template.get( 'articleList' ),
+ errorMessages: {
+ empty: {
+ heading: mw.msg(
'mobile-frontend-nearby-noresults' ),
+ guidance: mw.msg(
'mobile-frontend-nearby-noresults-guidance' )
+ },
+ location: {
+ heading: mw.msg(
'mobile-frontend-nearby-lookup-ui-error' ),
+ guidance: mw.msg(
'mobile-frontend-nearby-lookup-ui-error-guidance' )
+ },
+ server: {
+ heading: mw.msg(
'mobile-frontend-nearby-error' ),
+ guidance: mw.msg(
'mobile-frontend-nearby-error-guidance' )
+ },
+ // recycle it's already in html
+ incompatible: {
+ heading: $( '#mw-mf-nearby .noscript
h2' ).text(),
+ guidance: $( '#mw-mf-nearby .noscript
p' ).text()
+ }
+ },
+ initialize: function( options ) {
+ this._super( options );
+ this.range = options.range;
+ this.api = new NearbyApi();
+ },
+ // FIXME: Api should surely know this and return it in
response to save us the hassle
+ // haversine formula (
http://en.wikipedia.org/wiki/Haversine_formula )
+ calculateDistance: function( from, to ) {
+ var distance, a,
+ toRadians = Math.PI / 180,
+ deltaLat, deltaLng,
+ startLat, endLat,
+ haversinLat, haversinLng,
+ radius = 6378; // radius of Earth in km
+
+ if( from.latitude === to.latitude &&
from.longitude === to.longitude ) {
+ distance = 0;
+ } else {
+ deltaLat = ( to.longitude -
from.longitude ) * toRadians;
+ deltaLng = ( to.latitude -
from.latitude ) * toRadians;
+ startLat = from.latitude * toRadians;
+ endLat = to.latitude * toRadians;
+
+ haversinLat = Math.sin( deltaLat / 2 )
* Math.sin( deltaLat / 2 );
+ haversinLng = Math.sin( deltaLng / 2 )
* Math.sin( deltaLng / 2 );
+
+ a = haversinLat + Math.cos( startLat )
* Math.cos( endLat ) * haversinLng;
+ return 2 * radius * Math.asin(
Math.sqrt( a ) );
+ }
+ return distance;
+ },
+ /**
+ * Renders an error in the existing view
+ *
+ * @param {String} type A string that identifies a
particular type of error message
+ */
+ renderError: function( type ) {
+ this.render( { error: this.errorMessages[ type
] } );
+ },
+ openPage: function( ev ) {
+ // help back button work
+ window.location.hash = '#' + $(
ev.currentTarget ).attr( 'name' );
+ window.location = $( ev.currentTarget ).attr(
'href' );
+ },
+ _distanceMessage: function( d ) {
+ var msg = 'mobile-frontend-nearby-distance';
+ if ( d < 1 ) {
+ d *= 100;
+ d = Math.ceil( d ) * 10;
+ if ( d === 1000 ) {
+ d = 1;
+ } else {
+ msg =
'mobile-frontend-nearby-distance-meters';
+ }
+ d = d + '';
+ } else {
+ if ( d > 2 ) {
+ d *= 10;
+ d = Math.ceil( d ) / 10;
+ d = d.toFixed( 1 );
+ } else {
+ d *= 100;
+ d = Math.ceil( d ) / 100;
+ d = d.toFixed( 2 );
+ }
+ }
+ return mw.msg( msg, d );
+ },
+ _render: function( pages, curLocation ) {
+ var self = this;
+
+ pages = $.map( pages, function( page, i ) {
+ var coords, lngLat, thumb;
+
+ if ( page.thumbnail ) {
+ thumb = page.thumbnail;
+ page.listThumbStyleAttribute =
'background-image: url(' + thumb.source + ')';
+ page.pageimageClass =
thumb.width > thumb.height ? 'listThumbH' : 'listThumbV';
+ } else {
+ page.pageimageClass =
'needsPhoto';
+ }
+ page.anchor = 'item_' + i;
+ page.url = M.history.getArticleUrl(
page.title );
+ if ( page.coordinates ) { // FIXME:
protect against bug 47133 (remove when resolved)
+ coords = page.coordinates[0],
+ lngLat = { latitude:
coords.lat, longitude: coords.lon };
+ page.dist =
self.calculateDistance( curLocation, lngLat );
+ page.latitude = coords.lat;
+ page.longitude = coords.lon;
+ page.proximity =
self._distanceMessage( page.dist );
+ }
+ page.heading = page.title;
+ pages.push( page );
+ return page;
+ } );
+
+ pages.sort( function( a, b ) {
+ return a.dist > b.dist ? 1 : -1;
+ } );
+
+ self.render( {
+ pages: pages
+ } );
+ return pages;
+ },
+ load: function( lat, lng, curLocation ) {
+ var self = this, d = $.Deferred();
+ this.api.getPages( lat, lng, this.range ).done(
function( data ) {
+ var pages;
+ // FIXME: API bug 48512
+ if ( data.query ) {
+ pages = data.query.pages || {};
+ } else {
+ pages = {};
+ }
+ // FIXME: API returns object when array
would make much sense
+ pages = $.map( pages , function( i ) {
+ return i;
+ } );
+ if ( pages.length > 0 ) {
+ self._render( pages,
curLocation );
+ d.resolve( pages );
+ } else {
+ self.renderError( 'empty' );
+ }
+ } ).fail( function() {
+ self.renderError( 'server' );
+ } );
+ return d;
+ },
+ postRender: function() {
+ var self = this;
+ this.$( 'a' ).on( 'mousedown', function( ev ) {
+ // name funnel for watchlists to catch
subsequent uploads
+ $.cookie( 'mwUploadsFunnel', 'nearby',
{ expires: new Date( new Date().getTime() + 60000) } );
+ self.openPage( ev );
+ } );
+ self.emit( 'rendered', this.$el );
+ }
+ } );
+
+ M.define( 'widgets/nearby', NearbyWidget );
+
+}( mw.mobileFrontend, jQuery ) );
\ No newline at end of file
diff --git a/tests/javascripts/specials/test_nearby.js
b/tests/javascripts/specials/test_nearby.js
index 109efe5..ef2e607 100644
--- a/tests/javascripts/specials/test_nearby.js
+++ b/tests/javascripts/specials/test_nearby.js
@@ -1,6 +1,7 @@
( function ( M, $ ) {
-var m = M.require( 'nearby' );
+var Nearby = M.require( 'widgets/nearby-pages' ),
+ m = new Nearby();
QUnit.module( 'MobileFrontend Nearby', {
setup: function() {
@@ -27,7 +28,7 @@
QUnit.expect( tests.length );
$( tests ).each( function( i ) {
- m.distanceMessage( this[0] );
+ m._distanceMessage( this[0] );
strictEqual( mw.msg.getCall( i ).calledWith( this[1], this[2]
), true, 'failed test ' + i );
} );
} );
--
To view, visit https://gerrit.wikimedia.org/r/70235
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I10680d1f11e22ba0941a8c192bf3e8ee53d91a1c
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/MobileFrontend
Gerrit-Branch: master
Gerrit-Owner: Jdlrobson <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits