Merge "Remove not used private member variable mParserWarnings from OutputPage"
[mediawiki.git] / resources / src / jquery / jquery.hidpi.js
blob7d308f8e845b5061ed98cbc2a2ae87d8773e3795
1 /**
2  * Responsive images based on `srcset` and `window.devicePixelRatio` emulation where needed.
3  *
4  * Call `.hidpi()` on a document or part of a document to proces image srcsets within that section.
5  *
6  * `$.devicePixelRatio()` can be used as a substitute for `window.devicePixelRatio`.
7  * It provides a familiar interface to retrieve the pixel ratio for browsers that don't
8  * implement `window.devicePixelRatio` but do have a different way of getting it.
9  *
10  * @class jQuery.plugin.hidpi
11  */
12 ( function ( $ ) {
14 /**
15  * Get reported or approximate device pixel ratio.
16  *
17  * - 1.0 means 1 CSS pixel is 1 hardware pixel
18  * - 2.0 means 1 CSS pixel is 2 hardware pixels
19  * - etc.
20  *
21  * Uses `window.devicePixelRatio` if available, or CSS media queries on IE.
22  *
23  * @static
24  * @inheritable
25  * @return {number} Device pixel ratio
26  */
27 $.devicePixelRatio = function () {
28         if ( window.devicePixelRatio !== undefined ) {
29                 // Most web browsers:
30                 // * WebKit/Blink (Safari, Chrome, Android browser, etc)
31                 // * Opera
32                 // * Firefox 18+
33                 // * Microsoft Edge (Windows 10)
34                 return window.devicePixelRatio;
35         } else if ( window.msMatchMedia !== undefined ) {
36                 // Windows 8 desktops / tablets, probably Windows Phone 8
37                 //
38                 // IE 10/11 doesn't report pixel ratio directly, but we can get the
39                 // screen DPI and divide by 96. We'll bracket to [1, 1.5, 2.0] for
40                 // simplicity, but you may get different values depending on zoom
41                 // factor, size of screen and orientation in Metro IE.
42                 if ( window.msMatchMedia( '(min-resolution: 192dpi)' ).matches ) {
43                         return 2;
44                 } else if ( window.msMatchMedia( '(min-resolution: 144dpi)' ).matches ) {
45                         return 1.5;
46                 } else {
47                         return 1;
48                 }
49         } else {
50                 // Legacy browsers...
51                 // Assume 1 if unknown.
52                 return 1;
53         }
56 /**
57  * Bracket a given device pixel ratio to one of [1, 1.5, 2].
58  *
59  * This is useful for grabbing images on the fly with sizes based on the display
60  * density, without causing slowdown and extra thumbnail renderings on devices
61  * that are slightly different from the most common sizes.
62  *
63  * The bracketed ratios match the default 'srcset' output on MediaWiki thumbnails,
64  * so will be consistent with default renderings.
65  *
66  * @static
67  * @inheritable
68  * @return {number} Device pixel ratio
69  */
70 $.bracketDevicePixelRatio = function ( baseRatio ) {
71         if ( baseRatio > 1.5 ) {
72                 return 2;
73         } else if ( baseRatio > 1 ) {
74                 return 1.5;
75         } else {
76                 return 1;
77         }
80 /**
81  * Get reported or approximate device pixel ratio, bracketed to [1, 1.5, 2].
82  *
83  * This is useful for grabbing images on the fly with sizes based on the display
84  * density, without causing slowdown and extra thumbnail renderings on devices
85  * that are slightly different from the most common sizes.
86  *
87  * The bracketed ratios match the default 'srcset' output on MediaWiki thumbnails,
88  * so will be consistent with default renderings.
89  *
90  * - 1.0 means 1 CSS pixel is 1 hardware pixel
91  * - 1.5 means 1 CSS pixel is 1.5 hardware pixels
92  * - 2.0 means 1 CSS pixel is 2 hardware pixels
93  *
94  * @static
95  * @inheritable
96  * @return {number} Device pixel ratio
97  */
98 $.bracketedDevicePixelRatio = function () {
99         return $.bracketDevicePixelRatio( $.devicePixelRatio() );
103  * Implement responsive images based on srcset attributes, if browser has no
104  * native srcset support.
106  * @return {jQuery} This selection
107  * @chainable
108  */
109 $.fn.hidpi = function () {
110         var $target = this,
111                 // TODO add support for dpi media query checks on Firefox, IE
112                 devicePixelRatio = $.devicePixelRatio(),
113                 testImage = new Image();
115         if ( devicePixelRatio > 1 && testImage.srcset === undefined ) {
116                 // No native srcset support.
117                 $target.find( 'img' ).each( function () {
118                         var $img = $( this ),
119                                 srcset = $img.attr( 'srcset' ),
120                                 match;
121                         if ( typeof srcset === 'string' && srcset !== '' ) {
122                                 match = $.matchSrcSet( devicePixelRatio, srcset );
123                                 if ( match !== null ) {
124                                         $img.attr( 'src', match );
125                                 }
126                         }
127                 } );
128         }
130         return $target;
134  * Match a srcset entry for the given device pixel ratio
136  * Exposed for testing.
138  * @private
139  * @static
140  * @param {number} devicePixelRatio
141  * @param {string} srcset
142  * @return {Mixed} null or the matching src string
143  */
144 $.matchSrcSet = function ( devicePixelRatio, srcset ) {
145         var candidates,
146                 candidate,
147                 bits,
148                 src,
149                 i,
150                 ratioStr,
151                 ratio,
152                 selectedRatio = 1,
153                 selectedSrc = null;
154         candidates = srcset.split( / *, */ );
155         for ( i = 0; i < candidates.length; i++ ) {
156                 candidate = candidates[ i ];
157                 bits = candidate.split( / +/ );
158                 src = bits[ 0 ];
159                 if ( bits.length > 1 && bits[ 1 ].charAt( bits[ 1 ].length - 1 ) === 'x' ) {
160                         ratioStr = bits[ 1 ].slice( 0, -1 );
161                         ratio = parseFloat( ratioStr );
162                         if ( ratio <= devicePixelRatio && ratio > selectedRatio ) {
163                                 selectedRatio = ratio;
164                                 selectedSrc = src;
165                         }
166                 }
167         }
168         return selectedSrc;
172  * @class jQuery
173  * @mixins jQuery.plugin.hidpi
174  */
176 }( jQuery ) );