|
|
Line 4: |
Line 4: |
| /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ | | /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ |
| /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ | | /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ |
- | window.matchMedia = window.matchMedia || (function(doc, undefined){ | + | window.matchMedia=window.matchMedia||(function(e,f){var c,a=e.documentElement,b=a.firstElementChild||a.firstChild,d=e.createElement("body"),g=e.createElement("div");g.id="mq-test-1";g.style.cssText="position:absolute;top:-100em";d.style.background="none";d.appendChild(g);return function(h){g.innerHTML='­<style media="'+h+'"> #mq-test-1 { width: 42px; }</style>';a.insertBefore(d,b);c=g.offsetWidth==42;a.removeChild(d);return{matches:c,media:h}}})(document); |
- |
| + | |
- | var bool,
| + | |
- | docElem = doc.documentElement,
| + | |
- | refNode = docElem.firstElementChild || docElem.firstChild,
| + | |
- | // fakeBody required for <FF4 when executed in <head>
| + | |
- | fakeBody = doc.createElement('body'),
| + | |
- | div = doc.createElement('div');
| + | |
- |
| + | |
- | div.id = 'mq-test-1';
| + | |
- | div.style.cssText = "position:absolute;top:-100em";
| + | |
- | fakeBody.style.background = "none";
| + | |
- | fakeBody.appendChild(div);
| + | |
- |
| + | |
- | return function(q){
| + | |
- |
| + | |
- | div.innerHTML = '­<style media="'+q+'"> #mq-test-1 { width: 42px; }</style>';
| + | |
- |
| + | |
- | docElem.insertBefore(fakeBody, refNode);
| + | |
- | bool = div.offsetWidth == 42;
| + | |
- | docElem.removeChild(fakeBody);
| + | |
- |
| + | |
- | return { matches: bool, media: q };
| + | |
- | };
| + | |
- |
| + | |
- | })(document); | + | |
- | | + | |
- | | + | |
- | | + | |
| | | |
| /*! Respond.js v1.1.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ | | /*! Respond.js v1.1.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ |
- | (function( win ){ | + | (function(e){e.respond={};respond.update=function(){};respond.mediaQueriesSupported=e.matchMedia&&e.matchMedia("only all").matches;if(respond.mediaQueriesSupported){return}var w=e.document,s=w.documentElement,i=[],k=[],q=[],o={},h=30,f=w.getElementsByTagName("head")[0]||s,g=w.getElementsByTagName("base")[0],b=f.getElementsByTagName("link"),d=[],a=function(){var D=b,y=D.length,B=0,A,z,C,x;for(;B<y;B++){A=D[B],z=A.href,C=A.media,x=A.rel&&A.rel.toLowerCase()==="stylesheet";if(!!z&&x&&!o[z]){if(A.styleSheet&&A.styleSheet.rawCssText){m(A.styleSheet.rawCssText,z,C);o[z]=true}else{if((!/^([a-zA-Z:]*\/\/)/.test(z)&&!g)||z.replace(RegExp.$1,"").split("/")[0]===e.location.host){d.push({href:z,media:C})}}}}u()},u=function(){if(d.length){var x=d.shift();n(x.href,function(y){m(y,x.href,x.media);o[x.href]=true;u()})}},m=function(I,x,z){var G=I.match(/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi),J=G&&G.length||0,x=x.substring(0,x.lastIndexOf("/")),y=function(K){return K.replace(/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,"$1"+x+"$2$3")},A=!J&&z,D=0,C,E,F,B,H;if(x.length){x+="/"}if(A){J=1}for(;D<J;D++){C=0;if(A){E=z;k.push(y(I))}else{E=G[D].match(/@media *([^\{]+)\{([\S\s]+?)$/)&&RegExp.$1;k.push(RegExp.$2&&y(RegExp.$2))}B=E.split(",");H=B.length;for(;C<H;C++){F=B[C];i.push({media:F.split("(")[0].match(/(only\s+)?([a-zA-Z]+)\s?/)&&RegExp.$2||"all",rules:k.length-1,hasquery:F.indexOf("(")>-1,minw:F.match(/\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:F.match(/\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}}j()},l,r,v=function(){var z,A=w.createElement("div"),x=w.body,y=false;A.style.cssText="position:absolute;font-size:1em;width:1em";if(!x){x=y=w.createElement("body");x.style.background="none"}x.appendChild(A);s.insertBefore(x,s.firstChild);z=A.offsetWidth;if(y){s.removeChild(x)}else{x.removeChild(A)}z=p=parseFloat(z);return z},p,j=function(I){var x="clientWidth",B=s[x],H=w.compatMode==="CSS1Compat"&&B||w.body[x]||B,D={},G=b[b.length-1],z=(new Date()).getTime();if(I&&l&&z-l<h){clearTimeout(r);r=setTimeout(j,h);return}else{l=z}for(var E in i){var K=i[E],C=K.minw,J=K.maxw,A=C===null,L=J===null,y="em";if(!!C){C=parseFloat(C)*(C.indexOf(y)>-1?(p||v()):1)}if(!!J){J=parseFloat(J)*(J.indexOf(y)>-1?(p||v()):1)}if(!K.hasquery||(!A||!L)&&(A||H>=C)&&(L||H<=J)){if(!D[K.media]){D[K.media]=[]}D[K.media].push(k[K.rules])}}for(var E in q){if(q[E]&&q[E].parentNode===f){f.removeChild(q[E])}}for(var E in D){var M=w.createElement("style"),F=D[E].join("\n");M.type="text/css";M.media=E;f.insertBefore(M,G.nextSibling);if(M.styleSheet){M.styleSheet.cssText=F}else{M.appendChild(w.createTextNode(F))}q.push(M)}},n=function(x,z){var y=c();if(!y){return}y.open("GET",x,true);y.onreadystatechange=function(){if(y.readyState!=4||y.status!=200&&y.status!=304){return}z(y.responseText)};if(y.readyState==4){return}y.send(null)},c=(function(){var x=false;try{x=new XMLHttpRequest()}catch(y){x=new ActiveXObject("Microsoft.XMLHTTP")}return function(){return x}})();a();respond.update=a;function t(){j(true)}if(e.addEventListener){e.addEventListener("resize",t,false)}else{if(e.attachEvent){e.attachEvent("onresize",t)}}})(this); |
- | //exposed namespace
| + | |
- | win.respond = {};
| + | |
- |
| + | |
- | //define update even in native-mq-supporting browsers, to avoid errors
| + | |
- | respond.update = function(){};
| + | |
- |
| + | |
- | //expose media query support flag for external use
| + | |
- | respond.mediaQueriesSupported = win.matchMedia && win.matchMedia( "only all" ).matches;
| + | |
- |
| + | |
- | //if media queries are supported, exit here
| + | |
- | if( respond.mediaQueriesSupported ){ return; }
| + | |
- |
| + | |
- | //define vars
| + | |
- | var doc = win.document,
| + | |
- | docElem = doc.documentElement,
| + | |
- | mediastyles = [],
| + | |
- | rules = [],
| + | |
- | appendedEls = [],
| + | |
- | parsedSheets = {},
| + | |
- | resizeThrottle = 30,
| + | |
- | head = doc.getElementsByTagName( "head" )[0] || docElem,
| + | |
- | base = doc.getElementsByTagName( "base" )[0],
| + | |
- | links = head.getElementsByTagName( "link" ),
| + | |
- | requestQueue = [],
| + | |
- |
| + | |
- | //loop stylesheets, send text content to translate
| + | |
- | ripCSS = function(){
| + | |
- | var sheets = links,
| + | |
- | sl = sheets.length,
| + | |
- | i = 0,
| + | |
- | //vars for loop:
| + | |
- | sheet, href, media, isCSS;
| + | |
- | | + | |
- | for( ; i < sl; i++ ){
| + | |
- | sheet = sheets[ i ],
| + | |
- | href = sheet.href,
| + | |
- | media = sheet.media,
| + | |
- | isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet";
| + | |
- | | + | |
- | //only links plz and prevent re-parsing
| + | |
- | if( !!href && isCSS && !parsedSheets[ href ] ){
| + | |
- | // selectivizr exposes css through the rawCssText expando
| + | |
- | if (sheet.styleSheet && sheet.styleSheet.rawCssText) {
| + | |
- | translate( sheet.styleSheet.rawCssText, href, media );
| + | |
- | parsedSheets[ href ] = true;
| + | |
- | } else {
| + | |
- | if( (!/^([a-zA-Z:]*\/\/)/.test( href ) && !base)
| + | |
- | || href.replace( RegExp.$1, "" ).split( "/" )[0] === win.location.host ){
| + | |
- | requestQueue.push( {
| + | |
- | href: href,
| + | |
- | media: media
| + | |
- | } );
| + | |
- | }
| + | |
- | }
| + | |
- | }
| + | |
- | }
| + | |
- | makeRequests();
| + | |
- | },
| + | |
- |
| + | |
- | //recurse through request queue, get css text
| + | |
- | makeRequests = function(){
| + | |
- | if( requestQueue.length ){
| + | |
- | var thisRequest = requestQueue.shift();
| + | |
- |
| + | |
- | ajax( thisRequest.href, function( styles ){
| + | |
- | translate( styles, thisRequest.href, thisRequest.media );
| + | |
- | parsedSheets[ thisRequest.href ] = true;
| + | |
- | makeRequests();
| + | |
- | } );
| + | |
- | }
| + | |
- | },
| + | |
- |
| + | |
- | //find media blocks in css text, convert to style blocks
| + | |
- | translate = function( styles, href, media ){
| + | |
- | var qs = styles.match( /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi ),
| + | |
- | ql = qs && qs.length || 0,
| + | |
- | //try to get CSS path
| + | |
- | href = href.substring( 0, href.lastIndexOf( "/" )),
| + | |
- | repUrls = function( css ){
| + | |
- | return css.replace( /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g, "$1" + href + "$2$3" );
| + | |
- | },
| + | |
- | useMedia = !ql && media,
| + | |
- | //vars used in loop
| + | |
- | i = 0,
| + | |
- | j, fullq, thisq, eachq, eql;
| + | |
- | | + | |
- | //if path exists, tack on trailing slash
| + | |
- | if( href.length ){ href += "/"; }
| + | |
- |
| + | |
- | //if no internal queries exist, but media attr does, use that
| + | |
- | //note: this currently lacks support for situations where a media attr is specified on a link AND
| + | |
- | //its associated stylesheet has internal CSS media queries.
| + | |
- | //In those cases, the media attribute will currently be ignored.
| + | |
- | if( useMedia ){
| + | |
- | ql = 1;
| + | |
- | }
| + | |
- |
| + | |
- | | + | |
- | for( ; i < ql; i++ ){
| + | |
- | j = 0;
| + | |
- |
| + | |
- | //media attr
| + | |
- | if( useMedia ){
| + | |
- | fullq = media;
| + | |
- | rules.push( repUrls( styles ) );
| + | |
- | }
| + | |
- | //parse for styles
| + | |
- | else{
| + | |
- | fullq = qs[ i ].match( /@media *([^\{]+)\{([\S\s]+?)$/ ) && RegExp.$1;
| + | |
- | rules.push( RegExp.$2 && repUrls( RegExp.$2 ) );
| + | |
- | }
| + | |
- |
| + | |
- | eachq = fullq.split( "," );
| + | |
- | eql = eachq.length;
| + | |
- |
| + | |
- | for( ; j < eql; j++ ){
| + | |
- | thisq = eachq[ j ];
| + | |
- | mediastyles.push( {
| + | |
- | media : thisq.split( "(" )[ 0 ].match( /(only\s+)?([a-zA-Z]+)\s?/ ) && RegExp.$2 || "all",
| + | |
- | rules : rules.length - 1,
| + | |
- | hasquery: thisq.indexOf("(") > -1,
| + | |
- | minw : thisq.match( /\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ),
| + | |
- | maxw : thisq.match( /\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" )
| + | |
- | } );
| + | |
- | }
| + | |
- | }
| + | |
- | | + | |
- | applyMedia();
| + | |
- | },
| + | |
- |
| + | |
- | lastCall,
| + | |
- |
| + | |
- | resizeDefer,
| + | |
- |
| + | |
- | // returns the value of 1em in pixels
| + | |
- | getEmValue = function() {
| + | |
- | var ret,
| + | |
- | div = doc.createElement('div'),
| + | |
- | body = doc.body,
| + | |
- | fakeUsed = false;
| + | |
- |
| + | |
- | div.style.cssText = "position:absolute;font-size:1em;width:1em";
| + | |
- |
| + | |
- | if( !body ){
| + | |
- | body = fakeUsed = doc.createElement( "body" );
| + | |
- | body.style.background = "none";
| + | |
- | }
| + | |
- |
| + | |
- | body.appendChild( div );
| + | |
- |
| + | |
- | docElem.insertBefore( body, docElem.firstChild );
| + | |
- |
| + | |
- | ret = div.offsetWidth;
| + | |
- |
| + | |
- | if( fakeUsed ){
| + | |
- | docElem.removeChild( body );
| + | |
- | }
| + | |
- | else {
| + | |
- | body.removeChild( div );
| + | |
- | }
| + | |
- |
| + | |
- | //also update eminpx before returning
| + | |
- | ret = eminpx = parseFloat(ret);
| + | |
- |
| + | |
- | return ret;
| + | |
- | },
| + | |
- |
| + | |
- | //cached container for 1em value, populated the first time it's needed
| + | |
- | eminpx,
| + | |
- |
| + | |
- | //enable/disable styles
| + | |
- | applyMedia = function( fromResize ){
| + | |
- | var name = "clientWidth",
| + | |
- | docElemProp = docElem[ name ],
| + | |
- | currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[ name ] || docElemProp,
| + | |
- | styleBlocks = {},
| + | |
- | lastLink = links[ links.length-1 ],
| + | |
- | now = (new Date()).getTime();
| + | |
- | | + | |
- | //throttle resize calls
| + | |
- | if( fromResize && lastCall && now - lastCall < resizeThrottle ){
| + | |
- | clearTimeout( resizeDefer );
| + | |
- | resizeDefer = setTimeout( applyMedia, resizeThrottle );
| + | |
- | return;
| + | |
- | }
| + | |
- | else {
| + | |
- | lastCall = now;
| + | |
- | }
| + | |
- |
| + | |
- | for( var i in mediastyles ){
| + | |
- | var thisstyle = mediastyles[ i ],
| + | |
- | min = thisstyle.minw,
| + | |
- | max = thisstyle.maxw,
| + | |
- | minnull = min === null,
| + | |
- | maxnull = max === null,
| + | |
- | em = "em";
| + | |
- |
| + | |
- | if( !!min ){
| + | |
- | min = parseFloat( min ) * ( min.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 );
| + | |
- | }
| + | |
- | if( !!max ){
| + | |
- | max = parseFloat( max ) * ( max.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 );
| + | |
- | }
| + | |
- |
| + | |
- | // 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
| + | |
- | if( !thisstyle.hasquery || ( !minnull || !maxnull ) && ( minnull || currWidth >= min ) && ( maxnull || currWidth <= max ) ){
| + | |
- | if( !styleBlocks[ thisstyle.media ] ){
| + | |
- | styleBlocks[ thisstyle.media ] = [];
| + | |
- | }
| + | |
- | styleBlocks[ thisstyle.media ].push( rules[ thisstyle.rules ] );
| + | |
- | }
| + | |
- | }
| + | |
- |
| + | |
- | //remove any existing respond style element(s)
| + | |
- | for( var i in appendedEls ){
| + | |
- | if( appendedEls[ i ] && appendedEls[ i ].parentNode === head ){
| + | |
- | head.removeChild( appendedEls[ i ] );
| + | |
- | }
| + | |
- | }
| + | |
- |
| + | |
- | //inject active styles, grouped by media type
| + | |
- | for( var i in styleBlocks ){
| + | |
- | var ss = doc.createElement( "style" ),
| + | |
- | css = styleBlocks[ i ].join( "\n" );
| + | |
- |
| + | |
- | ss.type = "text/css";
| + | |
- | ss.media = i;
| + | |
- |
| + | |
- | //originally, ss was appended to a documentFragment and sheets were appended in bulk.
| + | |
- | //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!
| + | |
- | head.insertBefore( ss, lastLink.nextSibling );
| + | |
- |
| + | |
- | if ( ss.styleSheet ){
| + | |
- | ss.styleSheet.cssText = css;
| + | |
- | }
| + | |
- | else {
| + | |
- | ss.appendChild( doc.createTextNode( css ) );
| + | |
- | }
| + | |
- |
| + | |
- | //push to appendedEls to track for later removal
| + | |
- | appendedEls.push( ss );
| + | |
- | }
| + | |
- | },
| + | |
- | //tweaked Ajax functions from Quirksmode
| + | |
- | ajax = function( url, callback ) {
| + | |
- | var req = xmlHttp();
| + | |
- | if (!req){
| + | |
- | return;
| + | |
- | }
| + | |
- | req.open( "GET", url, true );
| + | |
- | req.onreadystatechange = function () {
| + | |
- | if ( req.readyState != 4 || req.status != 200 && req.status != 304 ){
| + | |
- | return;
| + | |
- | }
| + | |
- | callback( req.responseText );
| + | |
- | }
| + | |
- | if ( req.readyState == 4 ){
| + | |
- | return;
| + | |
- | }
| + | |
- | req.send( null );
| + | |
- | },
| + | |
- | //define ajax obj
| + | |
- | xmlHttp = (function() {
| + | |
- | var xmlhttpmethod = false;
| + | |
- | try {
| + | |
- | xmlhttpmethod = new XMLHttpRequest();
| + | |
- | }
| + | |
- | catch( e ){
| + | |
- | xmlhttpmethod = new ActiveXObject( "Microsoft.XMLHTTP" );
| + | |
- | }
| + | |
- | return function(){
| + | |
- | return xmlhttpmethod;
| + | |
- | };
| + | |
- | })();
| + | |
- |
| + | |
- | //translate CSS
| + | |
- | ripCSS();
| + | |
- |
| + | |
- | //expose update for re-running respond later on
| + | |
- | respond.update = ripCSS;
| + | |
- |
| + | |
- | //adjust on resize
| + | |
- | function callMedia(){
| + | |
- | applyMedia( true );
| + | |
- | }
| + | |
- | if( win.addEventListener ){
| + | |
- | win.addEventListener( "resize", callMedia, false );
| + | |
- | }
| + | |
- | else if( win.attachEvent ){
| + | |
- | win.attachEvent( "onresize", callMedia );
| + | |
- | }
| + | |
- | })(this); | + | |
| | | |
| | | |
| </script> | | </script> |
| </html> | | </html> |