Show Download button only on Android >= 5 & Chrome >= 41
Changes: - moved DownloadButtton checks & initialization to separate function - introduced supportedNamespaces variable for better readability - reorganized huge if(){} statement to set of smaller if's with nice comments why this configuration is not supported - introduced getAndroidVersion and getChromeVersion helper functions - added check to not allow Android < 5 or Chrome < 41 - added unit tests Bug: T182059 Change-Id: Ib5064459ee56aed68179389f37b4bc3b5c2c4492
This commit is contained in:
parent
def790a9e9
commit
4ca5666c06
|
@ -1,9 +1,31 @@
|
||||||
( function ( M, track ) {
|
( function ( M, track ) {
|
||||||
|
|
||||||
var msg = mw.msg,
|
var msg = mw.msg,
|
||||||
MAX_PRINT_TIMEOUT = 3000,
|
MAX_PRINT_TIMEOUT = 3000,
|
||||||
GLYPH = 'mf-download',
|
GLYPH = 'mf-download',
|
||||||
Icon = M.require( 'mobile.startup/Icon' );
|
Icon = M.require( 'mobile.startup/Icon' ),
|
||||||
|
browser = M.require( 'mobile.startup/Browser' ).getSingleton();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to retreive the Android version
|
||||||
|
* @ignore
|
||||||
|
* @param {String} userAgent User Agent
|
||||||
|
* @return {Integer}
|
||||||
|
*/
|
||||||
|
function getAndroidVersion( userAgent ) {
|
||||||
|
var match = userAgent.toLowerCase().match( /android\s(\d\.]*)/ );
|
||||||
|
return match ? parseInt( match[1] ) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to retrieve the Chrome/Chromium version
|
||||||
|
* @ignore
|
||||||
|
* @param {String} userAgent User Agent
|
||||||
|
* @return {Integer}
|
||||||
|
*/
|
||||||
|
function getChromeVersion( userAgent ) {
|
||||||
|
var match = userAgent.toLowerCase().match( /chrom(e|ium)\/(\d+)\./ );
|
||||||
|
return match ? parseInt( match[2] ) : false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A download icon for triggering print functionality
|
* A download icon for triggering print functionality
|
||||||
|
@ -11,11 +33,13 @@
|
||||||
* @extends Icon
|
* @extends Icon
|
||||||
*
|
*
|
||||||
* @param {Skin} skin
|
* @param {Skin} skin
|
||||||
|
* @param {Number[]} [supportedNamespaces]
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function DownloadIcon( skin ) {
|
function DownloadIcon( skin, supportedNamespaces ) {
|
||||||
var options = {};
|
var options = {};
|
||||||
this.skin = skin;
|
this.skin = skin;
|
||||||
|
this.supportedNamespaces = supportedNamespaces || [ 0 ];
|
||||||
options.tagName = 'li';
|
options.tagName = 'li';
|
||||||
options.title = msg( 'minerva-download' );
|
options.title = msg( 'minerva-download' );
|
||||||
options.name = GLYPH;
|
options.name = GLYPH;
|
||||||
|
@ -23,6 +47,34 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
OO.mfExtend( DownloadIcon, Icon, {
|
OO.mfExtend( DownloadIcon, Icon, {
|
||||||
|
/**
|
||||||
|
* Checks whether DownloadIcon is available for given user agent
|
||||||
|
* @param {string} userAgent User agent
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
isAvailable: function ( userAgent ) {
|
||||||
|
var androidVersion = getAndroidVersion( userAgent ),
|
||||||
|
chromeVersion = getChromeVersion( userAgent ),
|
||||||
|
page = this.skin.page;
|
||||||
|
|
||||||
|
// Download button is restricted to certain namespaces T181152.
|
||||||
|
// Defaults to 0, in case cached JS has been served.
|
||||||
|
if ( this.supportedNamespaces.indexOf( page.getNamespaceId() ) === -1 ||
|
||||||
|
page.isMainPage() ) {
|
||||||
|
// namespace is not supported or it's a main page
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( browser.isIos() || chromeVersion === false ) {
|
||||||
|
// we support only chrome/chromium on desktop/android
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( ( androidVersion && androidVersion < 5 ) || chromeVersion < 41 ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace download icon with a spinner
|
* Replace download icon with a spinner
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
( function ( M, track, $ ) {
|
( function ( M, track, config, $ ) {
|
||||||
var
|
var
|
||||||
config = mw.config,
|
|
||||||
toast = M.require( 'mobile.startup/toast' ),
|
toast = M.require( 'mobile.startup/toast' ),
|
||||||
time = M.require( 'mobile.startup/time' ),
|
time = M.require( 'mobile.startup/time' ),
|
||||||
skin = M.require( 'mobile.init/skin' ),
|
skin = M.require( 'mobile.init/skin' ),
|
||||||
|
@ -220,6 +219,29 @@
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize and inject the download button
|
||||||
|
*
|
||||||
|
* There are many restrictions when we can show the download button, this function should handle
|
||||||
|
* all device/os/operating system related checks and if device supports printing it will inject
|
||||||
|
* the Download icon
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
function appendDownloadButton() {
|
||||||
|
var downloadIcon = new DownloadIcon( skin, config.get( 'wgMinervaDownloadNamespaces' ) );
|
||||||
|
|
||||||
|
if ( downloadIcon.isAvailable( navigator.userAgent ) ) {
|
||||||
|
// Because the page actions are floated to the right, their order in the
|
||||||
|
// DOM is reversed in the display. The watchstar is last in the DOM and
|
||||||
|
// left-most in the display. Since we want the download button to be to
|
||||||
|
// the left of the watchstar, we put it after it in the DOM.
|
||||||
|
downloadIcon.$el.insertAfter( '#ca-watch' );
|
||||||
|
track( 'minerva.downloadAsPDF', {
|
||||||
|
action: 'buttonVisible'
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$( function () {
|
$( function () {
|
||||||
// Update anything else that needs enhancing (e.g. watchlist)
|
// Update anything else that needs enhancing (e.g. watchlist)
|
||||||
initModifiedInfo();
|
initModifiedInfo();
|
||||||
|
@ -227,29 +249,8 @@
|
||||||
initHistoryLink( $( '.last-modifier-tagline a' ) );
|
initHistoryLink( $( '.last-modifier-tagline a' ) );
|
||||||
M.on( 'resize', loadTabletModules );
|
M.on( 'resize', loadTabletModules );
|
||||||
loadTabletModules();
|
loadTabletModules();
|
||||||
|
appendDownloadButton();
|
||||||
if (
|
|
||||||
// Download button is restricted to certain namespaces T181152.
|
|
||||||
// Defaults to 0, in case cached JS has been served.
|
|
||||||
config.get( 'wgMinervaDownloadNamespaces', [ 0 ] )
|
|
||||||
.indexOf( config.get( 'wgNamespaceNumber' ) ) > -1 &&
|
|
||||||
!page.isMainPage() &&
|
|
||||||
// The iOS print dialog does not provide pdf functionality (see T177215)
|
|
||||||
!browser.isIos() &&
|
|
||||||
// Currently restricted to Chrome (T179529) until we have good fallbacks for browsers
|
|
||||||
// which do not provide print functionality
|
|
||||||
window.chrome !== undefined
|
|
||||||
) {
|
|
||||||
// Because the page actions are floated to the right, their order in the
|
|
||||||
// DOM is reversed in the display. The watchstar is last in the DOM and
|
|
||||||
// left-most in the display. Since we want the download button to be to
|
|
||||||
// the left of the watchstar, we put it after it in the DOM.
|
|
||||||
new DownloadIcon( skin ).$el.insertAfter( '#ca-watch' );
|
|
||||||
track( 'minerva.downloadAsPDF', {
|
|
||||||
action: 'buttonVisible'
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
M.define( 'skins.minerva.scripts/overlayManager', overlayManager );
|
M.define( 'skins.minerva.scripts/overlayManager', overlayManager );
|
||||||
}( mw.mobileFrontend, mw.track, jQuery ) );
|
}( mw.mobileFrontend, mw.track, mw.config, jQuery ) );
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
( function ( M ) {
|
( function ( M ) {
|
||||||
var Skin = M.require( 'mobile.startup/Skin' ),
|
var VALID_UA = 'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Mobile Safari/537.36',
|
||||||
|
VALID_SUPPORTED_NAMESPACES = [ 0 ],
|
||||||
|
Skin = M.require( 'mobile.startup/Skin' ),
|
||||||
Deferred = $.Deferred,
|
Deferred = $.Deferred,
|
||||||
DownloadIcon = M.require( 'skins.minerva.scripts/DownloadIcon' );
|
DownloadIcon = M.require( 'skins.minerva.scripts/DownloadIcon' ),
|
||||||
|
browser = M.require( 'mobile.startup/Browser' ).getSingleton(),
|
||||||
|
Page = M.require( 'mobile.startup/Page' );
|
||||||
|
|
||||||
QUnit.module( 'Minerva DownloadIcon', {
|
QUnit.module( 'Minerva DownloadIcon', {
|
||||||
setup: function () {
|
setup: function () {
|
||||||
|
@ -64,4 +68,81 @@
|
||||||
|
|
||||||
return d;
|
return d;
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
QUnit.module( 'Minerva DownloadIcon.isAvailable()', {
|
||||||
|
setup: function () {
|
||||||
|
this.skin = new Skin( {
|
||||||
|
page: new Page( {
|
||||||
|
id: 0,
|
||||||
|
title: 'Test',
|
||||||
|
isMainPage: false
|
||||||
|
} )
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
QUnit.test( 'isAvailable() handles properly correct namespace', function ( assert ) {
|
||||||
|
var icon = new DownloadIcon( this.skin, VALID_SUPPORTED_NAMESPACES );
|
||||||
|
assert.ok( icon.isAvailable( VALID_UA ) );
|
||||||
|
} );
|
||||||
|
|
||||||
|
QUnit.test( 'isAvailable() handles properly not supported namespace', function ( assert ) {
|
||||||
|
var icon = new DownloadIcon( this.skin, [ 9999 ] );
|
||||||
|
assert.notOk( icon.isAvailable( VALID_UA ) );
|
||||||
|
} );
|
||||||
|
|
||||||
|
QUnit.test( 'isAvailable() handles properly main page', function ( assert ) {
|
||||||
|
var icon;
|
||||||
|
this.skin.page = new Page( {
|
||||||
|
id: 0,
|
||||||
|
title: 'Test',
|
||||||
|
isMainPage: true
|
||||||
|
} );
|
||||||
|
icon = new DownloadIcon( this.skin, VALID_SUPPORTED_NAMESPACES );
|
||||||
|
assert.notOk( icon.isAvailable( VALID_UA ) );
|
||||||
|
} );
|
||||||
|
|
||||||
|
QUnit.test( 'isAvailable() returns false for iOS', function ( assert ) {
|
||||||
|
var icon;
|
||||||
|
this.sandbox.stub( browser, 'isIos' ).returns( true );
|
||||||
|
icon = new DownloadIcon( this.skin, VALID_SUPPORTED_NAMESPACES );
|
||||||
|
assert.notOk( icon.isAvailable( VALID_UA ) );
|
||||||
|
} );
|
||||||
|
|
||||||
|
QUnit.test( 'isAvailable() handles properly non-chrome browsers', function ( assert ) {
|
||||||
|
var icon = new DownloadIcon( this.skin, VALID_SUPPORTED_NAMESPACES );
|
||||||
|
// IPhone 6 Safari
|
||||||
|
assert.notOk( icon.isAvailable( 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_0_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13A405 Safari/601.1' ) );
|
||||||
|
// Nokia Lumia 930 Windows Phone 8.1
|
||||||
|
assert.notOk( icon.isAvailable( 'Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; Microsoft; Virtual) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537' ) );
|
||||||
|
// Firefox @ Ubuntu
|
||||||
|
assert.notOk( icon.isAvailable( 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:56.0) Gecko/20100101 Firefox/56.0' ) );
|
||||||
|
} );
|
||||||
|
|
||||||
|
QUnit.test( 'isAvailable() handles properly old devices', function ( assert ) {
|
||||||
|
var icon = new DownloadIcon( this.skin, VALID_SUPPORTED_NAMESPACES );
|
||||||
|
// Samsung Galaxy S5, Android 4.4, Chrome 28
|
||||||
|
assert.notOk( icon.isAvailable( 'Mozilla/5.0 (Linux; Android 4.4.2; en-us; SAMSUNG SM-G900F Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/1.6 Chrome/28.0.1500.94 Mobile Safari/537.36' ) );
|
||||||
|
// Samsung Galaxyu S1, Android 4.2.2 Cyanogenmod + built in Samsung Browser
|
||||||
|
assert.notOk( icon.isAvailable( 'Mozilla/5.0 (Linux; U; Android 4.2.2; en-ca; GT-I9000 Build/JDQ39E) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30 CyanogenMod/10.1.0/galaxysmtd' ) );
|
||||||
|
// Samsung Galaxy S3
|
||||||
|
assert.notOk( icon.isAvailable( 'Mozilla/5.0 (Linux; Android 4.3; GT-I9300 Build/JSS15J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36' ) );
|
||||||
|
} );
|
||||||
|
|
||||||
|
QUnit.test( 'isAvailable() handles properly supported browsers', function ( assert ) {
|
||||||
|
var icon = new DownloadIcon( this.skin, VALID_SUPPORTED_NAMESPACES );
|
||||||
|
// Samsung Galaxy S7, Android 6, Chrome 44
|
||||||
|
assert.ok( icon.isAvailable( 'Mozilla/5.0 (Linux; Android 6.0.1; SAMSUNG SM-G930F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile Safari/537.36' ) );
|
||||||
|
// Samsung Galaxy A5, Android 7, Samsung Browser 5.2
|
||||||
|
assert.ok( icon.isAvailable( 'Mozilla/5.0 (Linux; Android 7.0; SAMSUNG SM-A510F Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/5.2 Chrome/51.0.2704.106 Mobile Safari/537.36' ) );
|
||||||
|
// Galaxy J2, Android 5, Chrome 65
|
||||||
|
assert.ok( icon.isAvailable( 'Mozilla/5.0 (Linux; Android 5.1.1; SM-J200G Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3320.0 Mobile Safari/537.36' ) );
|
||||||
|
// Desktop, Chrome 63
|
||||||
|
assert.ok( icon.isAvailable( 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36' ) );
|
||||||
|
// Desktop, Ubuntu, Chromium 61
|
||||||
|
assert.ok( icon.isAvailable( 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/61.0.3163.100 Chrome/61.0.3163.100 Safari/537.36' ) );
|
||||||
|
// Galaxy S8, Android 8, Samsung Browser 6.2
|
||||||
|
assert.ok( icon.isAvailable( 'Mozilla/5.0 (Linux; Android 7.0; SAMSUNG SM-G950U1 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/6.2 Chrome/56.0.2924.87 Mobile Safari/537.36' ) );
|
||||||
|
} );
|
||||||
|
|
||||||
}( mw.mobileFrontend ) );
|
}( mw.mobileFrontend ) );
|
||||||
|
|
Loading…
Reference in New Issue