summaryrefslogtreecommitdiff
path: root/resources/src/jquery/jquery.hidpi.js
blob: 8fca05677e9e788c7170c8735deb8f669dba0684 (plain)
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/**
 * Responsive images based on `srcset` and `window.devicePixelRatio` emulation where needed.
 *
 * Call `.hidpi()` on a document or part of a document to proces image srcsets within that section.
 *
 * `$.devicePixelRatio()` can be used as a substitute for `window.devicePixelRatio`.
 * It provides a familiar interface to retrieve the pixel ratio for browsers that don't
 * implement `window.devicePixelRatio` but do have a different way of getting it.
 *
 * @class jQuery.plugin.hidpi
 */
( function ( $ ) {

/**
 * Get reported or approximate device pixel ratio.
 *
 * - 1.0 means 1 CSS pixel is 1 hardware pixel
 * - 2.0 means 1 CSS pixel is 2 hardware pixels
 * - etc.
 *
 * Uses `window.devicePixelRatio` if available, or CSS media queries on IE.
 *
 * @static
 * @inheritable
 * @return {number} Device pixel ratio
 */
$.devicePixelRatio = function () {
	if ( window.devicePixelRatio !== undefined ) {
		// Most web browsers:
		// * WebKit (Safari, Chrome, Android browser, etc)
		// * Opera
		// * Firefox 18+
		return window.devicePixelRatio;
	} else if ( window.msMatchMedia !== undefined ) {
		// Windows 8 desktops / tablets, probably Windows Phone 8
		//
		// IE 10 doesn't report pixel ratio directly, but we can get the
		// screen DPI and divide by 96. We'll bracket to [1, 1.5, 2.0] for
		// simplicity, but you may get different values depending on zoom
		// factor, size of screen and orientation in Metro IE.
		if ( window.msMatchMedia( '(min-resolution: 192dpi)' ).matches ) {
			return 2;
		} else if ( window.msMatchMedia( '(min-resolution: 144dpi)' ).matches ) {
			return 1.5;
		} else {
			return 1;
		}
	} else {
		// Legacy browsers...
		// Assume 1 if unknown.
		return 1;
	}
};

/**
 * Implement responsive images based on srcset attributes, if browser has no
 * native srcset support.
 *
 * @return {jQuery} This selection
 * @chainable
 */
$.fn.hidpi = function () {
	var $target = this,
		// @todo add support for dpi media query checks on Firefox, IE
		devicePixelRatio = $.devicePixelRatio(),
		testImage = new Image();

	if ( devicePixelRatio > 1 && testImage.srcset === undefined ) {
		// No native srcset support.
		$target.find( 'img' ).each( function () {
			var $img = $( this ),
				srcset = $img.attr( 'srcset' ),
				match;
			if ( typeof srcset === 'string' && srcset !== '' ) {
				match = $.matchSrcSet( devicePixelRatio, srcset );
				if ( match !== null ) {
					$img.attr( 'src', match );
				}
			}
		} );
	}

	return $target;
};

/**
 * Match a srcset entry for the given device pixel ratio
 *
 * Exposed for testing.
 *
 * @private
 * @static
 * @param {number} devicePixelRatio
 * @param {string} srcset
 * @return {Mixed} null or the matching src string
 */
$.matchSrcSet = function ( devicePixelRatio, srcset ) {
	var candidates,
		candidate,
		bits,
		src,
		i,
		ratioStr,
		ratio,
		selectedRatio = 1,
		selectedSrc = null;
	candidates = srcset.split( / *, */ );
	for ( i = 0; i < candidates.length; i++ ) {
		candidate = candidates[i];
		bits = candidate.split( / +/ );
		src = bits[0];
		if ( bits.length > 1 && bits[1].charAt( bits[1].length - 1 ) === 'x' ) {
			ratioStr = bits[1].slice( 0, -1 );
			ratio = parseFloat( ratioStr );
			if ( ratio <= devicePixelRatio && ratio > selectedRatio ) {
				selectedRatio = ratio;
				selectedSrc = src;
			}
		}
	}
	return selectedSrc;
};

/**
 * @class jQuery
 * @mixins jQuery.plugin.hidpi
 */

}( jQuery ) );