| Current File : /home/jvzmxxx/wiki/extensions/MobileFrontend/resources/mobile.nearby/Nearby.js |
( function ( M, $ ) {
var MessageBox = M.require( 'mobile.messageBox/MessageBox' ),
NearbyGateway = M.require( 'mobile.nearby/NearbyGateway' ),
WatchstarPageList = M.require( 'mobile.pagelist.scripts/WatchstarPageList' ),
browser = M.require( 'mobile.startup/Browser' ).getSingleton(),
icons = M.require( 'mobile.startup/icons' );
/**
* List of nearby pages
* @class Nearby
* @uses NearbyGateway
* @extends WatchstarPageList
*
* @constructor
* @param {Object} options Configuration options
*/
function Nearby( options ) {
var self = this,
_super = WatchstarPageList;
this.range = options.range || mw.config.get( 'wgMFNearbyRange' ) || 1000;
this.source = options.source || 'nearby';
this.nearbyApi = new NearbyGateway( {
api: options.api
} );
if ( options.errorType ) {
options.errorOptions = self._errorOptions( options.errorType );
}
_super.apply( this, arguments );
this.refresh( options );
}
OO.mfExtend( Nearby, WatchstarPageList, {
errorMessages: {
empty: {
heading: mw.msg( 'mobile-frontend-nearby-noresults' ),
msg: mw.msg( 'mobile-frontend-nearby-noresults-guidance' )
},
locating: {
heading: mw.msg( 'mobile-frontend-nearby-lookup-ui-error' ),
msg: mw.msg( 'mobile-frontend-nearby-lookup-ui-error-guidance' )
},
permission: {
heading: mw.msg( 'mobile-frontend-nearby-permission' ),
msg: mw.msg( 'mobile-frontend-nearby-permission-guidance' )
},
http: {
heading: mw.msg( 'mobile-frontend-nearby-error' ),
msg: mw.msg( 'mobile-frontend-nearby-error-guidance' )
},
incompatible: {
heading: mw.msg( 'mobile-frontend-nearby-requirements' ),
msg: mw.msg( 'mobile-frontend-nearby-requirements-guidance' )
}
},
templatePartials: $.extend( {}, WatchstarPageList.prototype.templatePartials, {
pageList: WatchstarPageList.prototype.template,
messageBox: MessageBox.prototype.template
} ),
template: mw.template.get( 'mobile.nearby', 'Nearby.hogan' ),
/**
* @inheritdoc
* @cfg {Object} defaults Default options hash.
* @cfg {mw.Api} defaults.api
* @cfg {Object} defaults.errorOptions options to pass to a messagebox template
* @cfg {string} defaults.spinner HTML of the spinner icon with a tooltip that
* tells the user that their location is being looked up
*/
defaults: $.extend( {}, WatchstarPageList.prototype.defaults, {
errorOptions: undefined,
spinner: icons.spinner( {
title: mw.msg( 'mobile-frontend-nearby-loading' )
} ).toHtmlString()
} ),
/**
* Obtain users current location and return a deferred object with the
* longitude and latitude values
* Resolve return object with 'incompatible' if browser doesn't support geo location
*
* @return {jQuery.Deferred}
*/
getCurrentPosition: function () {
var result = $.Deferred();
if ( browser.supportsGeoLocation() ) {
navigator.geolocation.getCurrentPosition( function ( geo ) {
result.resolve( {
latitude: geo.coords.latitude,
longitude: geo.coords.longitude
} );
},
function ( err ) {
// see https://developer.mozilla.org/en-US/docs/Web/API/PositionError
if ( err.code === 1 ) {
err = 'permission';
} else {
err = 'locating';
}
result.reject( err );
},
{
timeout: 10000,
enableHighAccuracy: true
} );
} else {
result.reject( 'incompatible' );
}
return result;
},
/**
* Request pages from api based on provided options.
* When options.longitude and options.latitude set getPages near that location.
* If those are not present use options.title to find pages near that title.
* If no valid options given resolve return object with error message.
* @param {Object} options Configuration options
* @return {jQuery.Deferred}
* @private
*/
_find: function ( options ) {
var result = $.Deferred(),
self = this;
/**
* Handler for successful query
* @param {Array} pages as passed by done callback of Nearby##getPages
* @ignore
*/
function pagesSuccess( pages ) {
options.pages = pages;
if ( pages && pages.length === 0 ) {
options.errorOptions = self._errorOptions( 'empty' );
}
self._isLoading = false;
result.resolve( options );
}
/**
* Handler for failed query
*
* @param {string} code Error Code
* @param {string} details A html-safe string with ad detailed error description
* @ignore
*/
function pagesError( code, details ) {
self._isLoading = false;
options.errorOptions = self._errorOptions( code, details );
result.resolve( options );
}
if ( options.latitude && options.longitude ) {
this.nearbyApi.getPages( {
latitude: options.latitude,
longitude: options.longitude
},
this.range, options.exclude
)
.done( pagesSuccess )
.fail( pagesError );
} else if ( options.pageTitle ) {
this.nearbyApi.getPagesAroundPage( options.pageTitle, this.range )
.done( pagesSuccess )
.fail( pagesError );
} else {
if ( options.errorType ) {
options.errorOptions = this._errorOptions( options.errorType );
}
result.resolve( options );
}
return result;
},
/**
* Generate a list of options that can be passed to a messagebox template.
* @private
* @param {string} key to a defined error message
* @param {string} msg Message to use, instead of a mapped error message from this.errorMessages
* @return {Object}
*/
_errorOptions: function ( key, msg ) {
var message;
if ( msg ) {
message = { msg: msg };
} else {
message = this.errorMessages[ key ] || this.errorMessages.http;
}
return $.extend( {
className: 'errorbox'
}, message );
},
/** @inheritdoc */
postRender: function () {
if ( !this._isLoading ) {
this.$( '.spinner' ).addClass( 'hidden' );
this.$( '.page-list' ).removeClass( 'hidden' );
}
WatchstarPageList.prototype.postRender.apply( this );
this._postRenderLinks();
},
/**
* Hijack links to apply several customisations to them:
* Ensure that when clicked they register an uploads funnel.
* Ensure that when a user navigates back to the page their page position is restored using
* fragment identifier trickery.
* @private
*/
_postRenderLinks: function () {
var offset,
hash = window.location.hash;
this.$( 'a' ).each( function ( i ) {
// FIXME: not unique if multiple Nearby objects on same page
$( this ).attr( 'id', 'nearby-page-list-item-' + i );
} ).on( 'click', function ( ev ) {
// Do not react to 'open in new tab' clicks as changing the hash
// re-renders the view.
if ( ev.altKey || ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.which === 2 ) {
return;
}
// if not on Special:Nearby/#page/page_title or Special:Nearby/#coord/
// then set hash to clicked element
if ( !hash.match( /^(#\/page|#\/coord)/i ) ) {
window.location.hash = $( this ).attr( 'id' );
}
} );
// Restore the offset
if ( hash.indexOf( '/' ) === -1 ) {
offset = $( window.location.hash ).offset();
if ( offset ) {
// Don't reset the hash here as we don't want to trigger another Route
$( window ).scrollTop( offset.top );
}
}
},
/**
* Refresh the list of the nearby articles depending on the options.
* The current location, latitude/longitude, or page title can be used
* to find the articles.
*
* @param {Object} options Configuration options
*/
refresh: function ( options ) {
var self = this,
_super = WatchstarPageList;
this.$( '.spinner' ).removeClass( 'hidden' );
this.$( '.page-list' ).addClass( 'hidden' );
// Re-run after api/geolocation request
if ( options.useCurrentLocation ) {
// Flush any existing list of pages
options.pages = [];
// Get some new pages
this.getCurrentPosition().done( function ( coordOptions ) {
$.extend( options, coordOptions );
self._find( options ).done( function ( options ) {
_super.call( self, options );
} );
} ).fail( function ( errorType ) {
options.errorOptions = self._errorOptions( errorType );
self._isLoading = false;
_super.call( self, options );
} );
} else if ( ( options.latitude && options.longitude ) || options.pageTitle ) {
// Flush any existing list of pages
options.pages = [];
// Get some new pages
this._find( options ).done( function ( options ) {
_super.call( self, options );
} ).fail( function ( errorType ) {
options.errorOptions = self._errorOptions( errorType );
self._isLoading = false;
_super.call( self, options );
} );
}
// Run it once for loader etc
this._isLoading = true;
}
} );
M.define( 'mobile.nearby/Nearby', Nearby );
}( mw.mobileFrontend, jQuery ) );