You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

respond.js 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. /* NUGET: BEGIN LICENSE TEXT
  2. *
  3. * Microsoft grants you the right to use these script files for the sole
  4. * purpose of either: (i) interacting through your browser with the Microsoft
  5. * website or online service, subject to the applicable licensing or use
  6. * terms; or (ii) using the files as included with a Microsoft product subject
  7. * to that product's license terms. Microsoft reserves all other rights to the
  8. * files not expressly granted by Microsoft, whether by implication, estoppel
  9. * or otherwise. Insofar as a script file is dual licensed under GPL,
  10. * Microsoft neither took the code under GPL nor distributes it thereunder but
  11. * under the terms set out in this paragraph. All notices and licenses
  12. * below are for informational purposes only.
  13. *
  14. * NUGET: END LICENSE TEXT */
  15. /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */
  16. /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */
  17. window.matchMedia = window.matchMedia || (function(doc, undefined){
  18. var bool,
  19. docElem = doc.documentElement,
  20. refNode = docElem.firstElementChild || docElem.firstChild,
  21. // fakeBody required for <FF4 when executed in <head>
  22. fakeBody = doc.createElement('body'),
  23. div = doc.createElement('div');
  24. div.id = 'mq-test-1';
  25. div.style.cssText = "position:absolute;top:-100em";
  26. fakeBody.style.background = "none";
  27. fakeBody.appendChild(div);
  28. return function(q){
  29. div.innerHTML = '&shy;<style media="'+q+'"> #mq-test-1 { width: 42px; }</style>';
  30. docElem.insertBefore(fakeBody, refNode);
  31. bool = div.offsetWidth == 42;
  32. docElem.removeChild(fakeBody);
  33. return { matches: bool, media: q };
  34. };
  35. })(document);
  36. /*! Respond.js v1.2.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */
  37. (function( win ){
  38. //exposed namespace
  39. win.respond = {};
  40. //define update even in native-mq-supporting browsers, to avoid errors
  41. respond.update = function(){};
  42. //expose media query support flag for external use
  43. respond.mediaQueriesSupported = win.matchMedia && win.matchMedia( "only all" ).matches;
  44. //if media queries are supported, exit here
  45. if( respond.mediaQueriesSupported ){ return; }
  46. //define vars
  47. var doc = win.document,
  48. docElem = doc.documentElement,
  49. mediastyles = [],
  50. rules = [],
  51. appendedEls = [],
  52. parsedSheets = {},
  53. resizeThrottle = 30,
  54. head = doc.getElementsByTagName( "head" )[0] || docElem,
  55. base = doc.getElementsByTagName( "base" )[0],
  56. links = head.getElementsByTagName( "link" ),
  57. requestQueue = [],
  58. //loop stylesheets, send text content to translate
  59. ripCSS = function(){
  60. var sheets = links,
  61. sl = sheets.length,
  62. i = 0,
  63. //vars for loop:
  64. sheet, href, media, isCSS;
  65. for( ; i < sl; i++ ){
  66. sheet = sheets[ i ],
  67. href = sheet.href,
  68. media = sheet.media,
  69. isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet";
  70. //only links plz and prevent re-parsing
  71. if( !!href && isCSS && !parsedSheets[ href ] ){
  72. // selectivizr exposes css through the rawCssText expando
  73. if (sheet.styleSheet && sheet.styleSheet.rawCssText) {
  74. translate( sheet.styleSheet.rawCssText, href, media );
  75. parsedSheets[ href ] = true;
  76. } else {
  77. if( (!/^([a-zA-Z:]*\/\/)/.test( href ) && !base)
  78. || href.replace( RegExp.$1, "" ).split( "/" )[0] === win.location.host ){
  79. requestQueue.push( {
  80. href: href,
  81. media: media
  82. } );
  83. }
  84. }
  85. }
  86. }
  87. makeRequests();
  88. },
  89. //recurse through request queue, get css text
  90. makeRequests = function(){
  91. if( requestQueue.length ){
  92. var thisRequest = requestQueue.shift();
  93. ajax( thisRequest.href, function( styles ){
  94. translate( styles, thisRequest.href, thisRequest.media );
  95. parsedSheets[ thisRequest.href ] = true;
  96. makeRequests();
  97. } );
  98. }
  99. },
  100. //find media blocks in css text, convert to style blocks
  101. translate = function( styles, href, media ){
  102. var qs = styles.match( /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi ),
  103. ql = qs && qs.length || 0,
  104. //try to get CSS path
  105. href = href.substring( 0, href.lastIndexOf( "/" )),
  106. repUrls = function( css ){
  107. return css.replace( /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g, "$1" + href + "$2$3" );
  108. },
  109. useMedia = !ql && media,
  110. //vars used in loop
  111. i = 0,
  112. j, fullq, thisq, eachq, eql;
  113. //if path exists, tack on trailing slash
  114. if( href.length ){ href += "/"; }
  115. //if no internal queries exist, but media attr does, use that
  116. //note: this currently lacks support for situations where a media attr is specified on a link AND
  117. //its associated stylesheet has internal CSS media queries.
  118. //In those cases, the media attribute will currently be ignored.
  119. if( useMedia ){
  120. ql = 1;
  121. }
  122. for( ; i < ql; i++ ){
  123. j = 0;
  124. //media attr
  125. if( useMedia ){
  126. fullq = media;
  127. rules.push( repUrls( styles ) );
  128. }
  129. //parse for styles
  130. else{
  131. fullq = qs[ i ].match( /@media *([^\{]+)\{([\S\s]+?)$/ ) && RegExp.$1;
  132. rules.push( RegExp.$2 && repUrls( RegExp.$2 ) );
  133. }
  134. eachq = fullq.split( "," );
  135. eql = eachq.length;
  136. for( ; j < eql; j++ ){
  137. thisq = eachq[ j ];
  138. mediastyles.push( {
  139. media : thisq.split( "(" )[ 0 ].match( /(only\s+)?([a-zA-Z]+)\s?/ ) && RegExp.$2 || "all",
  140. rules : rules.length - 1,
  141. hasquery: thisq.indexOf("(") > -1,
  142. minw : thisq.match( /\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ),
  143. maxw : thisq.match( /\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" )
  144. } );
  145. }
  146. }
  147. applyMedia();
  148. },
  149. lastCall,
  150. resizeDefer,
  151. // returns the value of 1em in pixels
  152. getEmValue = function() {
  153. var ret,
  154. div = doc.createElement('div'),
  155. body = doc.body,
  156. fakeUsed = false;
  157. div.style.cssText = "position:absolute;font-size:1em;width:1em";
  158. if( !body ){
  159. body = fakeUsed = doc.createElement( "body" );
  160. body.style.background = "none";
  161. }
  162. body.appendChild( div );
  163. docElem.insertBefore( body, docElem.firstChild );
  164. ret = div.offsetWidth;
  165. if( fakeUsed ){
  166. docElem.removeChild( body );
  167. }
  168. else {
  169. body.removeChild( div );
  170. }
  171. //also update eminpx before returning
  172. ret = eminpx = parseFloat(ret);
  173. return ret;
  174. },
  175. //cached container for 1em value, populated the first time it's needed
  176. eminpx,
  177. //enable/disable styles
  178. applyMedia = function( fromResize ){
  179. var name = "clientWidth",
  180. docElemProp = docElem[ name ],
  181. currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[ name ] || docElemProp,
  182. styleBlocks = {},
  183. lastLink = links[ links.length-1 ],
  184. now = (new Date()).getTime();
  185. //throttle resize calls
  186. if( fromResize && lastCall && now - lastCall < resizeThrottle ){
  187. clearTimeout( resizeDefer );
  188. resizeDefer = setTimeout( applyMedia, resizeThrottle );
  189. return;
  190. }
  191. else {
  192. lastCall = now;
  193. }
  194. for( var i in mediastyles ){
  195. var thisstyle = mediastyles[ i ],
  196. min = thisstyle.minw,
  197. max = thisstyle.maxw,
  198. minnull = min === null,
  199. maxnull = max === null,
  200. em = "em";
  201. if( !!min ){
  202. min = parseFloat( min ) * ( min.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 );
  203. }
  204. if( !!max ){
  205. max = parseFloat( max ) * ( max.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 );
  206. }
  207. // 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
  208. if( !thisstyle.hasquery || ( !minnull || !maxnull ) && ( minnull || currWidth >= min ) && ( maxnull || currWidth <= max ) ){
  209. if( !styleBlocks[ thisstyle.media ] ){
  210. styleBlocks[ thisstyle.media ] = [];
  211. }
  212. styleBlocks[ thisstyle.media ].push( rules[ thisstyle.rules ] );
  213. }
  214. }
  215. //remove any existing respond style element(s)
  216. for( var i in appendedEls ){
  217. if( appendedEls[ i ] && appendedEls[ i ].parentNode === head ){
  218. head.removeChild( appendedEls[ i ] );
  219. }
  220. }
  221. //inject active styles, grouped by media type
  222. for( var i in styleBlocks ){
  223. var ss = doc.createElement( "style" ),
  224. css = styleBlocks[ i ].join( "\n" );
  225. ss.type = "text/css";
  226. ss.media = i;
  227. //originally, ss was appended to a documentFragment and sheets were appended in bulk.
  228. //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!
  229. head.insertBefore( ss, lastLink.nextSibling );
  230. if ( ss.styleSheet ){
  231. ss.styleSheet.cssText = css;
  232. }
  233. else {
  234. ss.appendChild( doc.createTextNode( css ) );
  235. }
  236. //push to appendedEls to track for later removal
  237. appendedEls.push( ss );
  238. }
  239. },
  240. //tweaked Ajax functions from Quirksmode
  241. ajax = function( url, callback ) {
  242. var req = xmlHttp();
  243. if (!req){
  244. return;
  245. }
  246. req.open( "GET", url, true );
  247. req.onreadystatechange = function () {
  248. if ( req.readyState != 4 || req.status != 200 && req.status != 304 ){
  249. return;
  250. }
  251. callback( req.responseText );
  252. }
  253. if ( req.readyState == 4 ){
  254. return;
  255. }
  256. req.send( null );
  257. },
  258. //define ajax obj
  259. xmlHttp = (function() {
  260. var xmlhttpmethod = false;
  261. try {
  262. xmlhttpmethod = new XMLHttpRequest();
  263. }
  264. catch( e ){
  265. xmlhttpmethod = new ActiveXObject( "Microsoft.XMLHTTP" );
  266. }
  267. return function(){
  268. return xmlhttpmethod;
  269. };
  270. })();
  271. //translate CSS
  272. ripCSS();
  273. //expose update for re-running respond later on
  274. respond.update = ripCSS;
  275. //adjust on resize
  276. function callMedia(){
  277. applyMedia( true );
  278. }
  279. if( win.addEventListener ){
  280. win.addEventListener( "resize", callMedia, false );
  281. }
  282. else if( win.attachEvent ){
  283. win.attachEvent( "onresize", callMedia );
  284. }
  285. })(this);