All files / mobile.startup Browser.js

96.77% Statements 30/31
93.75% Branches 15/16
100% Functions 7/7
96.66% Lines 29/30

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 1111x                                   30x 41x   41x 41x 24x   17x   30x 30x                             10x 10x                 10x 14x 14x   14x 8x         4x   2x   2x         6x                 10x 2x     2x               10x               15x 1x 1x   15x       1x  
const util = require( './util' );
 
let browser;
 
/**
 * Memoize a class method. Caches the result of the method based on the
 * arguments. Instances do not share a cache.
 *
 * @private
 * @param {Function} method Method to be memoized
 * @return {Function}
 */
function memoize( method ) {
	/**
	 * Memoized version of the method
	 *
	 * @return {Function}
	 */
	const memoized = function () {
		const cache = this[ '__cache' + memoized.cacheId ] ||
			( this[ '__cache' + memoized.cacheId ] = {} ),
			key = [].join.call( arguments, '|' );
		if ( Object.prototype.hasOwnProperty.call( cache, key ) ) {
			return cache[ key ];
		}
		return ( cache[ key ] = method.apply( this, arguments ) );
	};
	memoized.cacheId = Date.now().toString() + Math.random().toString();
	return memoized;
}
 
/**
 * Representation of user's current browser
 *
 * @class Browser
 * @private
 */
class Browser {
	/**
	 * @param {string} userAgent the user agent of the current browser
	 * @param {jQuery.Object} $container an element to associate with the Browser object
	 */
	constructor( userAgent, $container ) {
		this.userAgent = userAgent;
		this.$el = $container;
		/**
		 * Returns whether the current browser is an ios device.
		 * FIXME: jquery.client does not support iPad detection so we cannot use it.
		 *
		 * @instance
		 * @param {number} [version] integer describing a specific version you want to test against.
		 * @return {boolean}
		 */
		this.isIos = memoize( function ( version ) {
			const ua = this.userAgent,
				ios = /ipad|iphone|ipod/i.test( ua );
 
			if ( ios && version ) {
				switch ( version ) {
					case 8:
					// Test UA for iOS8. Or for simulator look for Version 8
					// In the iOS simulator the OS is the host machine OS version
					// This makes testing in iOS8 simulator work as expected
						return /OS 8_/.test( ua ) || /Version\/8/.test( ua );
					case 4:
						return /OS 4_/.test( ua );
					case 5:
						return /OS 5_/.test( ua );
					default:
						return false;
				}
			} else {
				return ios;
			}
		} );
		/**
		 * Determine if a device has a widescreen.
		 *
		 * @instance
		 * @return {boolean}
		 */
		this.isWideScreen = memoize( () => {
			const val = parseInt( mw.config.get( 'wgMFDeviceWidthTablet' ), 10 );
			// Check viewport width to determine mobile vs tablet.
			// Note: Mobile devices held in landscape mode might receive tablet treatment.
			return window.innerWidth >= val;
		} );
		/**
		 * Whether touchstart and other touch events are supported by the current browser.
		 *
		 * @instance
		 * @return {boolean}
		 */
		this.supportsTouchEvents = memoize( () => 'ontouchstart' in window );
	}
 
	/**
	 * @return {Browser}
	 */
	static getSingleton() {
		let $html;
		if ( !browser ) {
			$html = util.getDocument();
			browser = new Browser( window.navigator.userAgent, $html );
		}
		return browser;
	}
}
 
module.exports = Browser;