1/* Respond.js: min/max-width media query polyfill. (c) Scott Jehl. MIT Lic. j.mp/respondjs */ 2(function( w ){ 3 4 5 "use strict"; 6 7 8 //exposed namespace 9 var respond = {}; 10 w.respond = respond; 11 12 13 //define update even in native-mq-supporting browsers, to avoid errors 14 respond.update = function(){}; 15 16 17 //define ajax obj 18 var requestQueue = [], 19 xmlHttp = (function() { 20 var xmlhttpmethod = false; 21 try { 22 xmlhttpmethod = new w.XMLHttpRequest(); 23 } 24 catch( e ){ 25 xmlhttpmethod = new w.ActiveXObject( "Microsoft.XMLHTTP" ); 26 } 27 return function(){ 28 return xmlhttpmethod; 29 }; 30 })(), 31 32 33 //tweaked Ajax functions from Quirksmode 34 ajax = function( url, callback ) { 35 var req = xmlHttp(); 36 if (!req){ 37 return; 38 } 39 req.open( "GET", url, true ); 40 req.onreadystatechange = function () { 41 if ( req.readyState !== 4 || req.status !== 200 && req.status !== 304 ){ 42 return; 43 } 44 callback( req.responseText ); 45 }; 46 if ( req.readyState === 4 ){ 47 return; 48 } 49 req.send( null ); 50 }, 51 isUnsupportedMediaQuery = function( query ) { 52 return query.replace( respond.regex.minmaxwh, '' ).match( respond.regex.other ); 53 }; 54 55 56 //expose for testing 57 respond.ajax = ajax; 58 respond.queue = requestQueue; 59 respond.unsupportedmq = isUnsupportedMediaQuery; 60 respond.regex = { 61 media: /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi, 62 keyframes: /@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi, 63 comments: /\/\*[^*]*\*+([^/][^*]*\*+)*\//gi, 64 urls: /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g, 65 findStyles: /@media *([^\{]+)\{([\S\s]+?)$/, 66 only: /(only\s+)?([a-zA-Z]+)\s?/, 67 minw: /\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/, 68 maxw: /\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/, 69 minmaxwh: /\(\s*m(in|ax)\-(height|width)\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/gi, 70 other: /\([^\)]*\)/g 71 }; 72 73 74 //expose media query support flag for external use 75 respond.mediaQueriesSupported = w.matchMedia && w.matchMedia( "only all" ) !== null && w.matchMedia( "only all" ).matches; 76 77 78 //if media queries are supported, exit here 79 if( respond.mediaQueriesSupported ){ 80 return; 81 } 82 83 84 //define vars 85 var doc = w.document, 86 docElem = doc.documentElement, 87 mediastyles = [], 88 rules = [], 89 appendedEls = [], 90 parsedSheets = {}, 91 resizeThrottle = 30, 92 head = doc.getElementsByTagName( "head" )[0] || docElem, 93 base = doc.getElementsByTagName( "base" )[0], 94 links = head.getElementsByTagName( "link" ), 95 96 97 lastCall, 98 resizeDefer, 99 100 101 //cached container for 1em value, populated the first time it's needed 102 eminpx, 103 104 105 // returns the value of 1em in pixels 106 getEmValue = function() { 107 var ret, 108 div = doc.createElement('div'), 109 body = doc.body, 110 originalHTMLFontSize = docElem.style.fontSize, 111 originalBodyFontSize = body && body.style.fontSize, 112 fakeUsed = false;
113 114 115 div.style.cssText = "position:absolute;font-size:1em;width:1em"; 116 117 118 if( !body ){ 119 body = fakeUsed = doc.createElement( "body" ); 120 body.style.background = "none"; 121 } 122 123 124 // 1em in a media query is the value of the default font size of the browser 125 // reset docElem and body to ensure the correct value is returned 126 docElem.style.fontSize = "100%"; 127 body.style.fontSize = "100%"; 128 129 130 body.appendChild( div ); 131 132 133 if( fakeUsed ){ 134 docElem.insertBefore( body, docElem.firstChild ); 135 } 136 137 138 ret = div.offsetWidth; 139 140 141 if( fakeUsed ){ 142 docElem.removeChild( body ); 143 } 144 else { 145 body.removeChild( div ); 146 } 147 148 149 // restore the original values 150 docElem.style.fontSize = originalHTMLFontSize; 151 if( originalBodyFontSize ) { 152 body.style.fontSize = originalBodyFontSize; 153 } 154 155 156 157 158 //also update eminpx before returning 159 ret = eminpx = parseFloat(ret); 160 161 162 return ret; 163 }, 164 165 166 //enable/disable styles 167 applyMedia = function( fromResize ){ 168 var name = "clientWidth", 169 docElemProp = docElem[ name ], 170 currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[ name ] || docElemProp, 171 styleBlocks = {}, 172 lastLink = links[ links.length-1 ], 173 now = (new Date()).getTime(); 174 175 176 //throttle resize calls 177 if( fromResize && lastCall && now - lastCall < resizeThrottle ){ 178 w.clearTimeout( resizeDefer ); 179 resizeDefer = w.setTimeout( applyMedia, resizeThrottle ); 180 return; 181 } 182 else { 183 lastCall = now; 184 } 185 186 187 for( var i in mediastyles ){ 188 if( mediastyles.hasOwnProperty( i ) ){ 189 var thisstyle = mediastyles[ i ], 190 min = thisstyle.minw, 191 max = thisstyle.maxw, 192 minnull = min === null, 193 maxnull = max === null, 194 em = "em"; 195 196 197 if( !!min ){ 198 min = parseFloat( min ) * ( min.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); 199 } 200 if( !!max ){ 201 max = parseFloat( max ) * ( max.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); 202 } 203 204 205 // 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 206 if( !thisstyle.hasquery || ( !minnull || !maxnull ) && ( minnull || currWidth >= min ) && ( maxnull || currWidth <= max ) ){ 207 if( !styleBlocks[ thisstyle.media ] ){ 208 styleBlocks[ thisstyle.media ] = []; 209 } 210 styleBlocks[ thisstyle.media ].push( rules[ thisstyle.rules ] ); 211 } 212 } 213 } 214 215 216 //remove any existing respond style element
216(s) 217 for( var j in appendedEls ){ 218 if( appendedEls.hasOwnProperty( j ) ){ 219 if( appendedEls[ j ] && appendedEls[ j ].parentNode === head ){ 220 head.removeChild( appendedEls[ j ] ); 221 } 222 } 223 } 224 appendedEls.length = 0; 225 226 227 //inject active styles, grouped by media type 228 for( var k in styleBlocks ){ 229 if( styleBlocks.hasOwnProperty( k ) ){ 230 var ss = doc.createElement( "style" ), 231 css = styleBlocks[ k ].join( "\n" ); 232 233 234 ss.type = "text/css"; 235 ss.media = k; 236 237 238 //originally, ss was appended to a documentFragment and sheets were appended in bulk. 239 //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! 240 head.insertBefore( ss, lastLink.nextSibling ); 241 242 243 if ( ss.styleSheet ){ 244 ss.styleSheet.cssText = css; 245 } 246 else { 247 ss.appendChild( doc.createTextNode( css ) ); 248 } 249 250 251 //push to appendedEls to track for later removal 252 appendedEls.push( ss ); 253 } 254 } 255 }, 256 //find media blocks in css text, convert to style blocks 257 translate = function( styles, href, media ){ 258 var qs = styles.replace( respond.regex.comments, '' ) 259 .replace( respond.regex.keyframes, '' ) 260 .match( respond.regex.media ), 261 ql = qs && qs.length || 0; 262 263 264 //try to get CSS path 265 href = href.substring( 0, href.lastIndexOf( "/" ) ); 266 267 268 var repUrls = function( css ){ 269 return css.replace( respond.regex.urls, "$1" + href + "$2$3" ); 270 }, 271 useMedia = !ql && media; 272 273 274 //if path exists, tack on trailing slash 275 if( href.length ){ href += "/"; } 276 277 278 //if no internal queries exist, but media attr does, use that 279 //note: this currently lacks support for situations where a media attr is specified on a link AND 280 //its associated stylesheet has internal CSS media queries. 281 //In those cases, the media attribute will currently be ignored. 282 if( useMedia ){ 283 ql = 1; 284 } 285 286 287 for( var i = 0; i < ql; i++ ){ 288 var fullq, thisq, eachq, eql; 289 290 291 //media attr 292 if( useMedia ){ 293 fullq = media; 294 rules.push( repUrls( styles ) ); 295 } 296 //parse for styles 297 else{ 298 fullq = qs[ i ].match( respond.regex.findStyles ) && RegExp.$1; 299 rules.push( RegExp.$2 && repUrls( RegExp.$2 ) ); 300 } 301 302 303 eachq = fullq.split( "," ); 304 eql = eachq.length; 305 306 307 for( var j = 0; j < eql; j++ ){ 308 thisq = eachq[ j ]; 309 310 311 if( isUnsupportedMediaQuery( thisq ) ) { 312 continue; 313 } 314 315 316 mediastyles.push( { 317 media : thisq.split( "(" )[ 0 ].match( respond.regex.only ) && RegExp.$2 || "all", 318 rules : rules.length - 1, 319 hasquery : thisq.indexOf("(") > -1, 320 minw : thisq.match( respond.regex.minw ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ), 321 maxw : thisq.match( respond.regex.maxw ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ) 322 } ); 323 } 324 } 325 326 327 applyMedia(); 328 }, 329 330 331 //recurse through request queue, get css text 332 makeRequests = function(){ 333 if( requestQueue.length ){ 334 var thisRequest = requestQueue.shift(); 335 336 337 ajax( thisRequest.href, function( styles ){ 338 translate( styles, thisRequest.href, thisRequest.media ); 339 parsedSheets[ thisRequest.href ] = true; 340 341 342 // by wrapping recursive function call in setTimeout 343 // we prevent "Stack overflow" error in IE7 344 w.setTimeout(function(){ makeRequests(); },0); 345 } ); 346 } 347 }, 348 349 350 //loop stylesheets, send text content to translate 351 ripCSS = function(){ 352 353 354 for( var i = 0; i < links.length; i++ ){ 355 var sheet = links[ i ], 356 href = sheet.href, 357 media = sheet.media, 358 isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet"; 359 360 361 //only links plz and prevent re-parsing 362 if( !!href && isCSS && !parsedSheets[ href ] ){ 363 // selectivizr exposes css through the rawCssText expando 364 if (sheet.styleSheet && sheet.styleSheet.rawCssText) { 365 translate( sheet.styleSheet.rawCssText, href, media ); 366 parsedSheets[ href ] = true; 367 } else { 368 if( (!/^([a-zA-Z:]*\/\/)/.test( href ) && !base) || 369 href.replace( RegExp.$1, "" ).split( "/" )[0] === w.location.host ){ 370 // IE7 doesn't handle urls that start with '//' for ajax request 371 // manually add in the protocol 372 if ( href.substring(0,2) === "//" ) { href = w.location.protocol + href; } 373 requestQueue.push( { 374 href: href, 375 media: media 376 } ); 377 } 378 } 379 } 380 } 381 makeRequests(); 382 }; 383 384 385 //translate CSS 386 ripCSS(); 387 388 389 //expose update for re-running respond later on 390 respond.update = ripCSS; 391 392 393 //expose getEmValue 394 respond.getEmValue = getEmValue; 395 396 397 //adjust on resize 398 function callMedia(){ 399 applyMedia( true ); 400 } 401 402 403 if( w.addEventListener ){ 404 w.addEventListener( "resize", callMedia, false ); 405 } 406 else if( w.attachEvent ){ 407 w.attachEvent( "onresize", callMedia ); 408 } 409})(this); 410
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.