[400] | 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 */ |
---|
| 3 | |
---|
| 4 | window.matchMedia = window.matchMedia || (function( doc, undefined ) { |
---|
| 5 | |
---|
| 6 | "use strict"; |
---|
| 7 | |
---|
| 8 | var bool, |
---|
| 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" ); |
---|
| 14 | |
---|
| 15 | div.id = "mq-test-1"; |
---|
| 16 | div.style.cssText = "position:absolute;top:-100em"; |
---|
| 17 | fakeBody.style.background = "none"; |
---|
| 18 | fakeBody.appendChild(div); |
---|
| 19 | |
---|
| 20 | return function(q){ |
---|
| 21 | |
---|
| 22 | div.innerHTML = "­<style media=\"" + q + "\"> #mq-test-1 { width: 42px; }</style>"; |
---|
| 23 | |
---|
| 24 | docElem.insertBefore( fakeBody, refNode ); |
---|
| 25 | bool = div.offsetWidth === 42; |
---|
| 26 | docElem.removeChild( fakeBody ); |
---|
| 27 | |
---|
| 28 | return { |
---|
| 29 | matches: bool, |
---|
| 30 | media: q |
---|
| 31 | }; |
---|
| 32 | |
---|
| 33 | }; |
---|
| 34 | |
---|
| 35 | }( document )); |
---|
| 36 | |
---|
| 37 | |
---|
| 38 | |
---|
| 39 | |
---|
| 40 | |
---|
| 41 | /*! Respond.js v1.3.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ |
---|
| 42 | (function( win ){ |
---|
| 43 | |
---|
| 44 | "use strict"; |
---|
| 45 | |
---|
| 46 | //exposed namespace |
---|
| 47 | var respond = {}; |
---|
| 48 | win.respond = respond; |
---|
| 49 | |
---|
| 50 | //define update even in native-mq-supporting browsers, to avoid errors |
---|
| 51 | respond.update = function(){}; |
---|
| 52 | |
---|
| 53 | //expose media query support flag for external use |
---|
| 54 | respond.mediaQueriesSupported = win.matchMedia && win.matchMedia( "only all" ).matches; |
---|
| 55 | |
---|
| 56 | //if media queries are supported, exit here |
---|
| 57 | if( respond.mediaQueriesSupported ){ |
---|
| 58 | return; |
---|
| 59 | } |
---|
| 60 | |
---|
| 61 | //define vars |
---|
| 62 | var doc = win.document, |
---|
| 63 | docElem = doc.documentElement, |
---|
| 64 | mediastyles = [], |
---|
| 65 | rules = [], |
---|
| 66 | appendedEls = [], |
---|
| 67 | parsedSheets = {}, |
---|
| 68 | resizeThrottle = 30, |
---|
| 69 | head = doc.getElementsByTagName( "head" )[0] || docElem, |
---|
| 70 | base = doc.getElementsByTagName( "base" )[0], |
---|
| 71 | links = head.getElementsByTagName( "link" ), |
---|
| 72 | requestQueue = [], |
---|
| 73 | |
---|
| 74 | //loop stylesheets, send text content to translate |
---|
| 75 | ripCSS = function(){ |
---|
| 76 | |
---|
| 77 | for( var i = 0; i < links.length; i++ ){ |
---|
| 78 | var sheet = links[ i ], |
---|
| 79 | href = sheet.href, |
---|
| 80 | media = sheet.media, |
---|
| 81 | isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet"; |
---|
| 82 | |
---|
| 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; |
---|
| 89 | } else { |
---|
| 90 | if( (!/^([a-zA-Z:]*\/\/)/.test( href ) && !base) || |
---|
| 91 | href.replace( RegExp.$1, "" ).split( "/" )[0] === win.location.host ){ |
---|
| 92 | requestQueue.push( { |
---|
| 93 | href: href, |
---|
| 94 | media: media |
---|
| 95 | } ); |
---|
| 96 | } |
---|
| 97 | } |
---|
| 98 | } |
---|
| 99 | } |
---|
| 100 | makeRequests(); |
---|
| 101 | }, |
---|
| 102 | |
---|
| 103 | //recurse through request queue, get css text |
---|
| 104 | makeRequests = function(){ |
---|
| 105 | if( requestQueue.length ){ |
---|
| 106 | var thisRequest = requestQueue.shift(); |
---|
| 107 | |
---|
| 108 | ajax( thisRequest.href, function( styles ){ |
---|
| 109 | translate( styles, thisRequest.href, thisRequest.media ); |
---|
| 110 | parsedSheets[ thisRequest.href ] = true; |
---|
| 111 | |
---|
| 112 | // by wrapping recursive function call in setTimeout |
---|
| 113 | // we prevent "Stack overflow" error in IE7 |
---|
| 114 | win.setTimeout(function(){ makeRequests(); },0); |
---|
| 115 | } ); |
---|
| 116 | } |
---|
| 117 | }, |
---|
| 118 | |
---|
| 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; |
---|
| 123 | |
---|
| 124 | //try to get CSS path |
---|
| 125 | href = href.substring( 0, href.lastIndexOf( "/" ) ); |
---|
| 126 | |
---|
| 127 | var repUrls = function( css ){ |
---|
| 128 | return css.replace( /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g, "$1" + href + "$2$3" ); |
---|
| 129 | }, |
---|
| 130 | useMedia = !ql && media; |
---|
| 131 | |
---|
| 132 | //if path exists, tack on trailing slash |
---|
| 133 | if( href.length ){ href += "/"; } |
---|
| 134 | |
---|
| 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. |
---|
| 139 | if( useMedia ){ |
---|
| 140 | ql = 1; |
---|
| 141 | } |
---|
| 142 | |
---|
| 143 | for( var i = 0; i < ql; i++ ){ |
---|
| 144 | var fullq, thisq, eachq, eql; |
---|
| 145 | |
---|
| 146 | //media attr |
---|
| 147 | if( useMedia ){ |
---|
| 148 | fullq = media; |
---|
| 149 | rules.push( repUrls( styles ) ); |
---|
| 150 | } |
---|
| 151 | //parse for styles |
---|
| 152 | else{ |
---|
| 153 | fullq = qs[ i ].match( /@media *([^\{]+)\{([\S\s]+?)$/ ) && RegExp.$1; |
---|
| 154 | rules.push( RegExp.$2 && repUrls( RegExp.$2 ) ); |
---|
| 155 | } |
---|
| 156 | |
---|
| 157 | eachq = fullq.split( "," ); |
---|
| 158 | eql = eachq.length; |
---|
| 159 | |
---|
| 160 | for( var j = 0; j < eql; j++ ){ |
---|
| 161 | thisq = eachq[ j ]; |
---|
| 162 | mediastyles.push( { |
---|
| 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 || "" ) |
---|
| 168 | } ); |
---|
| 169 | } |
---|
| 170 | } |
---|
| 171 | |
---|
| 172 | applyMedia(); |
---|
| 173 | }, |
---|
| 174 | |
---|
| 175 | lastCall, |
---|
| 176 | |
---|
| 177 | resizeDefer, |
---|
| 178 | |
---|
| 179 | // returns the value of 1em in pixels |
---|
| 180 | getEmValue = function() { |
---|
| 181 | var ret, |
---|
| 182 | div = doc.createElement('div'), |
---|
| 183 | body = doc.body, |
---|
| 184 | fakeUsed = false; |
---|
| 185 | |
---|
| 186 | div.style.cssText = "position:absolute;font-size:1em;width:1em"; |
---|
| 187 | |
---|
| 188 | if( !body ){ |
---|
| 189 | body = fakeUsed = doc.createElement( "body" ); |
---|
| 190 | body.style.background = "none"; |
---|
| 191 | } |
---|
| 192 | |
---|
| 193 | body.appendChild( div ); |
---|
| 194 | |
---|
| 195 | docElem.insertBefore( body, docElem.firstChild ); |
---|
| 196 | |
---|
| 197 | ret = div.offsetWidth; |
---|
| 198 | |
---|
| 199 | if( fakeUsed ){ |
---|
| 200 | docElem.removeChild( body ); |
---|
| 201 | } |
---|
| 202 | else { |
---|
| 203 | body.removeChild( div ); |
---|
| 204 | } |
---|
| 205 | |
---|
| 206 | //also update eminpx before returning |
---|
| 207 | ret = eminpx = parseFloat(ret); |
---|
| 208 | |
---|
| 209 | return ret; |
---|
| 210 | }, |
---|
| 211 | |
---|
| 212 | //cached container for 1em value, populated the first time it's needed |
---|
| 213 | eminpx, |
---|
| 214 | |
---|
| 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, |
---|
| 220 | styleBlocks = {}, |
---|
| 221 | lastLink = links[ links.length-1 ], |
---|
| 222 | now = (new Date()).getTime(); |
---|
| 223 | |
---|
| 224 | //throttle resize calls |
---|
| 225 | if( fromResize && lastCall && now - lastCall < resizeThrottle ){ |
---|
| 226 | win.clearTimeout( resizeDefer ); |
---|
| 227 | resizeDefer = win.setTimeout( applyMedia, resizeThrottle ); |
---|
| 228 | return; |
---|
| 229 | } |
---|
| 230 | else { |
---|
| 231 | lastCall = now; |
---|
| 232 | } |
---|
| 233 | |
---|
| 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, |
---|
| 241 | em = "em"; |
---|
| 242 | |
---|
| 243 | if( !!min ){ |
---|
| 244 | min = parseFloat( min ) * ( min.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); |
---|
| 245 | } |
---|
| 246 | if( !!max ){ |
---|
| 247 | max = parseFloat( max ) * ( max.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); |
---|
| 248 | } |
---|
| 249 | |
---|
| 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 ] = []; |
---|
| 254 | } |
---|
| 255 | styleBlocks[ thisstyle.media ].push( rules[ thisstyle.rules ] ); |
---|
| 256 | } |
---|
| 257 | } |
---|
| 258 | } |
---|
| 259 | |
---|
| 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 ] ); |
---|
| 265 | } |
---|
| 266 | } |
---|
| 267 | } |
---|
| 268 | |
---|
| 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" ); |
---|
| 274 | |
---|
| 275 | ss.type = "text/css"; |
---|
| 276 | ss.media = k; |
---|
| 277 | |
---|
| 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 ); |
---|
| 281 | |
---|
| 282 | if ( ss.styleSheet ){ |
---|
| 283 | ss.styleSheet.cssText = css; |
---|
| 284 | } |
---|
| 285 | else { |
---|
| 286 | ss.appendChild( doc.createTextNode( css ) ); |
---|
| 287 | } |
---|
| 288 | |
---|
| 289 | //push to appendedEls to track for later removal |
---|
| 290 | appendedEls.push( ss ); |
---|
| 291 | } |
---|
| 292 | } |
---|
| 293 | }, |
---|
| 294 | //tweaked Ajax functions from Quirksmode |
---|
| 295 | ajax = function( url, callback ) { |
---|
| 296 | var req = xmlHttp(); |
---|
| 297 | if (!req){ |
---|
| 298 | return; |
---|
| 299 | } |
---|
| 300 | req.open( "GET", url, true ); |
---|
| 301 | req.onreadystatechange = function () { |
---|
| 302 | if ( req.readyState !== 4 || req.status !== 200 && req.status !== 304 ){ |
---|
| 303 | return; |
---|
| 304 | } |
---|
| 305 | callback( req.responseText ); |
---|
| 306 | }; |
---|
| 307 | if ( req.readyState === 4 ){ |
---|
| 308 | return; |
---|
| 309 | } |
---|
| 310 | req.send( null ); |
---|
| 311 | }, |
---|
| 312 | //define ajax obj |
---|
| 313 | xmlHttp = (function() { |
---|
| 314 | var xmlhttpmethod = false; |
---|
| 315 | try { |
---|
| 316 | xmlhttpmethod = new win.XMLHttpRequest(); |
---|
| 317 | } |
---|
| 318 | catch( e ){ |
---|
| 319 | xmlhttpmethod = new win.ActiveXObject( "Microsoft.XMLHTTP" ); |
---|
| 320 | } |
---|
| 321 | return function(){ |
---|
| 322 | return xmlhttpmethod; |
---|
| 323 | }; |
---|
| 324 | })(); |
---|
| 325 | |
---|
| 326 | //translate CSS |
---|
| 327 | ripCSS(); |
---|
| 328 | |
---|
| 329 | //expose update for re-running respond later on |
---|
| 330 | respond.update = ripCSS; |
---|
| 331 | |
---|
| 332 | //adjust on resize |
---|
| 333 | function callMedia(){ |
---|
| 334 | applyMedia( true ); |
---|
| 335 | } |
---|
| 336 | if( win.addEventListener ){ |
---|
| 337 | win.addEventListener( "resize", callMedia, false ); |
---|
| 338 | } |
---|
| 339 | else if( win.attachEvent ){ |
---|
| 340 | win.attachEvent( "onresize", callMedia ); |
---|
| 341 | } |
---|
| 342 | })(this); |
---|