├── .gitignore ├── LICENSE.txt ├── README.md ├── bower.json ├── media.match.js ├── media.match.min.js ├── package.json └── test.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Build and Release Folders 2 | bin/ 3 | bin-debug/ 4 | bin-release/ 5 | 6 | # Other files and folders 7 | .settings/ 8 | .DS_Store -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 WebLinc LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Media.match 2 | =========== 3 | 4 | Test css media queries in javascript. A faster polyfill for matchMedia support. Follow the project on Twitter [@mediamatchjs](https://twitter.com/mediamatchjs). 5 | 6 | Why? 7 | --- 8 | * **Browser support**: Tested in IE 6-9, Chrome, Firefox, Opera, Safari, iOS and Android 9 | * **Feature support**: Has all the basics + most of the spec http://www.w3.org/TR/css3-mediaqueries/ 10 | * **Speed**: In many browsers, ops/sec rival or exceed native matchMedia. See 'test' to run your own speed tests using JSLitmus or check out http://jsperf.com/matchmedia/11 and http://jsfiddle.net/wV9HZ/2/ 11 | * **Size**: 2.73KB minified (1.46KB gzipped) 12 | 13 | Media type and feature support 14 | --- 15 | * **type**: `all`, `screen`, `print`, `speech`, `projection`, `handheld`, `tv`, `braille`, `embossed`, `tty` 16 | * **width**: `width`, `min-width`, `max-width` 17 | * **height**: `height`, `min-height`, `max-height` 18 | * **device-width**: `device-width`, `min-device-width`, `max-device-width` 19 | * **device-height**: `device-height`, `min-device-height`, `max-device-height` 20 | * **aspect-ratio**: `aspect-ratio`, `min-aspect-ratio`, `max-aspect-ratio` 21 | * **device-aspect-ratio**: `device-aspect-ratio`, `min-device-aspect-ratio`, `max-device-aspect-ratio` 22 | * **orientation**: `orientation` 23 | * **resolution**: `resolution`, `min-resolution`, `max-resolution` 24 | * **device-pixel-ratio**: `device-pixel-ratio`, `min-device-pixel-ratio`, `max-device-pixel-ratio` 25 | * **color**: `color`, `min-color`, `max-color` 26 | * **color-index**: `color-index`, `min-color-index`, `max-color-index` 27 | 28 | ### Lacks support 29 | * **monochrome**: `monochrome`, `min-monochrome`, `max-monochrome` 30 | * **scan**: `scan` 31 | * **grid**: `grid` 32 | 33 | Requirements 34 | --- 35 | #### media.match.min.js/media.match.js 36 | * Provides core functionality. Does not contain external javascript library or css dependencies. 37 | * Version 1 of this project contained a css dependency that is now solely handled by media.match.js. See branch, "version1" for previous iteration. 38 | 39 | Example 40 | --- 41 | 42 | Both code blocks are valid uses of ```matchMedia()```. The first example shows the caching of a ```MediaQueryList``` object and the second shows an alternative usage as well as ```addListener``` support. 43 | The ```addListener``` method is part of the ```MediaQueryList``` object, therefore it can be added on the cached version or immediately after ```matchMedia()```. 44 | 45 | ``` 46 | 57 | ``` 58 | ``` 59 | 69 | ``` 70 | 71 | ## Related projects 72 | * [Nonresponsive](https://github.com/weblinc/nonresponsive): Media queries for the unsupportive IE6-8. 73 | * [Picture](https://github.com/weblinc/picture): Responsive images based on the 'picture' element proposal. 74 | * [Img-srcset](https://github.com/weblinc/img-srcset): Responsive images based on the 'srcset' attribute proposal. 75 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "media-match", 3 | "main" :"media.match.js", 4 | "homepage": "https://github.com/weblinc/media-match", 5 | "version": "2.0.3", 6 | "_release": "2.0.3", 7 | "_resolution": { 8 | "type": "version", 9 | "tag": "v2.0.3", 10 | "commit": "8edbaa89dff477ed13107f84b4e74700c17b9382" 11 | }, 12 | "_source": "git://github.com/weblinc/media-match.git", 13 | "_target": "~2.0.3", 14 | "_originalSource": "media-match" 15 | } 16 | -------------------------------------------------------------------------------- /media.match.js: -------------------------------------------------------------------------------- 1 | /*! MediaMatch v.2.0.3 - Testing css media queries in Javascript. Authors & copyright (c) 2013: WebLinc, David Knight. */ 2 | 3 | window.matchMedia || (window.matchMedia = function (win) { 4 | 'use strict'; 5 | 6 | // Internal globals 7 | var _doc = win.document, 8 | _viewport = _doc.documentElement, 9 | _queries = [], 10 | _queryID = 0, 11 | _type = '', 12 | _features = {}, 13 | // only screen 14 | // only screen and 15 | // not screen 16 | // not screen and 17 | // screen 18 | // screen and 19 | _typeExpr = /\s*(only|not)?\s*(screen|print|[a-z\-]+)\s*(and)?\s*/i, 20 | // (-vendor-min-width: 300px) 21 | // (min-width: 300px) 22 | // (width: 300px) 23 | // (width) 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*$/, 26 | _timer = 0, 27 | 28 | // Helper methods 29 | 30 | /* 31 | _matches 32 | */ 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, 37 | mqLength = mqIndex, 38 | mq = null, 39 | 40 | // not screen, screen 41 | negateType = null, 42 | negateTypeFound = '', 43 | negateTypeIndex = 0, 44 | negate = false, 45 | type = '', 46 | 47 | // (min-width: 400px), (min-width) 48 | exprListStr = '', 49 | exprList = null, 50 | exprIndex = 0, 51 | exprLength = 0, 52 | expr = null, 53 | 54 | prefix = '', 55 | length = '', 56 | unit = '', 57 | value = '', 58 | feature = '', 59 | 60 | match = false; 61 | 62 | if (media === '') { 63 | return true; 64 | } 65 | 66 | do { 67 | mq = mql[mqLength - mqIndex]; 68 | negate = false; 69 | negateType = mq.match(_typeExpr); 70 | 71 | if (negateType) { 72 | negateTypeFound = negateType[0]; 73 | negateTypeIndex = negateType.index; 74 | } 75 | 76 | if (!negateType || ((mq.substring(0, negateTypeIndex).indexOf('(') === -1) && (negateTypeIndex || (!negateType[3] && negateTypeFound !== negateType.input)))) { 77 | match = false; 78 | continue; 79 | } 80 | 81 | exprListStr = mq; 82 | 83 | negate = negateType[1] === 'not'; 84 | 85 | if (!negateTypeIndex) { 86 | type = negateType[2]; 87 | exprListStr = mq.substring(negateTypeFound.length); 88 | } 89 | 90 | // Test media type 91 | // Test type against this device or if 'all' or empty '' 92 | match = type === _type || type === 'all' || type === ''; 93 | 94 | exprList = (exprListStr.indexOf(' and ') !== -1 && exprListStr.split(' and ')) || [exprListStr]; 95 | exprIndex = exprList.length - 1; 96 | exprLength = exprIndex; 97 | 98 | if (match && exprIndex >= 0 && exprListStr !== '') { 99 | do { 100 | expr = exprList[exprIndex].match(_mediaExpr); 101 | 102 | if (!expr || !_features[expr[3]]) { 103 | match = false; 104 | break; 105 | } 106 | 107 | prefix = expr[2]; 108 | length = expr[5]; 109 | value = length; 110 | unit = expr[7]; 111 | feature = _features[expr[3]]; 112 | 113 | // Convert unit types 114 | if (unit) { 115 | if (unit === 'px') { 116 | // If unit is px 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 121 | value = 16 * length; 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 127 | value = length * 96; 128 | } else if (unit === 'dpcm') { 129 | // Convert resolution dpcm unit to pixels 130 | value = length * 0.3937; 131 | } else { 132 | // default 133 | value = Number(length); 134 | } 135 | } 136 | 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; 143 | } else if (value) { 144 | match = feature === value; 145 | } else { 146 | match = !!feature; 147 | } 148 | 149 | // If 'match' is false, break loop 150 | // Continue main loop through query list 151 | if (!match) { 152 | break; 153 | } 154 | } while (exprIndex--); 155 | } 156 | 157 | // If match is true, break loop 158 | // Once matched, no need to check other queries 159 | if (match) { 160 | break; 161 | } 162 | } while (mqIndex--); 163 | 164 | return negate ? !match : match; 165 | }, 166 | 167 | /* 168 | _setFeature 169 | */ 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; 178 | 179 | _features.width = w; 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); 185 | _features.color = c; 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; 190 | }, 191 | 192 | /* 193 | _watch 194 | */ 195 | _watch = function () { 196 | clearTimeout(_timer); 197 | 198 | _timer = setTimeout(function () { 199 | var query = null, 200 | qIndex = _queryID - 1, 201 | qLength = qIndex, 202 | match = false; 203 | 204 | if (qIndex >= 0) { 205 | _setFeature(); 206 | 207 | do { 208 | query = _queries[qLength - qIndex]; 209 | 210 | if (query) { 211 | match = _matches(query.mql.media); 212 | 213 | if ((match && !query.mql.matches) || (!match && query.mql.matches)) { 214 | query.mql.matches = match; 215 | 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); 220 | } 221 | } 222 | } 223 | } 224 | } 225 | } while(qIndex--); 226 | } 227 | 228 | 229 | }, 10); 230 | }, 231 | 232 | /* 233 | _init 234 | */ 235 | _init = function () { 236 | var head = _doc.getElementsByTagName('head')[0], 237 | style = _doc.createElement('style'), 238 | info = null, 239 | typeList = ['screen', 'print', 'speech', 'projection', 'handheld', 'tv', 'braille', 'embossed', 'tty'], 240 | typeIndex = 0, 241 | typeLength = typeList.length, 242 | cssText = '#mediamatchjs { position: relative; z-index: 0; }', 243 | eventPrefix = '', 244 | addEvent = win.addEventListener || (eventPrefix = 'on') && win.attachEvent; 245 | 246 | style.type = 'text/css'; 247 | style.id = 'mediamatchjs'; 248 | 249 | head.appendChild(style); 250 | 251 | // Must be placed after style is inserted into the DOM for IE 252 | info = (win.getComputedStyle && win.getComputedStyle(style)) || style.currentStyle; 253 | 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 + ' } }'; 257 | } 258 | 259 | // Add rules to style element 260 | if (style.styleSheet) { 261 | style.styleSheet.cssText = cssText; 262 | } else { 263 | style.textContent = cssText; 264 | } 265 | 266 | // Get media type 267 | _type = typeList[(info.zIndex * 1) || 0]; 268 | 269 | head.removeChild(style); 270 | 271 | _setFeature(); 272 | 273 | // Set up listeners 274 | addEvent(eventPrefix + 'resize', _watch, false); 275 | addEvent(eventPrefix + 'orientationchange', _watch, false); 276 | }; 277 | 278 | _init(); 279 | 280 | /* 281 | A list of parsed media queries, ex. screen and (max-width: 400px), screen and (max-width: 800px) 282 | */ 283 | return function (media) { 284 | var id = _queryID, 285 | mql = { 286 | matches : false, 287 | media : media, 288 | addListener : function addListener(listener) { 289 | _queries[id].listeners || (_queries[id].listeners = []); 290 | listener && _queries[id].listeners.push(listener); 291 | }, 292 | removeListener : function removeListener(listener) { 293 | var query = _queries[id], 294 | i = 0, 295 | il = 0; 296 | 297 | if (!query) { 298 | return; 299 | } 300 | 301 | il = query.listeners.length; 302 | 303 | for ( ; i < il; i++) { 304 | if (query.listeners[i] === listener) { 305 | query.listeners.splice(i, 1); 306 | } 307 | } 308 | } 309 | }; 310 | 311 | if (media === '') { 312 | mql.matches = true; 313 | return mql; 314 | } 315 | 316 | mql.matches = _matches(media); 317 | 318 | _queryID = _queries.push({ 319 | mql : mql, 320 | listeners : null 321 | }); 322 | 323 | return mql; 324 | }; 325 | }(window)); 326 | -------------------------------------------------------------------------------- /media.match.min.js: -------------------------------------------------------------------------------- 1 | /*! MediaMatch v.2.0.3 - Testing css media queries in Javascript. Authors & copyright (c) 2013: WebLinc, David Knight. */ 2 | window.matchMedia||(window.matchMedia=function(e){"use strict";var t=e.document,i=t.documentElement,n=[],s=0,r="",l={},a=/\s*(only|not)?\s*(screen|print|[a-z\-]+)\s*(and)?\s*/i,c=/^\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*$/,d=0,o=function(e){var t=-1!==e.indexOf(",")&&e.split(",")||[e],i=t.length-1,n=i,s=null,d=null,o="",m=0,h=!1,p="",u="",f=null,x=0,v=0,g=null,w="",y="",b="",z="",q="",C=!1;if(""===e)return!0;do if(s=t[n-i],h=!1,d=s.match(a),d&&(o=d[0],m=d.index),!d||-1===s.substring(0,m).indexOf("(")&&(m||!d[3]&&o!==d.input))C=!1;else{if(u=s,h="not"===d[1],m||(p=d[2],u=s.substring(o.length)),C=p===r||"all"===p||""===p,f=-1!==u.indexOf(" and ")&&u.split(" and ")||[u],x=f.length-1,v=x,C&&x>=0&&""!==u)do{if(g=f[x].match(c),!g||!l[g[3]]){C=!1;break}if(w=g[2],y=g[5],z=y,b=g[7],q=l[g[3]],b&&(z="px"===b?Number(y):"em"===b||"rem"===b?16*y:g[8]?(y/g[8]).toFixed(2):"dppx"===b?96*y:"dpcm"===b?.3937*y:Number(y)),C="min-"===w&&z?q>=z:"max-"===w&&z?z>=q:z?q===z:!!q,!C)break}while(x--);if(C)break}while(i--);return h?!C:C},m=function(){var t=e.innerWidth||i.clientWidth,n=e.innerHeight||i.clientHeight,s=e.screen.width,r=e.screen.height,a=e.screen.colorDepth,c=e.devicePixelRatio;l.width=t,l.height=n,l["aspect-ratio"]=(t/n).toFixed(2),l["device-width"]=s,l["device-height"]=r,l["device-aspect-ratio"]=(s/r).toFixed(2),l.color=a,l["color-index"]=Math.pow(2,a),l.orientation=n>=t?"portrait":"landscape",l.resolution=c&&96*c||e.screen.deviceXDPI||96,l["device-pixel-ratio"]=c||1},h=function(){clearTimeout(d),d=setTimeout(function(){var t=null,i=s-1,r=i,l=!1;if(i>=0){m();do if(t=n[r-i],t&&(l=o(t.mql.media),(l&&!t.mql.matches||!l&&t.mql.matches)&&(t.mql.matches=l,t.listeners)))for(var a=0,c=t.listeners.length;c>a;a++)t.listeners[a]&&t.listeners[a].call(e,t.mql);while(i--)}},10)},p=function(){var i=t.getElementsByTagName("head")[0],n=t.createElement("style"),s=null,l=["screen","print","speech","projection","handheld","tv","braille","embossed","tty"],a=0,c=l.length,d="#mediamatchjs { position: relative; z-index: 0; }",o="",p=e.addEventListener||(o="on")&&e.attachEvent;for(n.type="text/css",n.id="mediamatchjs",i.appendChild(n),s=e.getComputedStyle&&e.getComputedStyle(n)||n.currentStyle;c>a;a++)d+="@media "+l[a]+" { #mediamatchjs { position: relative; z-index: "+a+" } }";n.styleSheet?n.styleSheet.cssText=d:n.textContent=d,r=l[1*s.zIndex||0],i.removeChild(n),m(),p(o+"resize",h,!1),p(o+"orientationchange",h,!1)};return p(),function(e){var t=s,i={matches:!1,media:e,addListener:function(e){n[t].listeners||(n[t].listeners=[]),e&&n[t].listeners.push(e)},removeListener:function(e){var i=n[t],s=0,r=0;if(i)for(r=i.listeners.length;r>s;s++)i.listeners[s]===e&&i.listeners.splice(s,1)}};return""===e?(i.matches=!0,i):(i.matches=o(e),s=n.push({mql:i,listeners:null}),i)}}(window)); 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "media-match", 3 | "version": "2.0.3", 4 | "description": "Test css media queries in javascript. A faster polyfill for matchMedia support.", 5 | "main": "media.match.js", 6 | "repository": "git@github.com:weblinc/media-match.git", 7 | "author": "David Knight ", 8 | "license": "MIT" 9 | } 10 | -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 28 | 29 | 30 | --------------------------------------------------------------------------------