Merge "Improve sorting on SpecialWanted*-Pages"
[mediawiki.git] / resources / src / jquery / jquery.hidpi.js
blob7a7109cbc3013847e85918e1bbf74484253ad17e
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                 }
54         };
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          * @param {number} baseRatio Base ratio
69          * @return {number} Device pixel ratio
70          */
71         $.bracketDevicePixelRatio = function ( baseRatio ) {
72                 if ( baseRatio > 1.5 ) {
73                         return 2;
74                 } else if ( baseRatio > 1 ) {
75                         return 1.5;
76                 } else {
77                         return 1;
78                 }
79         };
81         /**
82          * Get reported or approximate device pixel ratio, bracketed to [1, 1.5, 2].
83          *
84          * This is useful for grabbing images on the fly with sizes based on the display
85          * density, without causing slowdown and extra thumbnail renderings on devices
86          * that are slightly different from the most common sizes.
87          *
88          * The bracketed ratios match the default 'srcset' output on MediaWiki thumbnails,
89          * so will be consistent with default renderings.
90          *
91          * - 1.0 means 1 CSS pixel is 1 hardware pixel
92          * - 1.5 means 1 CSS pixel is 1.5 hardware pixels
93          * - 2.0 means 1 CSS pixel is 2 hardware pixels
94          *
95          * @static
96          * @inheritable
97          * @return {number} Device pixel ratio
98          */
99         $.bracketedDevicePixelRatio = function () {
100                 return $.bracketDevicePixelRatio( $.devicePixelRatio() );
101         };
103         /**
104          * Implement responsive images based on srcset attributes, if browser has no
105          * native srcset support.
106          *
107          * @return {jQuery} This selection
108          * @chainable
109          */
110         $.fn.hidpi = function () {
111                 var $target = this,
112                         // TODO add support for dpi media query checks on Firefox, IE
113                         devicePixelRatio = $.devicePixelRatio(),
114                         testImage = new Image();
116                 if ( devicePixelRatio > 1 && testImage.srcset === undefined ) {
117                         // No native srcset support.
118                         $target.find( 'img' ).each( function () {
119                                 var $img = $( this ),
120                                         srcset = $img.attr( 'srcset' ),
121                                         match;
122                                 if ( typeof srcset === 'string' && srcset !== '' ) {
123                                         match = $.matchSrcSet( devicePixelRatio, srcset );
124                                         if ( match !== null ) {
125                                                 $img.attr( 'src', match );
126                                         }
127                                 }
128                         } );
129                 }
131                 return $target;
132         };
134         /**
135          * Match a srcset entry for the given device pixel ratio
136          *
137          * Exposed for testing.
138          *
139          * @private
140          * @static
141          * @param {number} devicePixelRatio
142          * @param {string} srcset
143          * @return {Mixed} null or the matching src string
144          */
145         $.matchSrcSet = function ( devicePixelRatio, srcset ) {
146                 var candidates,
147                         candidate,
148                         bits,
149                         src,
150                         i,
151                         ratioStr,
152                         ratio,
153                         selectedRatio = 1,
154                         selectedSrc = null;
155                 candidates = srcset.split( / *, */ );
156                 for ( i = 0; i < candidates.length; i++ ) {
157                         candidate = candidates[ i ];
158                         bits = candidate.split( / +/ );
159                         src = bits[ 0 ];
160                         if ( bits.length > 1 && bits[ 1 ].charAt( bits[ 1 ].length - 1 ) === 'x' ) {
161                                 ratioStr = bits[ 1 ].slice( 0, -1 );
162                                 ratio = parseFloat( ratioStr );
163                                 if ( ratio <= devicePixelRatio && ratio > selectedRatio ) {
164                                         selectedRatio = ratio;
165                                         selectedSrc = src;
166                                 }
167                         }
168                 }
169                 return selectedSrc;
170         };
172         /**
173          * @class jQuery
174          * @mixins jQuery.plugin.hidpi
175          */
177 }( jQuery ) );