1 /* MediaMatch v.2.0.2 - Testing css media queries in Javascript. Authors & copyright (c) 2013: WebLinc, David Knight. */
3 window.matchMedia || (window.matchMedia = function (win) {
7 var _doc = win.document,
8 _viewport = _doc.documentElement,
19 _typeExpr = /\s*(only|not)?\s*(screen|print|[a-z\-]+)\s*(and)?\s*/i,
20 // (-vendor-min-width: 300px)
24 // (orientation: portrait|landscape)
25 _mediaExpr = /^\s*\(\s*(-[a-z]+-)?(min-|max-)?([a-z\-]+)\s*(:?\s*([0-9]+(\.[0-9]+)?|portrait|landscape)(px|em|dppx|dpcm|rem|%|in|cm|mm|ex|pt|pc|\/([0-9]+(\.[0-9]+)?))?)?\s*\)\s*$/,
33 _matches = function (media) {
34 // screen and (min-width: 400px), screen and (max-width: 500px)
35 var mql = (media.indexOf(',') !== -1 && media.split(',')) || [media],
36 mqIndex = mql.length - 1,
47 // (min-width: 400px), (min-width)
67 mq = mql[mqLength - mqIndex];
69 negateType = mq.match(_typeExpr);
72 negateTypeFound = negateType[0];
73 negateTypeIndex = negateType.index;
76 if (!negateType || ((mq.substring(0, negateTypeIndex).indexOf('(') === -1) && (negateTypeIndex || (!negateType[3] && negateTypeFound !== negateType.input)))) {
83 negate = negateType[1] === 'not';
85 if (!negateTypeIndex) {
87 exprListStr = mq.substring(negateTypeFound.length);
91 // Test type against this device or if 'all' or empty ''
92 match = type === _type || type === 'all' || type === '';
94 exprList = (exprListStr.indexOf(' and ') !== -1 && exprListStr.split(' and ')) || [exprListStr];
95 exprIndex = exprList.length - 1;
96 exprLength = exprIndex;
98 if (match && exprIndex >= 0 && exprListStr !== '') {
100 expr = exprList[exprIndex].match(_mediaExpr);
102 if (!expr || !_features[expr[3]]) {
111 feature = _features[expr[3]];
113 // Convert unit types
117 value = Number(length);
118 } else if (unit === 'em' || unit === 'rem') {
119 // Convert relative length unit to pixels
120 // Assumed base font size is 16px
122 } else if (expr[8]) {
123 // Convert aspect ratio to decimal
124 value = (length / expr[8]).toFixed(2);
125 } else if (unit === 'dppx') {
126 // Convert resolution dppx unit to pixels
128 } else if (unit === 'dpcm') {
129 // Convert resolution dpcm unit to pixels
130 value = length * 0.3937;
133 value = Number(length);
137 // Test for prefix min or max
138 // Test value against feature
139 if (prefix === 'min-' && value) {
140 match = feature >= value;
141 } else if (prefix === 'max-' && value) {
142 match = feature <= value;
144 match = feature === value;
149 // If 'match' is false, break loop
150 // Continue main loop through query list
154 } while (exprIndex--);
157 // If match is true, break loop
158 // Once matched, no need to check other queries
164 return negate ? !match : match;
170 _setFeature = function () {
171 // Sets properties of '_features' that change on resize and/or orientation.
172 var w = win.innerWidth || _viewport.clientWidth,
173 h = win.innerHeight || _viewport.clientHeight,
174 dw = win.screen.width,
175 dh = win.screen.height,
176 c = win.screen.colorDepth,
177 x = win.devicePixelRatio;
180 _features.height = h;
181 _features['aspect-ratio'] = (w / h).toFixed(2);
182 _features['device-width'] = dw;
183 _features['device-height'] = dh;
184 _features['device-aspect-ratio'] = (dw / dh).toFixed(2);
186 _features['color-index'] = Math.pow(2, c);
187 _features.orientation = (h >= w ? 'portrait' : 'landscape');
188 _features.resolution = (x && x * 96) || win.screen.deviceXDPI || 96;
189 _features['device-pixel-ratio'] = x || 1;
195 _watch = function () {
196 clearTimeout(_timer);
198 _timer = setTimeout(function () {
200 qIndex = _queryID - 1,
208 query = _queries[qLength - qIndex];
211 match = _matches(query.mql.media);
213 if ((match && !query.mql.matches) || (!match && query.mql.matches)) {
214 query.mql.matches = match;
216 if (query.listeners) {
217 for (var i = 0, il = query.listeners.length; i < il; i++) {
218 if (query.listeners[i]) {
219 query.listeners[i].call(win, query.mql);
235 _init = function () {
236 var head = _doc.getElementsByTagName('head')[0],
237 style = _doc.createElement('style'),
239 typeList = ['screen', 'print', 'speech', 'projection', 'handheld', 'tv', 'braille', 'embossed', 'tty'],
241 typeLength = typeList.length,
242 cssText = '#mediamatchjs { position: relative; z-index: 0; }',
244 addEvent = win.addEventListener || (eventPrefix = 'on') && win.attachEvent;
246 style.type = 'text/css';
247 style.id = 'mediamatchjs';
249 head.appendChild(style);
251 // Must be placed after style is inserted into the DOM for IE
252 info = (win.getComputedStyle && win.getComputedStyle(style)) || style.currentStyle;
254 // Create media blocks to test for media type
255 for ( ; typeIndex < typeLength; typeIndex++) {
256 cssText += '@media ' + typeList[typeIndex] + ' { #mediamatchjs { position: relative; z-index: ' + typeIndex + ' } }';
259 // Add rules to style element
260 if (style.styleSheet) {
261 style.styleSheet.cssText = cssText;
263 style.textContent = cssText;
267 _type = typeList[(info.zIndex * 1) || 0];
269 head.removeChild(style);
274 addEvent(eventPrefix + 'resize', _watch);
275 addEvent(eventPrefix + 'orientationchange', _watch);
281 A list of parsed media queries, ex. screen and (max-width: 400px), screen and (max-width: 800px)
283 return function (media) {
288 addListener : function addListener(listener) {
289 _queries[id].listeners || (_queries[id].listeners = []);
290 listener && _queries[id].listeners.push(listener);
292 removeListener : function removeListener(listener) {
293 var query = _queries[id],
301 il = query.listeners.length;
303 for ( ; i < il; i++) {
304 if (query.listeners[i] === listener) {
305 query.listeners.splice(i, 1);
316 mql.matches = _matches(media);
318 _queryID = _queries.push({