1 /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */
2 /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */
4 window.matchMedia = window.matchMedia || (function( doc, undefined ) {
9 docElem = doc.documentElement,
10 refNode = docElem.firstElementChild || docElem.firstChild,
11 // fakeBody required for <FF4 when executed in <head>
12 fakeBody = doc.createElement( "body" ),
13 div = doc.createElement( "div" );
16 div.style.cssText = "position:absolute;top:-100em";
17 fakeBody.style.background = "none";
18 fakeBody.appendChild(div);
22 div.innerHTML = "­<style media=\"" + q + "\"> #mq-test-1 { width: 42px; }</style>";
24 docElem.insertBefore( fakeBody, refNode );
25 bool = div.offsetWidth === 42;
26 docElem.removeChild( fakeBody );
41 /*! Respond.js v1.1.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */
48 win.respond = respond;
50 //define update even in native-mq-supporting browsers, to avoid errors
51 respond.update = function(){};
53 //expose media query support flag for external use
54 respond.mediaQueriesSupported = win.matchMedia && win.matchMedia( "only all" ).matches;
56 //if media queries are supported, exit here
57 if( respond.mediaQueriesSupported ){
62 var doc = win.document,
63 docElem = doc.documentElement,
69 head = doc.getElementsByTagName( "head" )[0] || docElem,
70 base = doc.getElementsByTagName( "base" )[0],
71 links = head.getElementsByTagName( "link" ),
74 //loop stylesheets, send text content to translate
77 for( var i = 0; i < links.length; i++ ){
78 var sheet = links[ i ],
81 isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet";
83 //only links plz and prevent re-parsing
84 if( !!href && isCSS && !parsedSheets[ href ] ){
85 // selectivizr exposes css through the rawCssText expando
86 if (sheet.styleSheet && sheet.styleSheet.rawCssText) {
87 translate( sheet.styleSheet.rawCssText, href, media );
88 parsedSheets[ href ] = true;
90 if( (!/^([a-zA-Z:]*\/\/)/.test( href ) && !base) ||
91 href.replace( RegExp.$1, "" ).split( "/" )[0] === win.location.host ){
103 //recurse through request queue, get css text
104 makeRequests = function(){
105 if( requestQueue.length ){
106 var thisRequest = requestQueue.shift();
108 ajax( thisRequest.href, function( styles ){
109 translate( styles, thisRequest.href, thisRequest.media );
110 parsedSheets[ thisRequest.href ] = true;
112 // by wrapping recursive function call in setTimeout
113 // we prevent "Stack overflow" error in IE7
114 win.setTimeout(function(){ makeRequests(); },0);
119 //find media blocks in css text, convert to style blocks
120 translate = function( styles, href, media ){
121 var qs = styles.match( /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi ),
122 ql = qs && qs.length || 0;
124 //try to get CSS path
125 href = href.substring( 0, href.lastIndexOf( "/" ) );
127 var repUrls = function( css ){
128 return css.replace( /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g, "$1" + href + "$2$3" );
130 useMedia = !ql && media;
132 //if path exists, tack on trailing slash
133 if( href.length ){ href += "/"; }
135 //if no internal queries exist, but media attr does, use that
136 //note: this currently lacks support for situations where a media attr is specified on a link AND
137 //its associated stylesheet has internal CSS media queries.
138 //In those cases, the media attribute will currently be ignored.
143 for( var i = 0; i < ql; i++ ){
144 var fullq, thisq, eachq, eql;
149 rules.push( repUrls( styles ) );
153 fullq = qs[ i ].match( /@media *([^\{]+)\{([\S\s]+?)$/ ) && RegExp.$1;
154 rules.push( RegExp.$2 && repUrls( RegExp.$2 ) );
157 eachq = fullq.split( "," );
160 for( var j = 0; j < eql; j++ ){
163 media : thisq.split( "(" )[ 0 ].match( /(only\s+)?([a-zA-Z]+)\s?/ ) && RegExp.$2 || "all",
164 rules : rules.length - 1,
165 hasquery : thisq.indexOf("(") > -1,
166 minw : thisq.match( /\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ),
167 maxw : thisq.match( /\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" )
179 // returns the value of 1em in pixels
180 getEmValue = function() {
182 div = doc.createElement('div'),
186 div.style.cssText = "position:absolute;font-size:1em;width:1em";
189 body = fakeUsed = doc.createElement( "body" );
190 body.style.background = "none";
193 body.appendChild( div );
195 docElem.insertBefore( body, docElem.firstChild );
197 ret = div.offsetWidth;
200 docElem.removeChild( body );
203 body.removeChild( div );
206 //also update eminpx before returning
207 ret = eminpx = parseFloat(ret);
212 //cached container for 1em value, populated the first time it's needed
215 //enable/disable styles
216 applyMedia = function( fromResize ){
217 var name = "clientWidth",
218 docElemProp = docElem[ name ],
219 currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[ name ] || docElemProp,
221 lastLink = links[ links.length-1 ],
222 now = (new Date()).getTime();
224 //throttle resize calls
225 if( fromResize && lastCall && now - lastCall < resizeThrottle ){
226 win.clearTimeout( resizeDefer );
227 resizeDefer = win.setTimeout( applyMedia, resizeThrottle );
234 for( var i in mediastyles ){
235 if( mediastyles.hasOwnProperty( i ) ){
236 var thisstyle = mediastyles[ i ],
237 min = thisstyle.minw,
238 max = thisstyle.maxw,
239 minnull = min === null,
240 maxnull = max === null,
244 min = parseFloat( min ) * ( min.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 );
247 max = parseFloat( max ) * ( max.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 );
250 // if there's no media query at all (the () part), or min or max is not null, and if either is present, they're true
251 if( !thisstyle.hasquery || ( !minnull || !maxnull ) && ( minnull || currWidth >= min ) && ( maxnull || currWidth <= max ) ){
252 if( !styleBlocks[ thisstyle.media ] ){
253 styleBlocks[ thisstyle.media ] = [];
255 styleBlocks[ thisstyle.media ].push( rules[ thisstyle.rules ] );
260 //remove any existing respond style element(s)
261 for( var j in appendedEls ){
262 if( appendedEls.hasOwnProperty( j ) ){
263 if( appendedEls[ j ] && appendedEls[ j ].parentNode === head ){
264 head.removeChild( appendedEls[ j ] );
269 //inject active styles, grouped by media type
270 for( var k in styleBlocks ){
271 if( styleBlocks.hasOwnProperty( k ) ){
272 var ss = doc.createElement( "style" ),
273 css = styleBlocks[ k ].join( "\n" );
275 ss.type = "text/css";
278 //originally, ss was appended to a documentFragment and sheets were appended in bulk.
279 //this caused crashes in IE in a number of circumstances, such as when the HTML element had a bg image set, so appending beforehand seems best. Thanks to @dvelyk for the initial research on this one!
280 head.insertBefore( ss, lastLink.nextSibling );
282 if ( ss.styleSheet ){
283 ss.styleSheet.cssText = css;
286 ss.appendChild( doc.createTextNode( css ) );
289 //push to appendedEls to track for later removal
290 appendedEls.push( ss );
294 //tweaked Ajax functions from Quirksmode
295 ajax = function( url, callback ) {
300 req.open( "GET", url, true );
301 req.onreadystatechange = function () {
302 if ( req.readyState !== 4 || req.status !== 200 && req.status !== 304 ){
305 callback( req.responseText );
307 if ( req.readyState === 4 ){
313 xmlHttp = (function() {
314 var xmlhttpmethod = false;
316 xmlhttpmethod = new win.XMLHttpRequest();
319 xmlhttpmethod = new win.ActiveXObject( "Microsoft.XMLHTTP" );
322 return xmlhttpmethod;
329 //expose update for re-running respond later on
330 respond.update = ripCSS;
333 function callMedia(){
336 if( win.addEventListener ){
337 win.addEventListener( "resize", callMedia, false );
339 else if( win.attachEvent ){
340 win.attachEvent( "onresize", callMedia );