summaryrefslogtreecommitdiff
path: root/resources/jquery/jquery.hidpi.js
blob: 70bfc4ea3c664570102bd697140912fedfd98373 (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
/**
 * Responsive images based on 'srcset' and 'window.devicePixelRatio' emulation where needed.
 *
 * Call $().hidpi() on a document or part of a document to replace image srcs in that section.
 *
 * $.devicePixelRatio() can be used to supplement window.devicePixelRatio with support on
 * some additional browsers.
 */
( function ( $ ) {

/**
 * Detect 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.
 *
 * @method
 * @returns {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.
 *
 * @method
 * @returns {jQuery} This selection
 */
$.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
 *
 * @param {number} devicePixelRatio
 * @param {string} srcset
 * @return {mixed} null or the matching src string
 *
 * Exposed for testing.
 */
$.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].substr( 0, bits[1].length - 1 );
			ratio = parseFloat( ratioStr );
			if ( ratio <= devicePixelRatio && ratio > selectedRatio ) {
				selectedRatio = ratio;
				selectedSrc = src;
			}
		}
	}
	return selectedSrc;
};

}( jQuery ) );